Skip to content

Commit

Permalink
Chore: append address to pubkey (#5)
Browse files Browse the repository at this point in the history
* chore: rm recovery component

* chore: make `sign` and `verify` EIP-712 compatible

* chore: fix `verify`

* fix: include `chainId`

* chore: pad chainId to 32 bytes

* chore: fix review changes

* chore: rep tx hash as hex

* feat: injected starknet signer

* chore: update injected starknet signer to account for multiple signatures

* chore: append address to pubkey

* chore: review modifications

* chore: remove logs
  • Loading branch information
Darlington02 authored Nov 2, 2024
1 parent 9c1285b commit 5e2b0bc
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 43 deletions.
17 changes: 7 additions & 10 deletions src/__tests__/starknet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ describe("Typed Starknet Signer", () => {
const expectedSignature = Buffer.from([
4, 122, 51, 60, 218, 66, 57, 104, 199, 126, 49, 15, 195, 203, 209, 15, 62, 214, 104, 245, 237, 79, 12, 252, 141, 242, 95, 4, 176, 235, 231, 189,
7, 126, 187, 220, 69, 127, 240, 85, 198, 31, 219, 33, 230, 0, 142, 230, 0, 200, 246, 208, 144, 191, 118, 88, 85, 216, 105, 65, 129, 174, 37,
165, 7, 142, 71, 187, 235, 77, 198, 135, 116, 24, 37, 215, 190, 173, 4, 78, 34, 153, 96, 211, 54, 44, 12, 33, 244, 91, 185, 32, 219, 8, 176,
196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 83, 69, 80, 79, 76, 73, 65,
165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 83, 69, 80, 79, 76, 73, 65,
]);

const data = Buffer.from("Hello Irys!");
Expand All @@ -55,10 +54,9 @@ describe("Typed Starknet Signer", () => {

it("should fail for an invalid signature", async () => {
const expectedSignature = Buffer.from([
3, 14, 26, 44, 182, 142, 237, 13, 51, 15, 51, 142, 100, 132, 8, 70, 90, 34, 222, 66, 92, 68, 20, 86, 18, 205, 207, 16, 215, 160, 82, 238, 7,
227, 27, 134, 157, 27, 47, 233, 175, 89, 26, 104, 127, 142, 192, 227, 45, 149, 179, 169, 202, 38, 75, 242, 68, 84, 75, 8, 222, 153, 188, 225, 7,
142, 71, 187, 235, 77, 198, 135, 116, 24, 37, 215, 190, 173, 4, 78, 34, 153, 96, 211, 54, 44, 12, 33, 244, 91, 185, 32, 219, 8, 176, 196, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 77, 65, 73, 78,
4, 122, 51, 60, 218, 66, 57, 104, 199, 126, 49, 15, 195, 203, 209, 15, 62, 214, 104, 245, 237, 79, 12, 252, 141, 242, 95, 4, 176, 235, 231, 189,
7, 126, 187, 220, 69, 127, 240, 85, 198, 31, 219, 33, 230, 0, 142, 230, 0, 200, 246, 208, 144, 191, 118, 88, 85, 216, 105, 65, 129, 174, 37,
165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 83, 69, 80, 79, 76, 73, 65,
]);

const data = Buffer.from("Hello World!");
Expand All @@ -80,10 +78,9 @@ describe("Typed Starknet Signer", () => {
it("should evaulate to false for invalid signature", async () => {
// generate invalid signature
const signature = Uint8Array.from([
3, 14, 26, 44, 182, 142, 237, 13, 51, 15, 51, 142, 100, 132, 8, 70, 90, 34, 222, 66, 92, 68, 20, 86, 18, 205, 207, 16, 215, 160, 82, 238, 7,
227, 27, 134, 157, 27, 47, 233, 175, 89, 26, 104, 127, 142, 192, 227, 45, 149, 179, 169, 202, 38, 75, 242, 68, 84, 75, 8, 222, 153, 188, 225, 7,
142, 71, 187, 235, 77, 198, 135, 116, 24, 37, 215, 190, 173, 4, 78, 34, 153, 96, 211, 54, 44, 12, 33, 244, 91, 185, 32, 219, 8, 176, 196, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 77, 65, 73, 78,
4, 122, 51, 60, 218, 66, 57, 104, 199, 126, 49, 15, 195, 203, 209, 15, 62, 214, 104, 245, 237, 79, 12, 252, 141, 242, 95, 4, 176, 235, 231, 189,
7, 126, 187, 220, 69, 127, 240, 85, 198, 31, 219, 33, 230, 0, 142, 230, 0, 200, 246, 208, 144, 191, 118, 88, 85, 216, 105, 65, 129, 174, 37,
165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 78, 95, 83, 69, 80, 79, 76, 73, 65,
]);

// try verifying
Expand Down
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ export const SIG_CONFIG: Record<SignatureConfig, SignatureMeta> = {
sigName: "typedEthereum",
},
[SignatureConfig.STARKNET]: {
sigLength: 128, // 64 bytes signature, + 32 bytes address + 32 bytes chainId
pubLength: 33,
sigLength: 96, // 64 bytes signature, + 32 bytes chainId
pubLength: 65, // 33 bytes public key + 32 bytes address (for sdk)
sigName: "starknet",
},
};
33 changes: 17 additions & 16 deletions src/signing/chains/StarknetSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ export default class StarknetSigner implements Signer {

public async init(): Promise<void> {
const pubKey = encode.addHexPrefix(encode.buf2hex(ec.starkCurve.getPublicKey(this.privateKey, true)));
const hexKey = pubKey.startsWith("0x") ? pubKey.slice(2) : pubKey;
const address = this.signer.address;

// get pubkey and address buffers
const pubKeyBuffer = Buffer.from(pubKey.startsWith("0x") ? pubKey.slice(2) : pubKey, "hex");
const addressBuffer = Buffer.from(address.startsWith("0x") ? address.slice(2) : address, "hex");

// concatenate buffers as pubKey
this.publicKey = Buffer.concat([pubKeyBuffer, addressBuffer]);

this.publicKey = Buffer.from(hexKey, "hex");
this.chainId = await this.provider.getChainId();
}

Expand All @@ -43,48 +49,43 @@ export default class StarknetSigner implements Signer {

const r = BigInt(signature.r).toString(16).padStart(64, "0"); // Convert BigInt to hex string
const s = BigInt(signature.s).toString(16).padStart(64, "0"); // Convert BigInt to hex string
const address = this.signer.address.replace(/^0x0?|^0x/, "").padStart(64, "0");

const rArray = Uint8Array.from(Buffer.from(r, "hex"));
const sArray = Uint8Array.from(Buffer.from(s, "hex"));
const addressToArray = Uint8Array.from(Buffer.from(address, "hex"));
const chainIdToArray = Uint8Array.from(Buffer.from(chainId.replace(/^0x/, "").padStart(64, "0"), "hex"));

// Concatenate the arrays
const result = new Uint8Array(rArray.length + sArray.length + addressToArray.length + chainIdToArray.length);
const result = new Uint8Array(rArray.length + sArray.length + chainIdToArray.length);
result.set(rArray);
result.set(sArray, rArray.length);
result.set(addressToArray, rArray.length + sArray.length);
result.set(chainIdToArray, rArray.length + sArray.length + addressToArray.length);
result.set(chainIdToArray, rArray.length + sArray.length);

// check signature is of required length
if (result.length !== 128) throw new Error("Signature length must be 128 bytes!");
if (result.length !== 96) throw new Error("Signature length must be 96 bytes!");

return result;
}

static async verify(pubkey: Buffer, message: Uint8Array, signature: Uint8Array, _opts?: any): Promise<boolean> {
const rLength = 32;
const sLength = 32;
const addressLength = 32;
const chainIdLength = 32;

// retrieve address from signature
const addressArrayRetrieved = signature.slice(rLength + sLength, rLength + sLength + addressLength);
const originalAddress = "0x" + Buffer.from(addressArrayRetrieved).toString("hex");
// retrieve pubKey and address from pubKey
const originalPubKey = pubkey.slice(0, 33);
const originalAddress = "0x" + Buffer.from(pubkey.slice(33)).toString("hex");

// retrieve chainId from signature
const chainIdArrayRetrieved = signature.slice(rLength + sLength + addressLength, rLength + sLength + addressLength + chainIdLength);
const chainIdArrayRetrieved = signature.slice(rLength + sLength);
const originalChainId = "0x" + Buffer.from(chainIdArrayRetrieved).toString("hex");

// calculate full public key
const fullPubKey = encode.addHexPrefix(encode.buf2hex(pubkey));
const fullPubKey = encode.addHexPrefix(encode.buf2hex(originalPubKey));

// generate message hash and signature
const msg = hash.computeHashOnElements(uint8ArrayToBigNumberishArray(message));
const data: TypedData = getTypedData(msg, originalChainId);
const msgHash = typedData.getMessageHash(data, originalAddress);
const trimmedSignature = signature.slice(0, -64);
const trimmedSignature = signature.slice(0, -32);

// verify
return ec.starkCurve.verify(trimmedSignature, msgHash, fullPubKey);
Expand Down
34 changes: 19 additions & 15 deletions src/signing/chains/injectedStarknetSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ export default class InjectedStarknetSigner implements Signer {
this.walletAccount = walletAccount;
}

public async init(): Promise<void> {
public async init(pubKey: string): Promise<void> {
const address = this.walletAccount.address;

// get pubkey and address buffers
const pubKeyBuffer = Buffer.from(pubKey.startsWith("0x") ? pubKey.slice(2) : pubKey, "hex");
const addressBuffer = Buffer.from(address.startsWith("0x") ? address.slice(2) : address, "hex");

// concatenate buffers as pubKey
this.publicKey = Buffer.concat([pubKeyBuffer, addressBuffer]);

this.chainId = await this.provider.getChainId();
}

Expand All @@ -36,48 +45,43 @@ export default class InjectedStarknetSigner implements Signer {
const rsComponents = Array.from(signature).slice(-2);
const r = BigInt(rsComponents[0]).toString(16).padStart(64, "0");
const s = BigInt(rsComponents[1]).toString(16).padStart(64, "0");
const address = this.walletAccount.address.replace(/^0x0?|^0x/, "").padStart(64, "0");

const rArray = Uint8Array.from(Buffer.from(r, "hex"));
const sArray = Uint8Array.from(Buffer.from(s, "hex"));
const addressToArray = Uint8Array.from(Buffer.from(address, "hex"));
const chainIdToArray = Uint8Array.from(Buffer.from(chainId.replace(/^0x/, "").padStart(64, "0"), "hex"));

// Concatenate the arrays
const result = new Uint8Array(rArray.length + sArray.length + addressToArray.length + chainIdToArray.length);
const result = new Uint8Array(rArray.length + sArray.length + chainIdToArray.length);
result.set(rArray);
result.set(sArray, rArray.length);
result.set(addressToArray, rArray.length + sArray.length);
result.set(chainIdToArray, rArray.length + sArray.length + addressToArray.length);
result.set(chainIdToArray, rArray.length + sArray.length);

// check signature is of required length
if (result.length !== 128) throw new Error("Signature length must be 128 bytes!");
if (result.length !== 96) throw new Error("Signature length must be 96 bytes!");

return result;
}

static async verify(pubkey: Buffer, message: Uint8Array, signature: Uint8Array, _opts?: any): Promise<boolean> {
const rLength = 32;
const sLength = 32;
const addressLength = 32;
const chainIdLength = 32;

// retrieve address from signature
const addressArrayRetrieved = signature.slice(rLength + sLength, rLength + sLength + addressLength);
const originalAddress = "0x" + Buffer.from(addressArrayRetrieved).toString("hex");
// retrieve pubKey and address from pubKey
const originalPubKey = pubkey.slice(0, 33);
const originalAddress = "0x" + Buffer.from(pubkey.slice(33)).toString("hex");

// retrieve chainId from signature
const chainIdArrayRetrieved = signature.slice(rLength + sLength + addressLength, rLength + sLength + addressLength + chainIdLength);
const chainIdArrayRetrieved = signature.slice(rLength + sLength);
const originalChainId = "0x" + Buffer.from(chainIdArrayRetrieved).toString("hex");

// calculate full public key
const fullPubKey = encode.addHexPrefix(encode.buf2hex(pubkey));
const fullPubKey = encode.addHexPrefix(encode.buf2hex(originalPubKey));

// generate message hash and signature
const msg = hash.computeHashOnElements(uint8ArrayToBigNumberishArray(message));
const data: TypedData = getTypedData(msg, originalChainId);
const msgHash = typedData.getMessageHash(data, originalAddress);
const trimmedSignature = signature.slice(0, -64);
const trimmedSignature = signature.slice(0, -32);

// verify
return ec.starkCurve.verify(trimmedSignature, msgHash, fullPubKey);
Expand Down

0 comments on commit 5e2b0bc

Please sign in to comment.