diff --git a/sdk/typescript/src/cryptography/secp256k1-keypair.ts b/sdk/typescript/src/cryptography/secp256k1-keypair.ts index 91cb44d7aa609..0e552c176de89 100644 --- a/sdk/typescript/src/cryptography/secp256k1-keypair.ts +++ b/sdk/typescript/src/cryptography/secp256k1-keypair.ts @@ -1,17 +1,18 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -import * as secp from "@noble/secp256k1"; +import * as secp from '@noble/secp256k1'; import { Base64DataBuffer } from '../serialization/base64'; import { Keypair } from './keypair'; import { PublicKey, SignatureScheme } from './publickey'; import { hmac } from '@noble/hashes/hmac'; -import { sha256 } from "@noble/hashes/sha256"; -import { Secp256k1PublicKey } from "./secp256k1-publickey"; +import { sha256 } from '@noble/hashes/sha256'; +import { Secp256k1PublicKey } from './secp256k1-publickey'; +import { Signature } from '@noble/secp256k1'; secp.utils.hmacSha256Sync = (key: Uint8Array, ...msgs: Uint8Array[]) => { const h = hmac.create(sha256, key); - msgs.forEach(msg => h.update(msg)); + msgs.forEach((msg) => h.update(msg)); return h.digest(); }; @@ -42,10 +43,10 @@ export class Secp256k1Keypair implements Keypair { const secretKey: Uint8Array = secp.utils.randomPrivateKey(); const publicKey: Uint8Array = secp.getPublicKey(secretKey, true); - this.keypair = {publicKey, secretKey}; + this.keypair = { publicKey, secretKey }; } } - + /** * Get the key scheme of the keypair Secp256k1 */ @@ -60,7 +61,7 @@ export class Secp256k1Keypair implements Keypair { const secretKey = secp.utils.randomPrivateKey(); const publicKey = secp.getPublicKey(secretKey, true); - return new Secp256k1Keypair({publicKey, secretKey}); + return new Secp256k1Keypair({ publicKey, secretKey }); } /** @@ -76,7 +77,6 @@ export class Secp256k1Keypair implements Keypair { * @param options: skip secret key validation */ - static fromSecretKey( secretKey: Uint8Array, options?: { skipValidation?: boolean } @@ -87,11 +87,11 @@ export class Secp256k1Keypair implements Keypair { const signData = encoder.encode('sui validation'); const msgHash = sha256(signData); const signature = secp.signSync(msgHash, secretKey); - if (!secp.verify(signature, msgHash, publicKey)) { + if (!secp.verify(signature, msgHash, publicKey, { strict: true })) { throw new Error('Provided secretKey is invalid'); } } - return new Secp256k1Keypair({publicKey, secretKey}); + return new Secp256k1Keypair({ publicKey, secretKey }); } /** @@ -101,7 +101,7 @@ export class Secp256k1Keypair implements Keypair { */ static fromSeed(seed: Uint8Array): Secp256k1Keypair { let publicKey = secp.getPublicKey(seed, true); - return new Secp256k1Keypair({publicKey, secretKey: seed}); + return new Secp256k1Keypair({ publicKey, secretKey: seed }); } /** @@ -116,8 +116,13 @@ export class Secp256k1Keypair implements Keypair { */ signData(data: Base64DataBuffer): Base64DataBuffer { const msgHash = sha256(data.getData()); - return new Base64DataBuffer( - secp.signSync(msgHash, this.keypair.secretKey) - ); + const [sig, rec_id] = secp.signSync(msgHash, this.keypair.secretKey, { + canonical: true, + recovered: true, + }); + var recoverable_sig = new Uint8Array(65); + recoverable_sig.set(Signature.fromDER(sig).toCompactRawBytes()); + recoverable_sig.set([rec_id], 64); + return new Base64DataBuffer(recoverable_sig); } } diff --git a/sdk/typescript/test/unit/cryptography/secp256k1-keypair.test.ts b/sdk/typescript/test/unit/cryptography/secp256k1-keypair.test.ts index 577bbd01fe8c3..d503d6b9b36f1 100644 --- a/sdk/typescript/test/unit/cryptography/secp256k1-keypair.test.ts +++ b/sdk/typescript/test/unit/cryptography/secp256k1-keypair.test.ts @@ -4,6 +4,7 @@ import { Base64DataBuffer, Secp256k1Keypair } from '../../../src'; import { describe, it, expect } from 'vitest'; import * as secp from '@noble/secp256k1'; +import { Signature } from '@noble/secp256k1'; // Test case from https://github.com/rust-bitcoin/rust-secp256k1/blob/master/examples/sign_verify.rs#L26 const VALID_SECP256K1_SECRET_KEY = [ @@ -64,12 +65,15 @@ describe('secp256k1-keypair', () => { ); const msgHash = await secp.utils.sha256(signData.getData()); - const signature = keypair.signData(signData); - const isValid = secp.verify( - signature.getData(), + const sig = keypair.signData(signData); + const pubkey = secp.recoverPublicKey( msgHash, - keypair.getPublicKey().toBytes() + Signature.fromCompact(sig.getData().slice(0, 64)), + sig.getData()[64], + true + ); + expect(Buffer.from(pubkey).toString('base64')).toEqual( + keypair.getPublicKey().toBase64() ); - expect(isValid).toBeTruthy(); }); }); diff --git a/sdk/typescript/test/unit/signers/raw-signer.test.ts b/sdk/typescript/test/unit/signers/raw-signer.test.ts index 48fe10cd4a6f0..b2aa5dc14175d 100644 --- a/sdk/typescript/test/unit/signers/raw-signer.test.ts +++ b/sdk/typescript/test/unit/signers/raw-signer.test.ts @@ -10,6 +10,7 @@ import { Secp256k1Keypair, } from '../../../src'; import * as secp from '@noble/secp256k1'; +import { Signature } from '@noble/secp256k1'; describe('RawSigner', () => { it('Ed25519 keypair signData', async () => { @@ -35,11 +36,14 @@ describe('RawSigner', () => { const msgHash = await secp.utils.sha256(signData.getData()); const signer = new RawSigner(keypair); const { signature, pubKey } = await signer.signData(signData); - const isSecpValid = secp.verify( - signature.getData(), + const recovered_pubkey = secp.recoverPublicKey( msgHash, - pubKey.toBytes() + Signature.fromCompact(signature.getData().slice(0, 64)), + signature.getData()[64], + true ); - expect(isSecpValid).toBeTruthy(); + const expected = keypair.getPublicKey().toBase64(); + expect(pubKey.toBase64()).toEqual(expected); + expect(Buffer.from(recovered_pubkey).toString('base64')).toEqual(expected); }); });