From dc64b94874e0bddb04062b869e84f9469e8b9600 Mon Sep 17 00:00:00 2001 From: feri42 Date: Tue, 25 Jun 2024 20:06:53 +0200 Subject: [PATCH 1/5] feat: add commin signature encoding --- features/keychain/module/crypto/secp256k1.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/features/keychain/module/crypto/secp256k1.js b/features/keychain/module/crypto/secp256k1.js index 378e7a53..8cceb86b 100644 --- a/features/keychain/module/crypto/secp256k1.js +++ b/features/keychain/module/crypto/secp256k1.js @@ -8,6 +8,18 @@ import { tweakPrivateKey } from './tweak' const isValidEcOptions = (ecOptions) => !ecOptions || Object.keys(ecOptions).every((key) => ['canonical'].includes(key)) +const encodeSignature = ({ signature, enc }) => { + if (enc === 'der') return Buffer.from(signature.toDER()) + + const sig = { ...signature } + + if (enc === 'raw') return sig + + const r = Buffer.from(sig.r.toArray('be', 32)) + const s = Buffer.from(sig.s.toArray('be', 32)) + return Buffer.concat([r, s, Buffer.from([sig.recoveryParam])]) +} + export const create = ({ getPrivateHDKey }) => { const EC = elliptic.ec const curve = new EC('secp256k1') @@ -18,11 +30,11 @@ export const create = ({ getPrivateHDKey }) => { keyId.keyType === 'secp256k1', `ECDSA signatures are not supported for ${keyId.keyType}` ) - assert(['der', 'raw'].includes(enc), 'signBuffer: invalid enc') + assert(['der', 'raw', 'buff'].includes(enc), 'signBuffer: invalid enc') assert(isValidEcOptions(ecOptions), 'signBuffer: invalid EC option') const { privateKey } = getPrivateHDKey({ seedId, keyId }) const signature = curve.sign(data, privateKey, pick(ecOptions, ['canonical'])) - return enc === 'der' ? Buffer.from(signature.toDER()) : { ...signature } + return encodeSignature({ signature, enc }) }, signSchnorr: async ({ seedId, keyId, data, tweak, extraEntropy }) => { assert( From 224a82837d2026ca8dc843abe2763be1466d70e5 Mon Sep 17 00:00:00 2001 From: feri42 Date: Wed, 26 Jun 2024 15:41:19 +0200 Subject: [PATCH 2/5] refactor: rename encoding --- features/keychain/module/crypto/secp256k1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/keychain/module/crypto/secp256k1.js b/features/keychain/module/crypto/secp256k1.js index 8cceb86b..4851c332 100644 --- a/features/keychain/module/crypto/secp256k1.js +++ b/features/keychain/module/crypto/secp256k1.js @@ -30,7 +30,7 @@ export const create = ({ getPrivateHDKey }) => { keyId.keyType === 'secp256k1', `ECDSA signatures are not supported for ${keyId.keyType}` ) - assert(['der', 'raw', 'buff'].includes(enc), 'signBuffer: invalid enc') + assert(['der', 'raw', 'binary'].includes(enc), 'signBuffer: invalid enc') assert(isValidEcOptions(ecOptions), 'signBuffer: invalid EC option') const { privateKey } = getPrivateHDKey({ seedId, keyId }) const signature = curve.sign(data, privateKey, pick(ecOptions, ['canonical'])) From 9a109b470acc6831743dd9a78fbe096752baac14 Mon Sep 17 00:00:00 2001 From: feri42 Date: Thu, 27 Jun 2024 20:06:07 +0200 Subject: [PATCH 3/5] chore: add encoding tests --- .../keychain/module/__tests__/ecdsa.test.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/features/keychain/module/__tests__/ecdsa.test.js b/features/keychain/module/__tests__/ecdsa.test.js index 1660d93a..247abf0d 100644 --- a/features/keychain/module/__tests__/ecdsa.test.js +++ b/features/keychain/module/__tests__/ecdsa.test.js @@ -93,3 +93,46 @@ describe.each([ expect(signature.toString('hex')).toBe(expected) }) }) + +describe('EcDSA Signer Signature Encoding', () => { + const keychain = createKeychain({ seed }) + const data = Buffer.from('I really love keychains') + const expected = { + default: + '30460221009288b22525674d76b0d5b8b20f333d4de4f4f88340a7d0a4cadd54b719e6162d022100f63e7591ce6b3bc0bf66fa2948d220e74ea2a74b63fc9dcb20e0f53191550b67', + binary: + '9288b22525674d76b0d5b8b20f333d4de4f4f88340a7d0a4cadd54b719e6162df63e7591ce6b3bc0bf66fa2948d220e74ea2a74b63fc9dcb20e0f53191550b6700', + } + it('Default encoding', async () => { + const signature = await keychain.secp256k1.signBuffer({ seedId, keyId, data }) + expect(signature instanceof Buffer).toBe(true) + expect(signature.toString('hex')).toBe(expected.default) + }) + + it('DER encoding', async () => { + const signature = await keychain.secp256k1.signBuffer({ seedId, keyId, data, enc: 'der' }) + expect(signature instanceof Buffer).toBe(true) + expect(signature.toString('hex')).toBe(expected.default) + }) + + it('Binary encoding', async () => { + const signature = await keychain.secp256k1.signBuffer({ seedId, keyId, data, enc: 'binary' }) + expect(signature instanceof Buffer).toBe(true) + expect(signature.toString('hex')).toBe(expected.binary) + }) + + it('Raw encoding', async () => { + const signature = await keychain.secp256k1.signBuffer({ seedId, keyId, data, enc: 'raw' }) + expect(typeof signature === 'object') + expect(Object.getOwnPropertyNames(signature)).toStrictEqual(['r', 's', 'recoveryParam']) + const r = Buffer.from(signature.r.toArray('be', 32)) + const s = Buffer.from(signature.s.toArray('be', 32)) + const binary = Buffer.concat([r, s, Buffer.from([signature.recoveryParam])]) + expect(binary.toString('hex')).toBe(expected.binary) + }) + + it('Invalid encoding', async () => { + const sign = () => keychain.secp256k1.signBuffer({ seedId, keyId, data, enc: 'other' }) + await expect(sign()).rejects.toThrow(/signBuffer: invalid enc/) + }) +}) From d66bc61edd201ee41926c8da90346999cca78542 Mon Sep 17 00:00:00 2001 From: feri42 Date: Thu, 27 Jun 2024 20:07:16 +0200 Subject: [PATCH 4/5] refactor: fix spacing --- features/keychain/module/__tests__/ecdsa.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/features/keychain/module/__tests__/ecdsa.test.js b/features/keychain/module/__tests__/ecdsa.test.js index 247abf0d..843eae62 100644 --- a/features/keychain/module/__tests__/ecdsa.test.js +++ b/features/keychain/module/__tests__/ecdsa.test.js @@ -103,6 +103,7 @@ describe('EcDSA Signer Signature Encoding', () => { binary: '9288b22525674d76b0d5b8b20f333d4de4f4f88340a7d0a4cadd54b719e6162df63e7591ce6b3bc0bf66fa2948d220e74ea2a74b63fc9dcb20e0f53191550b6700', } + it('Default encoding', async () => { const signature = await keychain.secp256k1.signBuffer({ seedId, keyId, data }) expect(signature instanceof Buffer).toBe(true) From eb39accbe209e623b667bfb5096c7a9b6800f0a7 Mon Sep 17 00:00:00 2001 From: feri42 Date: Thu, 4 Jul 2024 17:40:57 +0200 Subject: [PATCH 5/5] refactor: simplify --- features/keychain/module/crypto/secp256k1.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/features/keychain/module/crypto/secp256k1.js b/features/keychain/module/crypto/secp256k1.js index 4851c332..f91425c9 100644 --- a/features/keychain/module/crypto/secp256k1.js +++ b/features/keychain/module/crypto/secp256k1.js @@ -11,13 +11,11 @@ const isValidEcOptions = (ecOptions) => const encodeSignature = ({ signature, enc }) => { if (enc === 'der') return Buffer.from(signature.toDER()) - const sig = { ...signature } + if (enc === 'raw') return { ...signature } - if (enc === 'raw') return sig - - const r = Buffer.from(sig.r.toArray('be', 32)) - const s = Buffer.from(sig.s.toArray('be', 32)) - return Buffer.concat([r, s, Buffer.from([sig.recoveryParam])]) + const r = Buffer.from(signature.r.toArray('be', 32)) + const s = Buffer.from(signature.s.toArray('be', 32)) + return Buffer.concat([r, s, Buffer.from([signature.recoveryParam])]) } export const create = ({ getPrivateHDKey }) => {