From 7474b8245c7c9c40b917110c7a00b53ce8f70913 Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Mon, 18 Sep 2023 12:50:43 +0300 Subject: [PATCH 1/7] remove multiple sign and verify functions, leave only 1 more universal --- src/core/index.ts | 210 ++++++++------------- src/schnorrkel.ts | 48 +---- tests/schnorrkel/multiSigSign.test.ts | 4 +- tests/schnorrkel/onchainMultiSign.test.ts | 42 ++--- tests/schnorrkel/onchainSingleSign.test.ts | 4 +- tests/schnorrkel/sign.test.ts | 5 +- tests/schnorrkel/sumSigs.test.ts | 6 +- tests/schnorrkel/verify.test.ts | 51 ++--- 8 files changed, 146 insertions(+), 224 deletions(-) diff --git a/src/core/index.ts b/src/core/index.ts index f32b807..4346349 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -66,39 +66,66 @@ const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => { )) } -/** - * Sign the given hash by the private key - * - * @param Buffer privateKey - * @param string hash - * @returns InternalSignature - */ -const internalSign = (privateKey: Buffer, hash: string): InternalSignature => { - const localPk = Buffer.from(privateKey) - const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk)) +export const _generateL = (publicKeys: Array) => { + return ethers.utils.keccak256(_concatTypedArrays(publicKeys.sort(Buffer.compare))) +} - // R = G * k - var k = ethers.utils.randomBytes(32) - var R = Buffer.from(secp256k1.publicKeyCreate(k)) +export const _concatTypedArrays = (publicKeys: Buffer[]): Buffer => { + const c: Buffer = Buffer.alloc(publicKeys.reduce((partialSum, publicKey) => partialSum + publicKey.length, 0)) + publicKeys.map((publicKey, index) => c.set(publicKey, (index * publicKey.length))) + return Buffer.from(c.buffer) +} - // e = h(address(R) || compressed pubkey || m) - var e = challenge(R, hash, publicKey) - // xe = x * e - var xe = secp256k1.privateKeyTweakMul(localPk, e) +export const _aCoefficient = (publicKey: Buffer, L: string): Buffer => { + return Buffer.from(ethers.utils.arrayify(ethers.utils.solidityKeccak256( + ['bytes', 'bytes'], + [L, publicKey] + ))) +} - // s = k + xe mod(n) - var s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe)) - s = bigi.fromBuffer(s).mod(n).toBuffer(32) +export const generateRandomKeys = () => { + let privKeyBytes: Buffer + do { + privKeyBytes = Buffer.from(ethers.utils.randomBytes(32)) + } while (!secp256k1.privateKeyVerify(privKeyBytes)) + + const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKeyBytes)) + + const data = { + publicKey: pubKey, + privateKey: privKeyBytes, + } + + return new KeyPair(data) +} + +export const _hashPrivateKey = (privateKey: Buffer): string => { + return ethers.utils.keccak256(privateKey) +} + +export const _generatePublicNonces = (privateKey: Buffer): { + privateNonceData: Pick, + publicNonceData: InternalPublicNonces, + hash: string, +} => { + const hash = _hashPrivateKey(privateKey) + const nonce = _generateNonce() return { - finalPublicNonce: R, - challenge: e, - signature: s + hash, + privateNonceData: { + k: nonce.k, + kTwo: nonce.kTwo, + }, + publicNonceData: { + kPublic: nonce.kPublic, + kTwoPublic: nonce.kTwoPublic, + } } } -const internalMultiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, hash: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[]): InternalSignature => { +export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, hash: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[]): InternalSignature => { if (publicKeys.length < 2) { throw Error('At least 2 public keys should be provided') } @@ -155,16 +182,16 @@ const internalMultiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, } } -/** - * Verify a signature for the given hash, public nonce and public key - * - * @param Buffer s - * @param string hash - * @param Buffer R - * @param Buffer publicKey - * @returns boolean - */ -const internalVerify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { +export const _sumSigs = (signatures: Buffer[]): Buffer => { + let combined = bigi.fromBuffer(signatures[0]) + signatures.shift() + signatures.forEach(sig => { + combined = combined.add(bigi.fromBuffer(sig)) + }) + return combined.mod(n).toBuffer(32) +} + +export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { const eC = challenge(R, hash, publicKey) const sG = generatorPoint.mul(ethers.utils.arrayify(s)) const P = ec.keyFromPublic(publicKey).getPublic() @@ -175,109 +202,32 @@ const internalVerify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): return sG.eq(RplusPe) } -export const _generateL = (publicKeys: Array) => { - return ethers.utils.keccak256(_concatTypedArrays(publicKeys.sort(Buffer.compare))) -} - -export const _concatTypedArrays = (publicKeys: Buffer[]): Buffer => { - const c: Buffer = Buffer.alloc(publicKeys.reduce((partialSum, publicKey) => partialSum + publicKey.length, 0)) - publicKeys.map((publicKey, index) => c.set(publicKey, (index * publicKey.length))) - return Buffer.from(c.buffer) -} - - -export const _aCoefficient = (publicKey: Buffer, L: string): Buffer => { - return Buffer.from(ethers.utils.arrayify(ethers.utils.solidityKeccak256( - ['bytes', 'bytes'], - [L, publicKey] - ))) +export const _generatePk = (combinedPublicKey: Buffer): string => { + const px = ethers.utils.hexlify(combinedPublicKey.subarray(1,33)) + return '0x' + px.slice(px.length - 40, px.length) } -export const generateRandomKeys = () => { - let privKeyBytes: Buffer - do { - privKeyBytes = Buffer.from(ethers.utils.randomBytes(32)) - } while (!secp256k1.privateKeyVerify(privKeyBytes)) - - const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKeyBytes)) +export const _sign = (privateKey: Buffer, hash: string): InternalSignature => { + const localPk = Buffer.from(privateKey) + const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk)) - const data = { - publicKey: pubKey, - privateKey: privKeyBytes, - } + // R = G * k + var k = ethers.utils.randomBytes(32) + var R = Buffer.from(secp256k1.publicKeyCreate(k)) - return new KeyPair(data) -} + // e = h(address(R) || compressed pubkey || m) + var e = challenge(R, hash, publicKey) -export const _hashPrivateKey = (privateKey: Buffer): string => { - return ethers.utils.keccak256(privateKey) -} + // xe = x * e + var xe = secp256k1.privateKeyTweakMul(localPk, e) -export const _generatePublicNonces = (privateKey: Buffer): { - privateNonceData: Pick, - publicNonceData: InternalPublicNonces, - hash: string, -} => { - const hash = _hashPrivateKey(privateKey) - const nonce = _generateNonce() + // s = k + xe mod(n) + var s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe)) + s = bigi.fromBuffer(s).mod(n).toBuffer(32) return { - hash, - privateNonceData: { - k: nonce.k, - kTwo: nonce.kTwo, - }, - publicNonceData: { - kPublic: nonce.kPublic, - kTwoPublic: nonce.kTwoPublic, - } + finalPublicNonce: R, + challenge: e, + signature: s } -} - -export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, msg: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[], hashFn: Function|null = null): InternalSignature => { - const hashMsg = hashFn ? hashFn : _hashMessage - const hash = hashMsg(msg) - return internalMultiSigSign(nonces, combinedPublicKey, privateKey, hash, publicKeys, publicNonces) -} - -export const _multiSigSignHash = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, hash: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[]): InternalSignature => { - return internalMultiSigSign(nonces, combinedPublicKey, privateKey, hash, publicKeys, publicNonces) -} - -export const _sumSigs = (signatures: Buffer[]): Buffer => { - let combined = bigi.fromBuffer(signatures[0]) - signatures.shift() - signatures.forEach(sig => { - combined = combined.add(bigi.fromBuffer(sig)) - }) - return combined.mod(n).toBuffer(32) -} - -export const _hashMessage = (message: string): string => { - return ethers.utils.solidityKeccak256(['string'], [message]) -} - -export const _verify = (s: Buffer, msg: string, R: Buffer, publicKey: Buffer, hashFn: Function|null = null): boolean => { - const hashMsg = hashFn ? hashFn : _hashMessage - const hash = hashMsg(msg) - return internalVerify(s, hash, R, publicKey) -} - -export const _verifyHash = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { - return internalVerify(s, hash, R, publicKey) -} - -export const _generatePk = (combinedPublicKey: Buffer): string => { - const px = ethers.utils.hexlify(combinedPublicKey.subarray(1,33)) - return '0x' + px.slice(px.length - 40, px.length) -} - -export const _sign = (privateKey: Buffer, msg: string, hashFn: Function|null = null): InternalSignature => { - const hashMsg = hashFn ? hashFn : _hashMessage - const hash = hashMsg(msg) - return internalSign(privateKey, hash) -} - -export const _signHash = (privateKey: Buffer, hash: string): InternalSignature => { - return internalSign(privateKey, hash) } \ No newline at end of file diff --git a/src/schnorrkel.ts b/src/schnorrkel.ts index 873a6ab..013c300 100644 --- a/src/schnorrkel.ts +++ b/src/schnorrkel.ts @@ -2,7 +2,7 @@ import secp256k1 from 'secp256k1' import { Key, Nonces, PublicNonces, Signature, NoncePairs } from './types' -import { _generateL, _aCoefficient, _generatePublicNonces, _multiSigSign, _hashPrivateKey, _sumSigs, _verify, _generatePk, _sign, _signHash, _verifyHash, _multiSigSignHash } from './core' +import { _generateL, _aCoefficient, _generatePublicNonces, _multiSigSign, _hashPrivateKey, _sumSigs, _verify, _generatePk, _sign } from './core' import { InternalNonces, InternalPublicNonces, InternalSignature } from './core/types' import { Challenge, FinalPublicNonce, SignatureOutput } from './types/signature' @@ -112,46 +112,22 @@ class Schnorrkel { return hash in this.nonces } - multiSigSign(privateKey: Key, msg: string, publicKeys: Key[], publicNonces: PublicNonces[], hashFn: Function|null = null): SignatureOutput { + multiSigSign(privateKey: Key, hash: string, publicKeys: Key[], publicNonces: PublicNonces[]): SignatureOutput { const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) const mappedPublicNonce = this.getMappedPublicNonces(publicNonces) const mappedNonces = this.getMappedNonces() - const musigData = _multiSigSign(mappedNonces, combinedPublicKey.buffer, privateKey.buffer, msg, publicKeys.map(key => key.buffer), mappedPublicNonce, hashFn) + const musigData = _multiSigSign(mappedNonces, combinedPublicKey.buffer, privateKey.buffer, hash, publicKeys.map(key => key.buffer), mappedPublicNonce) - // absolutely crucial to delete the nonces once a signature has been crafted with them. + // absolutely crucial to delete the nonces once a signature has been crafted. // nonce reuse will lead to private key leakage! this.clearNonces(privateKey) return this.getMultisigOutput(musigData) } - multiSigSignHash(privateKey: Key, hash: string, publicKeys: Key[], publicNonces: PublicNonces[]): SignatureOutput { - const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const mappedPublicNonce = this.getMappedPublicNonces(publicNonces) - const mappedNonces = this.getMappedNonces() - - const musigData = _multiSigSignHash(mappedNonces, combinedPublicKey.buffer, privateKey.buffer, hash, publicKeys.map(key => key.buffer), mappedPublicNonce) - - // absolutely crucial to delete the nonces once a signature has been crafted with them. - // nonce reuse will lead to private key leakage! - this.clearNonces(privateKey) - - return this.getMultisigOutput(musigData) - } - - static sign(privateKey: Key, msg: string, hashFn: Function|null = null): SignatureOutput { - const output = _sign(privateKey.buffer, msg, hashFn) - - return { - signature: new Signature(Buffer.from(output.signature)), - finalPublicNonce: new FinalPublicNonce(Buffer.from(output.finalPublicNonce)), - challenge: new Challenge(Buffer.from(output.challenge)), - } - } - - static signHash(privateKey: Key, hash: string): SignatureOutput { - const output = _signHash(privateKey.buffer, hash) + static sign(privateKey: Key, hash: string): SignatureOutput { + const output = _sign(privateKey.buffer, hash) return { signature: new Signature(Buffer.from(output.signature)), @@ -167,22 +143,12 @@ class Schnorrkel { } static verify( - signature: Signature, - msg: string, - finalPublicNonce: FinalPublicNonce, - publicKey: Key, - hashFn: Function|null = null - ): boolean { - return _verify(signature.buffer, msg, finalPublicNonce.buffer, publicKey.buffer, hashFn) - } - - static verifyHash( signature: Signature, hash: string, finalPublicNonce: FinalPublicNonce, publicKey: Key ): boolean { - return _verifyHash(signature.buffer, hash, finalPublicNonce.buffer, publicKey.buffer) + return _verify(signature.buffer, hash, finalPublicNonce.buffer, publicKey.buffer) } } diff --git a/tests/schnorrkel/multiSigSign.test.ts b/tests/schnorrkel/multiSigSign.test.ts index a879ace..0a84848 100644 --- a/tests/schnorrkel/multiSigSign.test.ts +++ b/tests/schnorrkel/multiSigSign.test.ts @@ -19,7 +19,7 @@ describe('testing multiSigSign', () => { const publicKeys = [keyPairOne.publicKey, keyPairTwo.publicKey] const msg = 'test message' - const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msg, publicKeys, publicNonces) + const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, ethers.utils.solidityKeccak256(['string'], [msg]), publicKeys, publicNonces) expect(signature).toBeDefined() expect(signature.finalPublicNonce.buffer).toHaveLength(33) @@ -63,7 +63,7 @@ describe('testing multiSigSign', () => { const msg = 'test message' const hash = ethers.utils.solidityKeccak256(['string'], [msg]) - const signature = schnorrkelOne.multiSigSignHash(keyPairOne.privateKey, hash, publicKeys, publicNonces) + const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, hash, publicKeys, publicNonces) expect(signature).toBeDefined() expect(signature.finalPublicNonce.buffer).toHaveLength(33) diff --git a/tests/schnorrkel/onchainMultiSign.test.ts b/tests/schnorrkel/onchainMultiSign.test.ts index 6f4a987..c56a2b7 100644 --- a/tests/schnorrkel/onchainMultiSign.test.ts +++ b/tests/schnorrkel/onchainMultiSign.test.ts @@ -34,11 +34,12 @@ describe('Multi Sign Tests', function () { const { contract } = await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) - const {signature: sigTwo} = signerTwo.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigTwo} = signerTwo.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) // the multisig px and parity @@ -55,7 +56,6 @@ describe('Multi Sign Tests', function () { sSummed.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal(ERC1271_MAGICVALUE_BYTES32) }) @@ -67,16 +67,16 @@ describe('Multi Sign Tests', function () { const { contract } = await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) - const {signature: sigTwo} = signerTwo.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigTwo} = signerTwo.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) // the multisig px and parity const px = ethers.utils.hexlify(combinedPublicKey.buffer.slice(1, 33)) - const combinedPublicAddress = '0x' + px.slice(px.length - 40, px.length) const parity = combinedPublicKey.buffer[0] - 2 + 27 @@ -88,7 +88,6 @@ describe('Multi Sign Tests', function () { sSummed.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal(ERC1271_MAGICVALUE_BYTES32) }) @@ -101,6 +100,7 @@ describe('Multi Sign Tests', function () { const signerThree = new DefaultSigner(2) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerThree.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerThree.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) @@ -108,8 +108,8 @@ describe('Multi Sign Tests', function () { // finalPublicNonce: FinalPublicNonce, // the final public nonce // challenge: Challenge, // the schnorr challenge // signature: Signature, // the signature - const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) - const {signature: sigTwo} = signerThree.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigTwo} = signerThree.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) // the multisig px and parity @@ -124,7 +124,6 @@ describe('Multi Sign Tests', function () { sSummed.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal('0xffffffff') }) @@ -136,10 +135,11 @@ describe('Multi Sign Tests', function () { const { contract } = await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) // the multisig px and parity const px = combinedPublicKey.buffer.slice(1,33) @@ -153,7 +153,6 @@ describe('Multi Sign Tests', function () { sigOne.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal('0xffffffff') }) @@ -162,13 +161,14 @@ describe('Multi Sign Tests', function () { // deploy the contract const signerOne = new DefaultSigner(0) const signerTwo = new DefaultSigner(1) - const { contract } = await deployContract(signerOne, signerTwo) + await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] - const {signature: s, challenge: e} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) - expect(signerOne.multiSignMessage.bind(signerOne, msg, publicKeys, publicNonces)).to.throw('Nonces should be exchanged before signing') + signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + expect(signerOne.multiSignMessage.bind(signerOne, msgHash, publicKeys, publicNonces)).to.throw('Nonces should be exchanged before signing') }) it('should fail if only one signer tries to sign the transaction providing 2 messages', async function () { @@ -178,13 +178,14 @@ describe('Multi Sign Tests', function () { const { contract } = await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const signerTwoNonces = signerTwo.getPublicNonces() const publicNonces = [signerOne.getPublicNonces(), signerTwoNonces] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) const publicNoncesTwo = [signerOne.getPublicNonces(), signerTwoNonces] - const {signature: sigTwo} = signerOne.multiSignMessage(msg, publicKeys, publicNoncesTwo) + const {signature: sigTwo} = signerOne.multiSignMessage(msgHash, publicKeys, publicNoncesTwo) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) // the multisig px and parity @@ -199,7 +200,6 @@ describe('Multi Sign Tests', function () { sSummed.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal('0xffffffff') }) @@ -211,11 +211,12 @@ describe('Multi Sign Tests', function () { const { contract } = await deployContract(signerOne, signerTwo) const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const publicKeys = [signerTwo.getPublicKey(), signerOne.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) - const {signature: sigTwo} = signerTwo.multiSignMessage(msg, publicKeys, publicNonces) + const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigTwo} = signerTwo.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) // the multisig px and parity @@ -230,7 +231,6 @@ describe('Multi Sign Tests', function () { sSummed.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal(ERC1271_MAGICVALUE_BYTES32) }) diff --git a/tests/schnorrkel/onchainSingleSign.test.ts b/tests/schnorrkel/onchainSingleSign.test.ts index 1f5c1ea..e69791d 100644 --- a/tests/schnorrkel/onchainSingleSign.test.ts +++ b/tests/schnorrkel/onchainSingleSign.test.ts @@ -31,8 +31,9 @@ describe('Single Sign Tests', function () { // sign const msg = 'just a test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const pkBuffer = new Key(Buffer.from(ethers.utils.arrayify(pk1))) - const sig = Schnorrkel.sign(pkBuffer, msg) + const sig = Schnorrkel.sign(pkBuffer, msgHash) // wrap the result const publicKey = secp256k1.publicKeyCreate(ethers.utils.arrayify(pk1)) @@ -45,7 +46,6 @@ describe('Single Sign Tests', function () { sig.signature.buffer, parity ]) - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const result = await contract.isValidSignature(msgHash, sigData) expect(result).to.equal(ERC1271_MAGICVALUE_BYTES32) }) diff --git a/tests/schnorrkel/sign.test.ts b/tests/schnorrkel/sign.test.ts index b5958a7..db8181e 100644 --- a/tests/schnorrkel/sign.test.ts +++ b/tests/schnorrkel/sign.test.ts @@ -9,7 +9,8 @@ describe('testing sign', () => { const keyPair = generateRandomKeys() const msg = 'test message' - const signature = Schnorrkel.sign(keyPair.privateKey, msg) + const hash = ethers.utils.solidityKeccak256(['string'], [msg]) + const signature = Schnorrkel.sign(keyPair.privateKey, hash) expect(signature).toBeDefined() expect(signature.finalPublicNonce.buffer).toHaveLength(33) @@ -22,7 +23,7 @@ describe('testing sign', () => { const msg = 'test message' const hash = ethers.utils.solidityKeccak256(['string'], [msg]) - const signature = Schnorrkel.signHash(keyPair.privateKey, hash) + const signature = Schnorrkel.sign(keyPair.privateKey, hash) expect(signature).toBeDefined() expect(signature.finalPublicNonce.buffer).toHaveLength(33) diff --git a/tests/schnorrkel/sumSigs.test.ts b/tests/schnorrkel/sumSigs.test.ts index 7e3a9e0..4688968 100644 --- a/tests/schnorrkel/sumSigs.test.ts +++ b/tests/schnorrkel/sumSigs.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest' import Schnorrkel from '../../src/index' import { _hashPrivateKey, generateRandomKeys } from '../../src/core' +import { ethers } from 'ethers' describe('testing sumSigs', () => { @@ -18,8 +19,9 @@ describe('testing sumSigs', () => { const publicKeys = [keyPairOne.publicKey, keyPairTwo.publicKey] const msg = 'test message' - const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msg, publicKeys, publicNonces) - const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msg, publicKeys, publicNonces) + const msgHash = ethers.utils.solidityKeccak256(['string'],[msg]) + const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msgHash, publicKeys, publicNonces) + const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msgHash, publicKeys, publicNonces) const signatures = [signatureOne.signature, signatureTwo.signature] const signature = Schnorrkel.sumSigs(signatures) diff --git a/tests/schnorrkel/verify.test.ts b/tests/schnorrkel/verify.test.ts index 0cef04f..a73275f 100644 --- a/tests/schnorrkel/verify.test.ts +++ b/tests/schnorrkel/verify.test.ts @@ -9,7 +9,8 @@ describe('testing verify', () => { const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) const msg = 'test message' - const signature = Schnorrkel.sign(privateKey, msg) + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) + const signature = Schnorrkel.sign(privateKey, msgHash) const publicKey = ethers.utils.arrayify( ethers.utils.computePublicKey(ethers.utils.computePublicKey(privateKey.buffer, false), true) @@ -19,12 +20,13 @@ describe('testing verify', () => { expect(signature.finalPublicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) - const result = Schnorrkel.verify(signature.signature, msg, signature.finalPublicNonce, new Key(Buffer.from(publicKey))) + const result = Schnorrkel.verify(signature.signature, ethers.utils.solidityKeccak256(['string'], [msg]), signature.finalPublicNonce, new Key(Buffer.from(publicKey))) expect(result).toEqual(true) const secondMsg = 'this is another msg' - const secondSig = Schnorrkel.sign(privateKey, secondMsg) - const secondRes = Schnorrkel.verify(secondSig.signature, secondMsg, secondSig.finalPublicNonce, new Key(Buffer.from(publicKey))) + const secondMsgHash = ethers.utils.solidityKeccak256(['string'], [secondMsg]) + const secondSig = Schnorrkel.sign(privateKey, secondMsgHash) + const secondRes = Schnorrkel.verify(secondSig.signature, ethers.utils.solidityKeccak256(['string'], [secondMsg]), secondSig.finalPublicNonce, new Key(Buffer.from(publicKey))) expect(secondRes).toEqual(true) }) it('should sum signatures and verify them', () => { @@ -42,12 +44,13 @@ describe('testing verify', () => { const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) const msg = 'test message' - const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msg, publicKeys, publicNonces) - const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msg, publicKeys, publicNonces) + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) + const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msgHash, publicKeys, publicNonces) + const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msgHash, publicKeys, publicNonces) const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, msg, signatureTwo.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, ethers.utils.solidityKeccak256(['string'], [msg]), signatureTwo.finalPublicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -67,19 +70,20 @@ describe('testing verify', () => { const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) const msg = 'test message' + const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) const pkOneCache = new Key(Buffer.from(keyPairOne.privateKey.buffer)) expect(pkOneCache.buffer).toEqual(keyPairOne.privateKey.buffer) - const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msg, publicKeys, publicNonces) + const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msgHash, publicKeys, publicNonces) expect(pkOneCache.buffer).toEqual(keyPairOne.privateKey.buffer) const pkTwoCache = new Key(Buffer.from(keyPairTwo.privateKey.buffer)) expect(pkTwoCache.buffer).toEqual(keyPairTwo.privateKey.buffer) - const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msg, publicKeys, publicNonces) + const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msgHash, publicKeys, publicNonces) expect(pkTwoCache.buffer).toEqual(keyPairTwo.privateKey.buffer) const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, msg, signatureTwo.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, msgHash, signatureTwo.finalPublicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -88,8 +92,8 @@ describe('testing verify', () => { const abiCoder = new ethers.utils.AbiCoder() const msg = abiCoder.encode(['string'], ['test message']) - const hashFn = ethers.utils.keccak256 - const signature = Schnorrkel.sign(privateKey, msg, hashFn) + const msgHash = ethers.utils.keccak256(msg) + const signature = Schnorrkel.sign(privateKey, msgHash) const publicKey = ethers.utils.arrayify( ethers.utils.computePublicKey(ethers.utils.computePublicKey(privateKey.buffer, false), true) @@ -101,10 +105,9 @@ describe('testing verify', () => { expect(signature.challenge.buffer).toHaveLength(32) const result = Schnorrkel.verify( signature.signature, - msg, + ethers.utils.keccak256(msg), signature.finalPublicNonce, - new Key(Buffer.from(publicKey)), - hashFn + new Key(Buffer.from(publicKey)) ) expect(result).toEqual(true) }) @@ -124,13 +127,13 @@ describe('testing verify', () => { const abiCoder = new ethers.utils.AbiCoder() const msg = abiCoder.encode(['string'], ['test message']) - const hashFn = ethers.utils.keccak256 - const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msg, publicKeys, publicNonces, hashFn) - const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msg, publicKeys, publicNonces, hashFn) + const msgHash = ethers.utils.keccak256(msg) + const signatureOne = schnorrkelOne.multiSigSign(keyPairOne.privateKey, msgHash, publicKeys, publicNonces) + const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, msgHash, publicKeys, publicNonces) const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, msg, signatureTwo.finalPublicNonce, combinedPublicKey, hashFn) + const result = Schnorrkel.verify(signaturesSummed, ethers.utils.keccak256(msg), signatureTwo.finalPublicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -139,7 +142,7 @@ describe('testing verify', () => { const msg = 'test message' const hash = ethers.utils.solidityKeccak256(['string'], [msg]) - const signature = Schnorrkel.signHash(privateKey, hash) + const signature = Schnorrkel.sign(privateKey, hash) const publicKey = ethers.utils.arrayify( ethers.utils.computePublicKey(ethers.utils.computePublicKey(privateKey.buffer, false), true) @@ -149,7 +152,7 @@ describe('testing verify', () => { expect(signature.finalPublicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) - const result = Schnorrkel.verifyHash(signature.signature, hash, signature.finalPublicNonce, new Key(Buffer.from(publicKey))) + const result = Schnorrkel.verify(signature.signature, hash, signature.finalPublicNonce, new Key(Buffer.from(publicKey))) expect(result).toEqual(true) }) @@ -167,13 +170,13 @@ describe('testing verify', () => { const msg = 'test message' const hash = ethers.utils.solidityKeccak256(['string'], [msg]) - const signature = schnorrkelOne.multiSigSignHash(keyPairOne.privateKey, hash, publicKeys, publicNonces) - const signatureTwo = schnorrkelTwo.multiSigSignHash(keyPairTwo.privateKey, hash, publicKeys, publicNonces) + const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, hash, publicKeys, publicNonces) + const signatureTwo = schnorrkelTwo.multiSigSign(keyPairTwo.privateKey, hash, publicKeys, publicNonces) const signatures = [signature.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const result = Schnorrkel.verifyHash(signaturesSummed, hash, signature.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, hash, signature.finalPublicNonce, combinedPublicKey) expect(result).toEqual(true) }) }) \ No newline at end of file From d272260a38adc8a172afc3c36f4453ddfab97b0d Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Mon, 18 Sep 2023 15:00:42 +0300 Subject: [PATCH 2/7] add: maybe tests a bit more correct --- tests/schnorrkel/multiSigSign.test.ts | 8 +++++--- tests/schnorrkel/onchainMultiSign.test.ts | 6 ++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/schnorrkel/multiSigSign.test.ts b/tests/schnorrkel/multiSigSign.test.ts index 0a84848..4501383 100644 --- a/tests/schnorrkel/multiSigSign.test.ts +++ b/tests/schnorrkel/multiSigSign.test.ts @@ -19,7 +19,7 @@ describe('testing multiSigSign', () => { const publicKeys = [keyPairOne.publicKey, keyPairTwo.publicKey] const msg = 'test message' - const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, ethers.utils.solidityKeccak256(['string'], [msg]), publicKeys, publicNonces) + const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, ethers.utils.hashMessage(msg), publicKeys, publicNonces) expect(signature).toBeDefined() expect(signature.finalPublicNonce.buffer).toHaveLength(33) @@ -33,9 +33,10 @@ describe('testing multiSigSign', () => { const publicNonces = schnorrkel.generatePublicNonces(keyPair.privateKey) const msg = 'test message' + const msgHash = ethers.utils.hashMessage(msg) const publicKeys = [keyPair.publicKey] - expect(() => schnorrkel.multiSigSign(keyPair.privateKey, msg, publicKeys, [publicNonces])).toThrowError('At least 2 public keys should be provided') + expect(() => schnorrkel.multiSigSign(keyPair.privateKey, msgHash, publicKeys, [publicNonces])).toThrowError('At least 2 public keys should be provided') }) it('should requires nonces', () => { @@ -44,9 +45,10 @@ describe('testing multiSigSign', () => { const keyPairTwo = generateRandomKeys() const msg = 'test message' + const msgHash = ethers.utils.hashMessage(msg) const publicKeys = [keyPairOne.publicKey, keyPairTwo.publicKey] - expect(() => schnorrkel.multiSigSign(keyPairOne.privateKey, msg, publicKeys, [])).toThrowError('Nonces should be exchanged before signing') + expect(() => schnorrkel.multiSigSign(keyPairOne.privateKey, msgHash, publicKeys, [])).toThrowError('Nonces should be exchanged before signing') }) it('should generate multi signature by hash', () => { diff --git a/tests/schnorrkel/onchainMultiSign.test.ts b/tests/schnorrkel/onchainMultiSign.test.ts index c56a2b7..564dc70 100644 --- a/tests/schnorrkel/onchainMultiSign.test.ts +++ b/tests/schnorrkel/onchainMultiSign.test.ts @@ -44,8 +44,6 @@ describe('Multi Sign Tests', function () { // the multisig px and parity const px = ethers.utils.hexlify(combinedPublicKey.buffer.slice(1, 33)) - const combinedPublicAddress = '0x' + px.slice(px.length - 40, px.length) - const parity = combinedPublicKey.buffer[0] - 2 + 27 // wrap the result @@ -251,11 +249,11 @@ describe('Multi Sign Tests', function () { expect(e.message).to.equal('At least 2 public keys should be provided') } - const msg = 'just a test message' + const msgHash = ethers.utils.hashMessage('just a test message') const publicKeys = [signerOne.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] try { - signerOne.multiSignMessage(msg, publicKeys, publicNonces) + signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) } catch (e: any) { expect(e.message).to.equal('At least 2 public keys should be provided') } From dfffd11c94e5fb01dadbc8104d7f1a39ba32ccd0 Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Mon, 18 Sep 2023 15:22:32 +0300 Subject: [PATCH 3/7] make tests a bit more descriptive --- tests/schnorrkel/sign.test.ts | 6 +++--- tests/schnorrkel/verify.test.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/schnorrkel/sign.test.ts b/tests/schnorrkel/sign.test.ts index db8181e..fa869d1 100644 --- a/tests/schnorrkel/sign.test.ts +++ b/tests/schnorrkel/sign.test.ts @@ -5,7 +5,7 @@ import { generateRandomKeys } from '../../src/core' import { ethers } from 'ethers' describe('testing sign', () => { - it('should sign a message', () => { + it('should sign a message with solidityKeccak256', () => { const keyPair = generateRandomKeys() const msg = 'test message' @@ -18,11 +18,11 @@ describe('testing sign', () => { expect(signature.challenge.buffer).toHaveLength(32) }) - it('should sign a hash', () => { + it('should sign a message with keccak256', () => { const keyPair = generateRandomKeys() const msg = 'test message' - const hash = ethers.utils.solidityKeccak256(['string'], [msg]) + const hash = ethers.utils.hashMessage(msg) const signature = Schnorrkel.sign(keyPair.privateKey, hash) expect(signature).toBeDefined() diff --git a/tests/schnorrkel/verify.test.ts b/tests/schnorrkel/verify.test.ts index a73275f..f7ee8c9 100644 --- a/tests/schnorrkel/verify.test.ts +++ b/tests/schnorrkel/verify.test.ts @@ -87,12 +87,12 @@ describe('testing verify', () => { expect(result).toEqual(true) }) - it('should verify a schnorr signature with a custom hash function', () => { + it('should verify a schnorr signature with sha256 hash function', () => { const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) const abiCoder = new ethers.utils.AbiCoder() const msg = abiCoder.encode(['string'], ['test message']) - const msgHash = ethers.utils.keccak256(msg) + const msgHash = ethers.utils.sha256(ethers.utils.toUtf8Bytes(msg)) const signature = Schnorrkel.sign(privateKey, msgHash) const publicKey = ethers.utils.arrayify( @@ -105,7 +105,7 @@ describe('testing verify', () => { expect(signature.challenge.buffer).toHaveLength(32) const result = Schnorrkel.verify( signature.signature, - ethers.utils.keccak256(msg), + msgHash, signature.finalPublicNonce, new Key(Buffer.from(publicKey)) ) @@ -137,7 +137,7 @@ describe('testing verify', () => { expect(result).toEqual(true) }) - it('should verify a signature hash', () => { + it('should verify a signature for a msg hashed with solidityKeccak256', () => { const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) const msg = 'test message' @@ -156,7 +156,7 @@ describe('testing verify', () => { expect(result).toEqual(true) }) - it('should verify a multi signature hash', () => { + it('should verify a multi signature for a msg hashed with solidityKeccak256', () => { const schnorrkelOne = new Schnorrkel() const schnorrkelTwo = new Schnorrkel() From d6289f210cef8156620a16a4b257ae6a5103bb5b Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Mon, 18 Sep 2023 18:15:48 +0300 Subject: [PATCH 4/7] use the same elliptic curve library for signature and verification --- README.md | 38 ++++++++++++++-------- package-lock.json | 19 ----------- package.json | 1 - src/core/index.ts | 24 +++++--------- tests/schnorrkel/onchainSingleSign.test.ts | 6 ++-- 5 files changed, 37 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 182bef1..9e18aad 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Blockchain validation via ecrecover is also supported. # Typescript support Since version 2.0.0, we're moving entirely to Typescript. -## Breaking changes +## Version 2.0 Breaking changes * `sign()` and `multiSigSign()` return an instance of `SignatureOutput`. Each element in it has a buffer property * instead of `e` we return `challenge` for the Schnorr Challenge. To accces its value, use `challenge.buffer` * instead of `s` we return `signature` for the Schnorr Signature. To accces its value, use `signature.buffer` @@ -19,6 +19,13 @@ Since version 2.0.0, we're moving entirely to Typescript. * `getCombinedPublicKey` * `getCombinedAddress` +## Version 3.0 Breaking changes +* `sign()` is the former `signHash()`. A sign function that accepts a plain-text message as an argument no longer exists. +* `multiSigSign()` is the former `multiSigSignHash()`. A sign function that accepts a plain-text message as an argument no longer exists. +* `verify()` is the former `verifyHash()`. A verification function that accepts a plain-text message as an argument no longer exists. + +In version 2, we had plenty of ways to sign a message. This broad a lot of confusion as to what function was the correct one to use in various situations. This lead us to believe that making things simpler and forcing a hash to be passed to the methods is the way forward. + ## Requirements: * Node: >=16.0.0, <20.0.0 @@ -32,7 +39,7 @@ cd schnorrkel.js npm i ``` -### Testing +## Testing ``` npm run test ``` @@ -44,18 +51,20 @@ We refer to Single Signatures as ones that have a single signer. Sign: ```js -import Schnorrkel from 'schnorrkel' +import Schnorrkel from '@borislav.itskov/schnorrkel.js' -const privateKey = randomBytes(32) // Buffer +const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) const msg = 'test message' -const {signature, finalPublicNonce} = Schnorrkel.sign(privateKey, msg) +const hash = ethers.utils.hashMessage(msg) +const {signature, finalPublicNonce, challenge} = Schnorrkel.sign(privateKey, hash) ``` Offchain verification: +We take the `signature`, `hash` and `finalPublicNonce` from the example above and do: ```js -const publicKey: Buffer = ... (derived from the privateKey) -// signature and finalPublicNonce come from s -const result = Schnorrkel.verify(signature, msg, finalPublicNonce, publicKey) +const publicKey = Buffer.from(secp256k1.publicKeyCreate(privateKey.buffer)) +// signature and finalPublicNonce come from Schnorrkel.sign +const result = Schnorrkel.verify(signature, hash, finalPublicNonce, publicKey) ``` Onchain verification: @@ -91,14 +100,18 @@ Afterwards, here is part of the code: import { ethers } from 'ethers' import secp256k1 from 'secp256k1' -const address = 'input schnorr generated address here' +const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) +const publicKey = secp256k1.publicKeyCreate(ethers.utils.arrayify(privateKey)) +const px = publicKey.slice(1, 33) +const pxGeneratedAddress = ethers.utils.hexlify(px) +const schnorrAddr = '0x' + pxGeneratedAddress.slice(pxGeneratedAddress.length - 40, pxGeneratedAddress.length) const factory = new ethers.ContractFactory(SchnorrAccountAbstraction.abi, SchnorrAccountAbstraction.bytecode, wallet) -const contract: any = await factory.deploy([address]) +const contract: any = await factory.deploy([schnorrAddr]) -const privateKey: Buffer = '...' const pkBuffer = new Key(Buffer.from(ethers.utils.arrayify(privateKey))) const msg = 'just a test message'; -const sig = schnorrkel.sign(msg, privateKey); +const msgHash = ethers.utils.hashMessage(msg) +const sig = Schnorrkel.sign(pkBuffer, msgHash) // wrap the result const publicKey = secp256k1.publicKeyCreate(ethers.utils.arrayify(privateKey)) @@ -111,7 +124,6 @@ const sigData = abiCoder.encode([ "bytes32", "bytes32", "bytes32", "uint8" ], [ sig.signature.buffer, parity ]); -const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]); const result = await contract.isValidSignature(msgHash, sigData); ``` diff --git a/package-lock.json b/package-lock.json index 680f613..1647fdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ }, "devDependencies": { "@types/ecurve": "^1.0.0", - "@types/elliptic": "^6.4.14", "@types/secp256k1": "^4.0.3", "@vitest/coverage-c8": "^0.29.2", "hardhat": "^2.14.0", @@ -2135,15 +2134,6 @@ "@types/node": "*" } }, - "node_modules/@types/elliptic": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz", - "integrity": "sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==", - "dev": true, - "dependencies": { - "@types/bn.js": "*" - } - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -7519,15 +7509,6 @@ "@types/node": "*" } }, - "@types/elliptic": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz", - "integrity": "sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==", - "dev": true, - "requires": { - "@types/bn.js": "*" - } - }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", diff --git a/package.json b/package.json index b17f68e..e46577c 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "devDependencies": { "solc": "0.8.19", "@types/ecurve": "^1.0.0", - "@types/elliptic": "^6.4.14", "@types/secp256k1": "^4.0.3", "@vitest/coverage-c8": "^0.29.2", "typescript": "^4.9.5", diff --git a/src/core/index.ts b/src/core/index.ts index 4346349..0a0eae4 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,18 +1,12 @@ import { ethers } from 'ethers' import secp256k1 from 'secp256k1' -import ecurve from 'ecurve' -import elliptic from 'elliptic' +import ecurve, { Point } from 'ecurve' import bigi from 'bigi' -import { BN } from 'bn.js' - import { InternalNoncePairs, InternalNonces, InternalPublicNonces, InternalSignature } from './types' import { KeyPair } from '../types' const curve = ecurve.getCurveByName('secp256k1') -const n = curve?.n -const EC = elliptic.ec -const ec = new EC('secp256k1') -const generatorPoint = ec.g +const n = curve.n const _generateNonce = (): InternalNoncePairs => { const k = Buffer.from(ethers.utils.randomBytes(32)) @@ -193,13 +187,13 @@ export const _sumSigs = (signatures: Buffer[]): Buffer => { export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { const eC = challenge(R, hash, publicKey) - const sG = generatorPoint.mul(ethers.utils.arrayify(s)) - const P = ec.keyFromPublic(publicKey).getPublic() - const bnEC = new BN(Buffer.from(eC).toString('hex'), 'hex') - const Pe = P.mul(bnEC) - const toPublicR = ec.keyFromPublic(R).getPublic() - const RplusPe = toPublicR.add(Pe) - return sG.eq(RplusPe) + + const sG = curve.G.multiply(bigi.fromBuffer(s)) + const PasPoint = Point.decodeFrom(curve, publicKey) + const Pe = PasPoint.multiply(bigi.fromBuffer(eC)) + const RasPoint = Point.decodeFrom(curve, R) + const RplusPetest = RasPoint.add(Pe) + return sG.equals(RplusPetest) } export const _generatePk = (combinedPublicKey: Buffer): string => { diff --git a/tests/schnorrkel/onchainSingleSign.test.ts b/tests/schnorrkel/onchainSingleSign.test.ts index e69791d..fa50808 100644 --- a/tests/schnorrkel/onchainSingleSign.test.ts +++ b/tests/schnorrkel/onchainSingleSign.test.ts @@ -31,9 +31,9 @@ describe('Single Sign Tests', function () { // sign const msg = 'just a test message' - const msgHash = ethers.utils.solidityKeccak256(['string'], [msg]) - const pkBuffer = new Key(Buffer.from(ethers.utils.arrayify(pk1))) - const sig = Schnorrkel.sign(pkBuffer, msgHash) + const msgHash = ethers.utils.hashMessage(msg) + const privateKey = new Key(Buffer.from(ethers.utils.arrayify(pk1))) + const sig = Schnorrkel.sign(privateKey, msgHash) // wrap the result const publicKey = secp256k1.publicKeyCreate(ethers.utils.arrayify(pk1)) From c8612162bc7f0a6ae1ed1aee51a1a1158f5d7f14 Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Tue, 19 Sep 2023 10:51:20 +0300 Subject: [PATCH 5/7] make _sumSigs use the secp256k1 library --- package-lock.json | 14 ++++++++++---- src/core/index.ts | 14 +++++++------- tests/schnorrkel/onchainMultiSign.test.ts | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1647fdc..bd11fdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ }, "devDependencies": { "@types/ecurve": "^1.0.0", - "@types/secp256k1": "^4.0.3", "@vitest/coverage-c8": "^0.29.2", "hardhat": "^2.14.0", + "secp256k1": "^5.0.0", "solc": "0.8.19", "typescript": "^4.9.5", "vitest": "^0.29.2" @@ -4682,12 +4682,14 @@ "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true }, "node_modules/node-gyp-build": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "dev": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -5168,6 +5170,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "dev": true, "hasInstallScript": true, "dependencies": { "elliptic": "^6.5.4", @@ -9464,12 +9467,14 @@ "node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true }, "node-gyp-build": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -9794,6 +9799,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "dev": true, "requires": { "elliptic": "^6.5.4", "node-addon-api": "^5.0.0", diff --git a/src/core/index.ts b/src/core/index.ts index 0a0eae4..8cd0667 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -167,7 +167,6 @@ export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, // k + kTwoMulB + xea const final = secp256k1.privateKeyTweakAdd(kPlusxea, kTwoMulB) - return { // s = k + xea mod(n) signature: bigi.fromBuffer(final).mod(n).toBuffer(32), @@ -177,12 +176,13 @@ export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, } export const _sumSigs = (signatures: Buffer[]): Buffer => { - let combined = bigi.fromBuffer(signatures[0]) - signatures.shift() - signatures.forEach(sig => { - combined = combined.add(bigi.fromBuffer(sig)) - }) - return combined.mod(n).toBuffer(32) + let combined = new Uint8Array() + + for (let i = 0; i < signatures.length - 1; i++) { + combined = secp256k1.privateKeyTweakAdd(signatures[i], signatures[i+1]) + } + + return Buffer.from(combined) } export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { diff --git a/tests/schnorrkel/onchainMultiSign.test.ts b/tests/schnorrkel/onchainMultiSign.test.ts index 564dc70..dace4b7 100644 --- a/tests/schnorrkel/onchainMultiSign.test.ts +++ b/tests/schnorrkel/onchainMultiSign.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import { ethers } from 'ethers' -import Schnorrkel, { Key } from '../../src/index' +import Schnorrkel from '../../src/index' import { compile } from '../../utils/compile.js' import { wallet2 } from '../config.js' import DefaultSigner from '../../utils/DefaultSigner' @@ -58,7 +58,7 @@ describe('Multi Sign Tests', function () { expect(result).to.equal(ERC1271_MAGICVALUE_BYTES32) }) - it('should generate a schnorr musig2 and validate it on the blockchain', async function () { + it('should generate the same sig to be sure caching does not affect validation', async function () { // deploy the contract const signerOne = new DefaultSigner(0) const signerTwo = new DefaultSigner(1) From d0bd26ff4b8c15aee53bef7a9b713f8eadad32b8 Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Tue, 19 Sep 2023 17:59:09 +0300 Subject: [PATCH 6/7] better documentation and omit not needed lines of code --- src/core/index.ts | 134 ++++++++++++++++------ src/schnorrkel.ts | 11 +- tests/schnorrkel/onchainMultiSign.test.ts | 1 - 3 files changed, 102 insertions(+), 44 deletions(-) diff --git a/src/core/index.ts b/src/core/index.ts index 8cd0667..7c7a6ff 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -6,9 +6,8 @@ import { InternalNoncePairs, InternalNonces, InternalPublicNonces, InternalSigna import { KeyPair } from '../types' const curve = ecurve.getCurveByName('secp256k1') -const n = curve.n -const _generateNonce = (): InternalNoncePairs => { +const generateNonce = (): InternalNoncePairs => { const k = Buffer.from(ethers.utils.randomBytes(32)) const kTwo = Buffer.from(ethers.utils.randomBytes(32)) const kPublic = Buffer.from(secp256k1.publicKeyCreate(k)) @@ -22,7 +21,7 @@ const _generateNonce = (): InternalNoncePairs => { } } -const _bCoefficient = (combinedPublicKey: Buffer, msgHash: string, publicNonces: InternalPublicNonces[]): Buffer => { +const bCoefficient = (combinedPublicKey: Buffer, msgHash: string, publicNonces: InternalPublicNonces[]): Buffer => { type KeyOf = keyof InternalPublicNonces const arrayColumn = (arr: Array, n: KeyOf) => arr.map(x => x[n]) const kPublicNonces = secp256k1.publicKeyCombine(arrayColumn(publicNonces, 'kPublic')) @@ -37,9 +36,9 @@ const _bCoefficient = (combinedPublicKey: Buffer, msgHash: string, publicNonces: const areBuffersSame = (buf1: Buffer, buf2: Buffer): boolean => { if (buf1.byteLength != buf2.byteLength) return false - var dv1 = Buffer.from(buf1) - var dv2 = Buffer.from(buf2) - for (var i = 0; i != buf1.byteLength; i++) { + const dv1 = Buffer.from(buf1) + const dv2 = Buffer.from(buf2) + for (let i = 0; i != buf1.byteLength; i++) { if (dv1[i] != dv2[i]) return false } @@ -48,8 +47,8 @@ const areBuffersSame = (buf1: Buffer, buf2: Buffer): boolean => { const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => { // convert R to address - var R_uncomp = secp256k1.publicKeyConvert(R, false) - var R_addr = ethers.utils.arrayify(ethers.utils.keccak256(R_uncomp.slice(1, 65))).slice(12, 32) + const R_uncomp = secp256k1.publicKeyConvert(R, false) + const R_addr = ethers.utils.arrayify(ethers.utils.keccak256(R_uncomp.slice(1, 65))).slice(12, 32) // e = keccak256(address(R) || compressed publicKey || msgHash) return Buffer.from(ethers.utils.arrayify( @@ -60,6 +59,22 @@ const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => { )) } +export const generateRandomKeys = () => { + let privKeyBytes: Buffer + do { + privKeyBytes = Buffer.from(ethers.utils.randomBytes(32)) + } while (!secp256k1.privateKeyVerify(privKeyBytes)) + + const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKeyBytes)) + + const data = { + publicKey: pubKey, + privateKey: privKeyBytes, + } + + return new KeyPair(data) +} + export const _generateL = (publicKeys: Array) => { return ethers.utils.keccak256(_concatTypedArrays(publicKeys.sort(Buffer.compare))) } @@ -78,33 +93,31 @@ export const _aCoefficient = (publicKey: Buffer, L: string): Buffer => { ))) } -export const generateRandomKeys = () => { - let privKeyBytes: Buffer - do { - privKeyBytes = Buffer.from(ethers.utils.randomBytes(32)) - } while (!secp256k1.privateKeyVerify(privKeyBytes)) - - const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKeyBytes)) - - const data = { - publicKey: pubKey, - privateKey: privKeyBytes, - } - - return new KeyPair(data) -} - +/** + * Hash the privateKey so it is not in plain text in arrays. + * A separate method for easy reuse + * + * @param privateKey + * @returns string + */ export const _hashPrivateKey = (privateKey: Buffer): string => { return ethers.utils.keccak256(privateKey) } -export const _generatePublicNonces = (privateKey: Buffer): { +/** + * Generate the nonces for the next signature. + * Use the hash of the private key for a unique identifier + * TODO: maybe change this with to the public key... + * @param privateKey + * @returns + */ +export const _generateNonces = (privateKey: Buffer): { privateNonceData: Pick, publicNonceData: InternalPublicNonces, hash: string, } => { const hash = _hashPrivateKey(privateKey) - const nonce = _generateNonce() + const nonce = generateNonce() return { hash, @@ -133,7 +146,7 @@ export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk)) const L = _generateL(publicKeys) const a = _aCoefficient(publicKey, L) - const b = _bCoefficient(combinedPublicKey, hash, publicNonces) + const b = bCoefficient(combinedPublicKey, hash, publicNonces) const effectiveNonces = publicNonces.map((batch) => { return Buffer.from(secp256k1.publicKeyCombine([batch.kPublic, secp256k1.publicKeyTweakMul(batch.kTwoPublic, b)])) @@ -164,17 +177,23 @@ export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, // kTwo * b const kTwoMulB = secp256k1.privateKeyTweakMul(kTwo, b) - // k + kTwoMulB + xea - const final = secp256k1.privateKeyTweakAdd(kPlusxea, kTwoMulB) + // k + kTwoMulB + xea mod(n) + const final = Buffer.from(secp256k1.privateKeyTweakAdd(kPlusxea, kTwoMulB)) return { - // s = k + xea mod(n) - signature: bigi.fromBuffer(final).mod(n).toBuffer(32), + signature: final, challenge: e, finalPublicNonce: R } } +/** + * Sum the passed signatures. + * mod(n) is automatically applied in the privateKeyTweakAdd function + * + * @param signatures + * @returns Buffer summed signature + */ export const _sumSigs = (signatures: Buffer[]): Buffer => { let combined = new Uint8Array() @@ -185,6 +204,20 @@ export const _sumSigs = (signatures: Buffer[]): Buffer => { return Buffer.from(combined) } +/** + * The verification formula is: s*G = R + H(m)*X + * s is the signature + * G is the generation point of the elliptic curve + * R is the public nonce, or the ephemeral public nonce + * H(m) is the hash of the message + * X is the public key + * + * @param s the signature + * @param hash the signed hash + * @param R the public nonce used for this signature + * @param publicKey the public key used for this signature + * @returns bool + */ export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => { const eC = challenge(R, hash, publicKey) @@ -196,32 +229,57 @@ export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): return sG.equals(RplusPetest) } -export const _generatePk = (combinedPublicKey: Buffer): string => { +/** + * Take the x-coordinate of the public key and transform it + * into ethereum-like address + * + * @param combinedPublicKey + * @returns address + */ +export const _generateSchnorrAddr = (combinedPublicKey: Buffer): string => { + if (combinedPublicKey.length != 33) { + throw Error('Public key should be 33 length, 1 byte parity and 32 bytes x-coordinate') + } + const px = ethers.utils.hexlify(combinedPublicKey.subarray(1,33)) return '0x' + px.slice(px.length - 40, px.length) } export const _sign = (privateKey: Buffer, hash: string): InternalSignature => { + // if we use secp256k1 directly on the private key for operations + // different than publicKeyCreate (privateKeyTweakMul, for example), + // the private key gets modified. We do not want that and hence + // do operations with a local copy const localPk = Buffer.from(privateKey) const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk)) // R = G * k - var k = ethers.utils.randomBytes(32) - var R = Buffer.from(secp256k1.publicKeyCreate(k)) + const k = ethers.utils.randomBytes(32) + const R = Buffer.from(secp256k1.publicKeyCreate(k)) // e = h(address(R) || compressed pubkey || m) - var e = challenge(R, hash, publicKey) + const e = challenge(R, hash, publicKey) // xe = x * e - var xe = secp256k1.privateKeyTweakMul(localPk, e) + const xe = secp256k1.privateKeyTweakMul(localPk, e) // s = k + xe mod(n) - var s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe)) - s = bigi.fromBuffer(s).mod(n).toBuffer(32) + const s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe)) return { finalPublicNonce: R, challenge: e, signature: s } +} + +/** + * Provide a default hash function + * It is not mandotory to use this one. + * + * @param message + * @returns string + */ +export const _hashMessage = (message: string): string => { + return ethers.utils.solidityKeccak256(['string'], [message]) } \ No newline at end of file diff --git a/src/schnorrkel.ts b/src/schnorrkel.ts index 013c300..5e69f06 100644 --- a/src/schnorrkel.ts +++ b/src/schnorrkel.ts @@ -2,7 +2,7 @@ import secp256k1 from 'secp256k1' import { Key, Nonces, PublicNonces, Signature, NoncePairs } from './types' -import { _generateL, _aCoefficient, _generatePublicNonces, _multiSigSign, _hashPrivateKey, _sumSigs, _verify, _generatePk, _sign } from './core' +import { _generateL, _aCoefficient, _generateNonces, _multiSigSign, _hashPrivateKey, _sumSigs, _verify, _generateSchnorrAddr, _sign } from './core' import { InternalNonces, InternalPublicNonces, InternalSignature } from './core/types' import { Challenge, FinalPublicNonce, SignatureOutput } from './types/signature' @@ -10,7 +10,7 @@ class Schnorrkel { protected nonces: Nonces = {} private _setNonce(privateKey: Buffer): string { - const { publicNonceData, privateNonceData, hash } = _generatePublicNonces(privateKey) + const { publicNonceData, privateNonceData, hash } = _generateNonces(privateKey) const mappedPublicNonce: PublicNonces = { kPublic: new Key(Buffer.from(publicNonceData.kPublic)), @@ -80,11 +80,12 @@ class Schnorrkel { } static getCombinedAddress(publicKeys: Array): string { - if (publicKeys.length < 2) throw Error('At least 2 public keys should be provided') + if (publicKeys.length < 2) { + throw Error('At least 2 public keys should be provided') + } const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const px = _generatePk(combinedPublicKey.buffer) - return px + return _generateSchnorrAddr(combinedPublicKey.buffer) } generatePublicNonces(privateKey: Key): PublicNonces { diff --git a/tests/schnorrkel/onchainMultiSign.test.ts b/tests/schnorrkel/onchainMultiSign.test.ts index dace4b7..e50448d 100644 --- a/tests/schnorrkel/onchainMultiSign.test.ts +++ b/tests/schnorrkel/onchainMultiSign.test.ts @@ -4,7 +4,6 @@ import Schnorrkel from '../../src/index' import { compile } from '../../utils/compile.js' import { wallet2 } from '../config.js' import DefaultSigner from '../../utils/DefaultSigner' -import { _generatePk } from '../../src/core' const ERC1271_MAGICVALUE_BYTES32 = '0x1626ba7e' describe('Multi Sign Tests', function () { From 4c115bc174ca3faa28d867dee15cdc13ed1225f9 Mon Sep 17 00:00:00 2001 From: Borislav Itskov Date: Mon, 25 Sep 2023 16:42:13 +0300 Subject: [PATCH 7/7] document the library, change variable name for easier understandind, add a helper method generateOrGetPublicNonces --- README.md | 15 +-- src/core/index.ts | 62 ++++++++-- src/core/types.ts | 2 +- src/index.ts | 2 +- src/schnorrkel.ts | 141 +++++++++++++++++++--- src/types/index.ts | 2 +- src/types/signature.ts | 16 +-- tests/schnorrkel/getPublicNonces.test.ts | 6 + tests/schnorrkel/multiSigSign.test.ts | 4 +- tests/schnorrkel/onchainMultiSign.test.ts | 6 +- tests/schnorrkel/sign.test.ts | 4 +- tests/schnorrkel/verify.test.ts | 22 ++-- 12 files changed, 224 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 9e18aad..47fefb2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Since version 2.0.0, we're moving entirely to Typescript. * `sign()` and `multiSigSign()` return an instance of `SignatureOutput`. Each element in it has a buffer property * instead of `e` we return `challenge` for the Schnorr Challenge. To accces its value, use `challenge.buffer` * instead of `s` we return `signature` for the Schnorr Signature. To accces its value, use `signature.buffer` - * instead of `R` we return `finalPublicNonce` for the nonce. To accces its value, use `finalPublicNonce.buffer` + * instead of `R` we return `publicNonce` for the nonce. To accces its value, use `publicNonce.buffer` * `getCombinedPublicKey()` returns a `Key` class. To get the actual key, use `key.buffer` * a lot of method become static as they don't keep any state: * `verify` @@ -20,6 +20,7 @@ Since version 2.0.0, we're moving entirely to Typescript. * `getCombinedAddress` ## Version 3.0 Breaking changes +* `finalPublicNonce`, `FinalPublicNonce` is replaced everywhere with `publicNonce`, `PublicNonce`. The old name just didn't make sense. * `sign()` is the former `signHash()`. A sign function that accepts a plain-text message as an argument no longer exists. * `multiSigSign()` is the former `multiSigSignHash()`. A sign function that accepts a plain-text message as an argument no longer exists. * `verify()` is the former `verifyHash()`. A verification function that accepts a plain-text message as an argument no longer exists. @@ -56,15 +57,15 @@ import Schnorrkel from '@borislav.itskov/schnorrkel.js' const privateKey = new Key(Buffer.from(ethers.utils.randomBytes(32))) const msg = 'test message' const hash = ethers.utils.hashMessage(msg) -const {signature, finalPublicNonce, challenge} = Schnorrkel.sign(privateKey, hash) +const {signature, publicNonce, challenge} = Schnorrkel.sign(privateKey, hash) ``` Offchain verification: -We take the `signature`, `hash` and `finalPublicNonce` from the example above and do: +We take the `signature`, `hash` and `publicNonce` from the example above and do: ```js const publicKey = Buffer.from(secp256k1.publicKeyCreate(privateKey.buffer)) -// signature and finalPublicNonce come from Schnorrkel.sign -const result = Schnorrkel.verify(signature, hash, finalPublicNonce, publicKey) +// signature and publicNonce come from Schnorrkel.sign +const result = Schnorrkel.verify(signature, hash, publicNonce, publicKey) ``` Onchain verification: @@ -156,7 +157,7 @@ const publicKey1: Buffer = '...' const publicKey2: Buffer = '...' const publicKeys = [publicKey1, publicKey2]; const combinedPublicKey = schnorrkel.getCombinedPublicKey(publicKeys) -const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) +const {signature: sigOne, challenge: e, publicNonce} = signerOne.multiSignMessage(msg, publicKeys, publicNonces) const {signature: sigTwo} = signerTwo.multiSignMessage(msg, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) ``` @@ -180,7 +181,7 @@ const result = await contract.isValidSignature(msgHash, sigData); #### verify offchain ```js -const result = schnorrkel.verify(sSummed, msg, finalPublicNonce, combinedPublicKey); +const result = schnorrkel.verify(sSummed, msg, publicNonce, combinedPublicKey); ``` You can find reference to this in `tests/schnorrkel/onchainMultiSign.test.ts` in this repository. diff --git a/src/core/index.ts b/src/core/index.ts index 7c7a6ff..61e042f 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -7,6 +7,12 @@ import { KeyPair } from '../types' const curve = ecurve.getCurveByName('secp256k1') +/** + * Generate two random nonces in preparation for a multisignature. + * We return along with them their public representations + * + * @returns InternalNoncePairs + */ const generateNonce = (): InternalNoncePairs => { const k = Buffer.from(ethers.utils.randomBytes(32)) const kTwo = Buffer.from(ethers.utils.randomBytes(32)) @@ -21,6 +27,17 @@ const generateNonce = (): InternalNoncePairs => { } } +/** + * Compute the b coefficient needed for multisignature signing. + * The b coefficient is needed to prevent the DL query attack + * on the public nonces, in hand allowing us to skip the nonce + * commitment round + * + * @param combinedPublicKey - the sum of the keys of the participants + * @param msgHash - the hash that's going to be signed + * @param publicNonces - the exchanged public nonces + * @returns Buffer + */ const bCoefficient = (combinedPublicKey: Buffer, msgHash: string, publicNonces: InternalPublicNonces[]): Buffer => { type KeyOf = keyof InternalPublicNonces const arrayColumn = (arr: Array, n: KeyOf) => arr.map(x => x[n]) @@ -45,6 +62,15 @@ const areBuffersSame = (buf1: Buffer, buf2: Buffer): boolean => { return true } +/** + * Compute the schnorr challenge. + * The formula is: s = k + e*d. We're computing `e` here + * + * @param R + * @param msgHash + * @param publicKey + * @returns Buffer hash(concat(public_nonce_addr, parity, x_coord, message)) + */ const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => { // convert R to address const R_uncomp = secp256k1.publicKeyConvert(R, false) @@ -59,7 +85,12 @@ const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => { )) } -export const generateRandomKeys = () => { +/** + * A helper function that creates a key pair + * + * @returns KeyPair + */ +export const generateRandomKeys = (): KeyPair => { let privKeyBytes: Buffer do { privKeyBytes = Buffer.from(ethers.utils.randomBytes(32)) @@ -75,7 +106,15 @@ export const generateRandomKeys = () => { return new KeyPair(data) } -export const _generateL = (publicKeys: Array) => { +/** + * Generate a hash of all the public keys that are participating + * in the signing process. We need this to craft the `a` coefficient, + * which helps us prevent key cancelation attacks. + * + * @param publicKeys + * @returns string + */ +export const _generateL = (publicKeys: Array): string => { return ethers.utils.keccak256(_concatTypedArrays(publicKeys.sort(Buffer.compare))) } @@ -85,7 +124,15 @@ export const _concatTypedArrays = (publicKeys: Buffer[]): Buffer => { return Buffer.from(c.buffer) } - +/** + * Generate `a` coefficient to prevent key cancelation attacks. + * Hash commitment to all the public keys to prevent your key + * not participating in the multisignature. + * + * @param publicKey - the signer's public key + * @param L - review _generateL + * @returns Buffer hash(concat(L, own_public_key)) + */ export const _aCoefficient = (publicKey: Buffer, L: string): Buffer => { return Buffer.from(ethers.utils.arrayify(ethers.utils.solidityKeccak256( ['bytes', 'bytes'], @@ -107,7 +154,7 @@ export const _hashPrivateKey = (privateKey: Buffer): string => { /** * Generate the nonces for the next signature. * Use the hash of the private key for a unique identifier - * TODO: maybe change this with to the public key... + * * @param privateKey * @returns */ @@ -183,7 +230,7 @@ export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, return { signature: final, challenge: e, - finalPublicNonce: R + publicNonce: R } } @@ -231,7 +278,8 @@ export const _verify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): /** * Take the x-coordinate of the public key and transform it - * into ethereum-like address + * into ethereum-like address. + * This is the address returned by ecrecover on-chain schnorr verification * * @param combinedPublicKey * @returns address @@ -267,7 +315,7 @@ export const _sign = (privateKey: Buffer, hash: string): InternalSignature => { const s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe)) return { - finalPublicNonce: R, + publicNonce: R, challenge: e, signature: s } diff --git a/src/core/types.ts b/src/core/types.ts index e00a94a..75a7bd1 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -11,7 +11,7 @@ export interface InternalPublicNonces { } export interface InternalSignature { - finalPublicNonce: Buffer, // the final public nonce + publicNonce: Buffer, // the final public nonce challenge: Buffer, // the schnorr challenge signature: Buffer, // the signature } diff --git a/src/index.ts b/src/index.ts index 3325240..bf4d2ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import Schnorrkel from './schnorrkel' export { default as UnsafeSchnorrkel } from './unsafe-schnorrkel' -export { Key, KeyPair, Signature, PublicNonces, Challenge, SignatureOutput, FinalPublicNonce } from './types' +export { Key, KeyPair, Signature, PublicNonces, Challenge, SignatureOutput, PublicNonce } from './types' export default Schnorrkel \ No newline at end of file diff --git a/src/schnorrkel.ts b/src/schnorrkel.ts index 5e69f06..e01750d 100644 --- a/src/schnorrkel.ts +++ b/src/schnorrkel.ts @@ -4,12 +4,21 @@ import { Key, Nonces, PublicNonces, Signature, NoncePairs } from './types' import { _generateL, _aCoefficient, _generateNonces, _multiSigSign, _hashPrivateKey, _sumSigs, _verify, _generateSchnorrAddr, _sign } from './core' import { InternalNonces, InternalPublicNonces, InternalSignature } from './core/types' -import { Challenge, FinalPublicNonce, SignatureOutput } from './types/signature' +import { Challenge, PublicNonce, SignatureOutput } from './types/signature' class Schnorrkel { protected nonces: Nonces = {} - private _setNonce(privateKey: Buffer): string { + /** + * Set the nonces for the next multisignature. + * Nonces should not be manipulated outside the library. Also, + * they should be completely random. + * + * @param privateKey - we use the private key to create + * an unique identifier hash. See _generateNonces + * @returns string identifier + */ + private setNonce(privateKey: Buffer): string { const { publicNonceData, privateNonceData, hash } = _generateNonces(privateKey) const mappedPublicNonce: PublicNonces = { @@ -26,10 +35,26 @@ class Schnorrkel { return hash } + /** + * Clear the nonces used in the last signature + * This is a very important step as otherwise, we go into nonce + * reuse scenario + * + * @param privateKey + */ private clearNonces(privateKey: Key): void { const x = privateKey.buffer const hash = _hashPrivateKey(x) + // this shouldn't happen, just extra safety + // clearNonces should be called after a signature has been crafted. + // If the hash is not found in the nonces by any chance after + // a signature, then the process should be stopped as we don't + // know nonces have been used for the signature + if (! this.nonces[hash]) { + throw new Error('Multisignature nonces not found') + } + delete this.nonces[hash] } @@ -56,14 +81,13 @@ class Schnorrkel { })) } - private getMultisigOutput(multiSig: InternalSignature): SignatureOutput { - return { - signature: new Signature(Buffer.from(multiSig.signature)), - finalPublicNonce: new FinalPublicNonce(Buffer.from(multiSig.finalPublicNonce)), - challenge: new Challenge(Buffer.from(multiSig.challenge)), - } - } - + /** + * Sum the public keys in a safe manner with a specific + * _aCoefficient for each publicKey + * + * @param publicKeys - the signers + * @returns Key summed public key + */ static getCombinedPublicKey(publicKeys: Array): Key { if (publicKeys.length < 2) { throw Error('At least 2 public keys should be provided') @@ -79,6 +103,12 @@ class Schnorrkel { return new Key(Buffer.from(secp256k1.publicKeyCombine(modifiedKeys))) } + /** + * The address returned by ecrecover on-chain schnorr verification + * for the given public keys + * @param publicKeys + * @returns string address + */ static getCombinedAddress(publicKeys: Array): string { if (publicKeys.length < 2) { throw Error('At least 2 public keys should be provided') @@ -88,8 +118,35 @@ class Schnorrkel { return _generateSchnorrAddr(combinedPublicKey.buffer) } + /** + * Generate nonces for the next signature if there aren't any. + * If there are, just return them. + * This is a method you should use if you don't want to manage + * the nonces yourself + * + * @param privateKey + * @returns PublicNonces + */ + generateOrGetPublicNonces(privateKey: Key): PublicNonces { + if (this.hasNonces(privateKey)) { + return this.getPublicNonces(privateKey) + } + + return this.generatePublicNonces(privateKey) + } + + /** + * Genetate the nonces and return the public ones for a multisignature. + * This method always generates new nonces. If you want to keep + * you state, you should check with hasNonces() whether they are set. + * You need to maintain the state for the nonce exchanging phase and + * the signing phase + * + * @param privateKey + * @returns PublicNonces + */ generatePublicNonces(privateKey: Key): PublicNonces { - const hash = this._setNonce(privateKey.buffer) + const hash = this.setNonce(privateKey.buffer) const nonce = this.nonces[hash] return { @@ -98,21 +155,48 @@ class Schnorrkel { } } + /** + * Get the public nonces. + * If none are set, an error is returned + * + * @param privateKey + * @returns PublicNonces + */ getPublicNonces(privateKey: Key): PublicNonces { const hash = _hashPrivateKey(privateKey.buffer) const nonce = this.nonces[hash] + if (!nonce) { + throw new Error('Nonces not set') + } + return { kPublic: nonce.kPublic, kTwoPublic: nonce.kTwoPublic, } } + /** + * Check if there are nonces generated in the state + * + * @param privateKey + * @returns Key + */ hasNonces(privateKey: Key): boolean { const hash = _hashPrivateKey(privateKey.buffer) return hash in this.nonces } + /** + * Compute a multisignature. + * The nonce exchange phase should have passed before this stage + * + * @param privateKey - the key you're signing with + * @param hash - the message of the multisignature + * @param publicKeys - the participants + * @param publicNonces - the public nonces of the participants + * @returns SignatureOutput + */ multiSigSign(privateKey: Key, hash: string, publicKeys: Key[], publicNonces: PublicNonces[]): SignatureOutput { const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) const mappedPublicNonce = this.getMappedPublicNonces(publicNonces) @@ -124,32 +208,59 @@ class Schnorrkel { // nonce reuse will lead to private key leakage! this.clearNonces(privateKey) - return this.getMultisigOutput(musigData) + return { + signature: new Signature(Buffer.from(musigData.signature)), + publicNonce: new PublicNonce(Buffer.from(musigData.publicNonce)), + challenge: new Challenge(Buffer.from(musigData.challenge)), + } } + /** + * Compute a single schnorr signature + * + * @param privateKey - the key you're signing with + * @param hash - the message you're signing + * @returns SignatureOutput + */ static sign(privateKey: Key, hash: string): SignatureOutput { const output = _sign(privateKey.buffer, hash) return { signature: new Signature(Buffer.from(output.signature)), - finalPublicNonce: new FinalPublicNonce(Buffer.from(output.finalPublicNonce)), + publicNonce: new PublicNonce(Buffer.from(output.publicNonce)), challenge: new Challenge(Buffer.from(output.challenge)), } } + /** + * Sum two signatures. + * Needed for a multisignature verification + * + * @param signatures + * @returns Signature + */ static sumSigs(signatures: Signature[]): Signature { const mappedSignatures = signatures.map(signature => signature.buffer) const sum = _sumSigs(mappedSignatures) return new Signature(Buffer.from(sum)) } + /** + * Off-chain signature verification + * + * @param signature - what we're verifying + * @param hash - the message that should have been signed + * @param publicNonce - the public version of the nonce used for the signature + * @param publicKey - the public key of the private key used for the signature + * @returns + */ static verify( signature: Signature, hash: string, - finalPublicNonce: FinalPublicNonce, + publicNonce: PublicNonce, publicKey: Key ): boolean { - return _verify(signature.buffer, hash, finalPublicNonce.buffer, publicKey.buffer) + return _verify(signature.buffer, hash, publicNonce.buffer, publicKey.buffer) } } diff --git a/src/types/index.ts b/src/types/index.ts index 0b86beb..1c30887 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ export { Key } from './key' export { KeyPair } from './key-pair' export { PublicNonces, Nonces, NoncePairs } from './nonce' -export { Signature, Challenge, SignatureOutput, FinalPublicNonce } from './signature' +export { Signature, Challenge, SignatureOutput, PublicNonce } from './signature' diff --git a/src/types/signature.ts b/src/types/signature.ts index 4d56c11..e966317 100644 --- a/src/types/signature.ts +++ b/src/types/signature.ts @@ -1,11 +1,11 @@ export interface SignatureOutput { - finalPublicNonce: FinalPublicNonce, // the final public nonce + publicNonce: PublicNonce, // the final public nonce challenge: Challenge, // the schnorr challenge signature: Signature, // the signature } -export class FinalPublicNonce { +export class PublicNonce { readonly buffer: Buffer constructor(buffer: Buffer) { @@ -16,8 +16,8 @@ export class FinalPublicNonce { return this.buffer.toString('hex') } - static fromHex(hex: string): FinalPublicNonce { - return new FinalPublicNonce(Buffer.from(hex, 'hex')) + static fromHex(hex: string): PublicNonce { + return new PublicNonce(Buffer.from(hex, 'hex')) } } @@ -32,8 +32,8 @@ export class Challenge { return this.buffer.toString('hex') } - static fromHex(hex: string): FinalPublicNonce { - return new FinalPublicNonce(Buffer.from(hex, 'hex')) + static fromHex(hex: string): Challenge { + return new Challenge(Buffer.from(hex, 'hex')) } } @@ -48,8 +48,8 @@ export class Signature { return this.buffer.toString('hex') } - static fromHex(hex: string): FinalPublicNonce { - return new FinalPublicNonce(Buffer.from(hex, 'hex')) + static fromHex(hex: string): Signature { + return new Signature(Buffer.from(hex, 'hex')) } } diff --git a/tests/schnorrkel/getPublicNonces.test.ts b/tests/schnorrkel/getPublicNonces.test.ts index 2774ee6..42b248d 100644 --- a/tests/schnorrkel/getPublicNonces.test.ts +++ b/tests/schnorrkel/getPublicNonces.test.ts @@ -21,4 +21,10 @@ describe('testing getPublicNonces', () => { expect(retrievedPublicNonces.kPublic.buffer).to.equal(publicNonces.kPublic.buffer) expect(retrievedPublicNonces.kTwoPublic.buffer).to.equal(publicNonces.kTwoPublic.buffer) }) + it('should throw an error when calling getPublicNonces if they are not set', () => { + const schnorrkel = new Schnorrkel() + + const keyPair = generateRandomKeys() + expect(() => schnorrkel.getPublicNonces(keyPair.privateKey)).toThrowError('Nonces not set') + }) }) \ No newline at end of file diff --git a/tests/schnorrkel/multiSigSign.test.ts b/tests/schnorrkel/multiSigSign.test.ts index 4501383..2fd0925 100644 --- a/tests/schnorrkel/multiSigSign.test.ts +++ b/tests/schnorrkel/multiSigSign.test.ts @@ -22,7 +22,7 @@ describe('testing multiSigSign', () => { const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, ethers.utils.hashMessage(msg), publicKeys, publicNonces) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) }) @@ -68,7 +68,7 @@ describe('testing multiSigSign', () => { const signature = schnorrkelOne.multiSigSign(keyPairOne.privateKey, hash, publicKeys, publicNonces) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) }) diff --git a/tests/schnorrkel/onchainMultiSign.test.ts b/tests/schnorrkel/onchainMultiSign.test.ts index e50448d..c9659f3 100644 --- a/tests/schnorrkel/onchainMultiSign.test.ts +++ b/tests/schnorrkel/onchainMultiSign.test.ts @@ -37,7 +37,7 @@ describe('Multi Sign Tests', function () { const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigOne, challenge: e, publicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) const {signature: sigTwo} = signerTwo.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) @@ -68,7 +68,7 @@ describe('Multi Sign Tests', function () { const publicKeys = [signerOne.getPublicKey(), signerTwo.getPublicKey()] const publicNonces = [signerOne.getPublicNonces(), signerTwo.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) + const {signature: sigOne, challenge: e, publicNonce} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) const {signature: sigTwo} = signerTwo.multiSignMessage(msgHash, publicKeys, publicNonces) const sSummed = Schnorrkel.sumSigs([sigOne, sigTwo]) @@ -102,7 +102,7 @@ describe('Multi Sign Tests', function () { const publicNonces = [signerOne.getPublicNonces(), signerThree.getPublicNonces()] const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) -// finalPublicNonce: FinalPublicNonce, // the final public nonce +// publicNonce: publicNonce, // the final public nonce // challenge: Challenge, // the schnorr challenge // signature: Signature, // the signature const {signature: sigOne, challenge: e} = signerOne.multiSignMessage(msgHash, publicKeys, publicNonces) diff --git a/tests/schnorrkel/sign.test.ts b/tests/schnorrkel/sign.test.ts index fa869d1..99cda3b 100644 --- a/tests/schnorrkel/sign.test.ts +++ b/tests/schnorrkel/sign.test.ts @@ -13,7 +13,7 @@ describe('testing sign', () => { const signature = Schnorrkel.sign(keyPair.privateKey, hash) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) }) @@ -26,7 +26,7 @@ describe('testing sign', () => { const signature = Schnorrkel.sign(keyPair.privateKey, hash) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) }) diff --git a/tests/schnorrkel/verify.test.ts b/tests/schnorrkel/verify.test.ts index f7ee8c9..1007cef 100644 --- a/tests/schnorrkel/verify.test.ts +++ b/tests/schnorrkel/verify.test.ts @@ -17,16 +17,16 @@ describe('testing verify', () => { ) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) - const result = Schnorrkel.verify(signature.signature, ethers.utils.solidityKeccak256(['string'], [msg]), signature.finalPublicNonce, new Key(Buffer.from(publicKey))) + const result = Schnorrkel.verify(signature.signature, ethers.utils.solidityKeccak256(['string'], [msg]), signature.publicNonce, new Key(Buffer.from(publicKey))) expect(result).toEqual(true) const secondMsg = 'this is another msg' const secondMsgHash = ethers.utils.solidityKeccak256(['string'], [secondMsg]) const secondSig = Schnorrkel.sign(privateKey, secondMsgHash) - const secondRes = Schnorrkel.verify(secondSig.signature, ethers.utils.solidityKeccak256(['string'], [secondMsg]), secondSig.finalPublicNonce, new Key(Buffer.from(publicKey))) + const secondRes = Schnorrkel.verify(secondSig.signature, ethers.utils.solidityKeccak256(['string'], [secondMsg]), secondSig.publicNonce, new Key(Buffer.from(publicKey))) expect(secondRes).toEqual(true) }) it('should sum signatures and verify them', () => { @@ -50,7 +50,7 @@ describe('testing verify', () => { const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, ethers.utils.solidityKeccak256(['string'], [msg]), signatureTwo.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, ethers.utils.solidityKeccak256(['string'], [msg]), signatureTwo.publicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -83,7 +83,7 @@ describe('testing verify', () => { const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, msgHash, signatureTwo.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, msgHash, signatureTwo.publicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -100,13 +100,13 @@ describe('testing verify', () => { ) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) const result = Schnorrkel.verify( signature.signature, msgHash, - signature.finalPublicNonce, + signature.publicNonce, new Key(Buffer.from(publicKey)) ) expect(result).toEqual(true) @@ -133,7 +133,7 @@ describe('testing verify', () => { const signatures = [signatureOne.signature, signatureTwo.signature] const signaturesSummed = Schnorrkel.sumSigs(signatures) - const result = Schnorrkel.verify(signaturesSummed, ethers.utils.keccak256(msg), signatureTwo.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, ethers.utils.keccak256(msg), signatureTwo.publicNonce, combinedPublicKey) expect(result).toEqual(true) }) @@ -149,10 +149,10 @@ describe('testing verify', () => { ) expect(signature).toBeDefined() - expect(signature.finalPublicNonce.buffer).toHaveLength(33) + expect(signature.publicNonce.buffer).toHaveLength(33) expect(signature.signature.buffer).toHaveLength(32) expect(signature.challenge.buffer).toHaveLength(32) - const result = Schnorrkel.verify(signature.signature, hash, signature.finalPublicNonce, new Key(Buffer.from(publicKey))) + const result = Schnorrkel.verify(signature.signature, hash, signature.publicNonce, new Key(Buffer.from(publicKey))) expect(result).toEqual(true) }) @@ -176,7 +176,7 @@ describe('testing verify', () => { const signaturesSummed = Schnorrkel.sumSigs(signatures) const combinedPublicKey = Schnorrkel.getCombinedPublicKey(publicKeys) - const result = Schnorrkel.verify(signaturesSummed, hash, signature.finalPublicNonce, combinedPublicKey) + const result = Schnorrkel.verify(signaturesSummed, hash, signature.publicNonce, combinedPublicKey) expect(result).toEqual(true) }) }) \ No newline at end of file