From c8dfd2c442ff8589a21b493412137813224394e0 Mon Sep 17 00:00:00 2001 From: junderw Date: Thu, 11 Nov 2021 22:45:11 +0900 Subject: [PATCH] Add schnorr signing and verification --- src/bip32.js | 8 ++++++++ test/index.js | 6 +++++- ts-src/bip32.ts | 13 +++++++++++++ types/bip32.d.ts | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/bip32.js b/src/bip32.js index 86f806d..deb6f09 100644 --- a/src/bip32.js +++ b/src/bip32.js @@ -209,9 +209,17 @@ function default_1(ecc) { return sig; } } + signSchnorr(hash) { + if (!this.privateKey) + throw new Error('Missing private key'); + return Buffer.from(ecc.signSchnorr(hash, this.privateKey)); + } verify(hash, signature) { return ecc.verify(hash, this.publicKey, signature); } + verifySchnorr(hash, signature) { + return ecc.verifySchnorr(hash, this.publicKey.subarray(1, 33), signature); + } } function fromBase58(inString, network) { const buffer = bs58check.decode(inString); diff --git a/test/index.js b/test/index.js index be09084..2d44965 100644 --- a/test/index.js +++ b/test/index.js @@ -215,15 +215,19 @@ tape('ecdsa', (t) => { let seed = Buffer.alloc(32, 1) let hash = Buffer.alloc(32, 2) let signature = Buffer.from('9636ee2fac31b795a308856b821ebe297dda7b28220fb46ea1fbbd7285977cc04c82b734956246a0f15a9698f03f546d8d96fe006c8e7bd2256ca7c8229e6f5c', 'hex') + let schnorrsig = Buffer.from('2fae8b517cb0e7302ca48a4109d1819e3d75af96bd58d297023e3058c4e98ff812fe6ae32a2b2bc4abab10f88f7fe56efbafc8a4e4fa437af78926f528b0585e', 'hex') let signatureLowR = Buffer.from('0587a40b391b76596c257bf59565b24eaff2cc42b45caa2640902e73fb97a6e702c3402ab89348a7dae1bf171c3e172fa60353d7b01621a94cb7caca59b995db', 'hex') let node = BIP32.fromSeed(seed) - t.plan(6) + t.plan(9) t.equal(node.sign(hash).toString('hex'), signature.toString('hex')) t.equal(node.sign(hash, true).toString('hex'), signatureLowR.toString('hex')) + t.equal(node.signSchnorr(hash).toString('hex'), schnorrsig.toString('hex')) t.equal(node.verify(hash, signature), true) t.equal(node.verify(seed, signature), false) t.equal(node.verify(hash, signatureLowR), true) t.equal(node.verify(seed, signatureLowR), false) + t.equal(node.verifySchnorr(hash, schnorrsig), true) + t.equal(node.verifySchnorr(seed, schnorrsig), false) }) }) \ No newline at end of file diff --git a/ts-src/bip32.ts b/ts-src/bip32.ts index 0b7d16c..538a960 100644 --- a/ts-src/bip32.ts +++ b/ts-src/bip32.ts @@ -35,6 +35,8 @@ export interface BIP32Interface { derivePath(path: string): BIP32Interface; sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): boolean; + signSchnorr(hash: Buffer): Buffer; + verifySchnorr(hash: Buffer, signature: Buffer): boolean; } export interface BIP32API { @@ -63,12 +65,14 @@ export interface TinySecp256k1Interface { ): Uint8Array | null; privateAdd(d: Uint8Array, tweak: Uint8Array): Uint8Array | null; sign(h: Uint8Array, d: Uint8Array, e?: Uint8Array): Uint8Array; + signSchnorr(h: Uint8Array, d: Uint8Array, e?: Uint8Array): Uint8Array; verify( h: Uint8Array, Q: Uint8Array, signature: Uint8Array, strict?: boolean, ): boolean; + verifySchnorr(h: Uint8Array, Q: Uint8Array, signature: Uint8Array): boolean; } export default function(ecc: TinySecp256k1Interface): BIP32API { @@ -340,9 +344,18 @@ export default function(ecc: TinySecp256k1Interface): BIP32API { } } + signSchnorr(hash: Buffer): Buffer { + if (!this.privateKey) throw new Error('Missing private key'); + return Buffer.from(ecc.signSchnorr(hash, this.privateKey)); + } + verify(hash: Buffer, signature: Buffer): boolean { return ecc.verify(hash, this.publicKey, signature); } + + verifySchnorr(hash: Buffer, signature: Buffer): boolean { + return ecc.verifySchnorr(hash, this.publicKey.subarray(1, 33), signature); + } } function fromBase58(inString: string, network?: Network): BIP32Interface { diff --git a/types/bip32.d.ts b/types/bip32.d.ts index 83fda9a..9a5950b 100644 --- a/types/bip32.d.ts +++ b/types/bip32.d.ts @@ -30,6 +30,8 @@ export interface BIP32Interface { derivePath(path: string): BIP32Interface; sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): boolean; + signSchnorr(hash: Buffer): Buffer; + verifySchnorr(hash: Buffer, signature: Buffer): boolean; } export interface BIP32API { fromSeed(seed: Buffer, network?: Network): BIP32Interface; @@ -44,7 +46,9 @@ export interface TinySecp256k1Interface { pointAddScalar(p: Uint8Array, tweak: Uint8Array, compressed?: boolean): Uint8Array | null; privateAdd(d: Uint8Array, tweak: Uint8Array): Uint8Array | null; sign(h: Uint8Array, d: Uint8Array, e?: Uint8Array): Uint8Array; + signSchnorr(h: Uint8Array, d: Uint8Array, e?: Uint8Array): Uint8Array; verify(h: Uint8Array, Q: Uint8Array, signature: Uint8Array, strict?: boolean): boolean; + verifySchnorr(h: Uint8Array, Q: Uint8Array, signature: Uint8Array): boolean; } export default function (ecc: TinySecp256k1Interface): BIP32API; export {};