bip-schnorr
Version:
Pure JavaScript implementation of the BIP schnorr signature scheme and the muSig multi-signature scheme
113 lines (103 loc) • 7.33 kB
JavaScript
/* global describe, it, beforeEach */
const assert = require('assert');
const Buffer = require('safe-buffer').Buffer;
const BigInteger = require('bigi');
const schnorr = require('../src/schnorr');
const ecurve = require('ecurve');
const curve = ecurve.getCurveByName('secp256k1');
const n = curve.n;
const testVectors = require('./test-vectors-schnorr.json');
function assertError(error, expectedMessage) {
assert.strictEqual(error.message, expectedMessage);
}
describe('edge cases', () => {
const positiveTestVectors = testVectors.filter(vec => vec.result);
const vec = positiveTestVectors[0];
const pubKey = Buffer.from(vec.pk, 'hex');
const m = Buffer.from(vec.m, 'hex');
const sig = Buffer.from(vec.sig, 'hex');
describe('sign', () => {
it('can check sign params', () => {
try { schnorr.sign('foo', m); } catch (e) { assertError(e, 'privateKey must be a BigInteger or valid hex string'); }
try { schnorr.sign('abdcefg', m) } catch(e) { assertError(e, 'privateKey must be a BigInteger or valid hex string') };
try { schnorr.sign('@!$%', m) } catch(e) { assertError(e, 'privateKey must be a BigInteger or valid hex string') };
try { schnorr.sign(BigInteger.valueOf(1), 'foo'); } catch (e) { assertError(e, 'message must be a Buffer'); }
try { schnorr.sign(BigInteger.valueOf(1), Buffer.from([])); } catch (e) { assertError(e, 'message must be 32 bytes long'); }
try { schnorr.sign(BigInteger.valueOf(0), m); } catch (e) { assertError(e, 'privateKey must be an integer in the range 1..n-1'); }
try { schnorr.sign(BigInteger.valueOf(0), m); } catch (e) { assertError(e, 'privateKey must be an integer in the range 1..n-1'); }
try { schnorr.sign(n, m); } catch (e) { assertError(e, 'privateKey must be an integer in the range 1..n-1'); }
});
it('can sign example code in README', () => {
const privateKey = BigInteger.fromHex('B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF');
const privateKeyHexString = 'B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF';
const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const signatureToVerify = Buffer.from('6D461BEB2F2DA00027D884FD13A24E2AE85CAECCA8AAA2D41777217EC38FB4960A67D47BC4F0722754EDB0E9017072600FFE4030C2E73771DCD3773F46A62652', 'hex');
const createdSignature = schnorr.sign(privateKey, message);
const createdSignatureFromPrivKeyString = schnorr.sign(privateKeyHexString, message);
assert.strictEqual(createdSignature.toString('hex'), signatureToVerify.toString('hex'));
assert.strictEqual(createdSignatureFromPrivKeyString.toString('hex'), signatureToVerify.toString('hex'));
});
});
describe('verify', () => {
it('can check verify params', () => {
// when / then
try { schnorr.verify('foo', m, sig); } catch (e) { assertError(e, 'pubKey must be a Buffer'); }
try { schnorr.verify(Buffer.from([]), m, sig); } catch (e) { assertError(e, 'pubKey must be 32 bytes long'); }
try { schnorr.verify(pubKey, 'foo', sig); } catch (e) { assertError(e, 'message must be a Buffer'); }
try { schnorr.verify(pubKey, m, 'foo'); } catch (e) { assertError(e, 'signature must be a Buffer'); }
try { schnorr.verify(pubKey, m.slice(0, 16), sig); } catch (e) { assertError(e, 'message must be 32 bytes long'); }
try { schnorr.verify(pubKey, m, sig.slice(32)); } catch (e) { assertError(e, 'signature must be 64 bytes long'); }
try { schnorr.verify(pubKey.slice(16), m, sig); } catch (e) { assertError(e, 'pubKey must be 32 bytes long'); }
});
it('can verify example code in README', () => {
const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const signatureToVerify = Buffer.from('6D461BEB2F2DA00027D884FD13A24E2AE85CAECCA8AAA2D41777217EC38FB4960A67D47BC4F0722754EDB0E9017072600FFE4030C2E73771DCD3773F46A62652', 'hex');
const publicKey = Buffer.from('DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', 'hex');
try {
schnorr.verify(publicKey, message, signatureToVerify);
console.log('The signatures are valid.');
} catch (e) {
assert.fail(e);
}
});
});
describe('batchVerify', () => {
it('can check batch verify params', () => {
// when / then
try { schnorr.batchVerify([], [m], [sig]); } catch (e) { assertError(e, 'pubKeys must be an array with one or more elements'); }
try { schnorr.batchVerify([pubKey], [], [sig]); } catch (e) { assertError(e, 'messages must be an array with one or more elements'); }
try { schnorr.batchVerify([pubKey], [m], []); } catch (e) { assertError(e, 'signatures must be an array with one or more elements'); }
try { schnorr.batchVerify([pubKey], [m], [sig, sig]); } catch (e) { assertError(e, 'all parameters must be an array with the same length'); }
try { schnorr.batchVerify([pubKey], [m, m], [sig, sig]); } catch (e) { assertError(e, 'all parameters must be an array with the same length'); }
try { schnorr.batchVerify([pubKey, pubKey], [m, m], [sig]); } catch (e) { assertError(e, 'all parameters must be an array with the same length'); }
try { schnorr.batchVerify(['foo'], [m], [sig]); } catch (e) { assertError(e, 'pubKey[0] must be a Buffer'); }
try { schnorr.batchVerify([pubKey], ['foo'], [sig]); } catch (e) { assertError(e, 'message[0] must be a Buffer'); }
try { schnorr.batchVerify([pubKey], [m], ['foo']); } catch (e) { assertError(e, 'signature[0] must be a Buffer'); }
try { schnorr.batchVerify([pubKey], [m.slice(0, 16)], [sig]); } catch (e) { assertError(e, 'message[0] must be 32 bytes long'); }
try { schnorr.batchVerify([pubKey], [m], [sig.slice(32)]); } catch (e) { assertError(e, 'signature[0] must be 64 bytes long'); }
});
it('can batch verify example code in README', () => {
const publicKeys = [
Buffer.from('9D03B28781BD34C3250E4250FEB4543AF02AC6529398EBF776AAA5C3BDA10CFD', 'hex'),
Buffer.from('141F9A1B6360A717A7C71CB67E98D57513A84101192DC048F4382B5DF1B3C756', 'hex'),
Buffer.from('F986619C277577317E362101E08F8ACF63B34623B6A4758C2254398F70564D5A', 'hex'),
];
const messages = [
Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex'),
Buffer.from('5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C', 'hex'),
Buffer.from('B2F0CD8ECB23C1710903F872C31B0FD37E15224AF457722A87C5E0C7F50FFFB3', 'hex'),
];
const signatures = [
Buffer.from('1C621A42A3397988B63FC8F6F5EA81F8C88A71E2D30B1D7F3681CC9CB99E5AC022E52FC927DCA01B3BD3A16793F06996A5FE8A9B3FA7A91EC8934AF15F12FCF8', 'hex'),
Buffer.from('E94ECF2B0446171E44D62311EBDB631612B8AC5C4A5974033C61B924BD11B24AFC118CB661C18B0C94FDCD3F10C6F8B3F8DDA44A20DC4308430F0396EE9F477C', 'hex'),
Buffer.from('F25929B90A93130BF85EC6ABA70DA6B26FDFC37F71C7E268342873575CA0C01375F372B31E5C218E30CAE08DEAEF47F37096C7E11D506EC8DC9221109B79FB2D', 'hex'),
];
try {
schnorr.batchVerify(publicKeys, messages, signatures);
console.log('The signatures are valid.');
} catch (e) {
assert.fail(e);
}
});
});
});