Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

bip-schnorr

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bip-schnorr - npm Package Compare versions

Comparing version 0.1.1 to 0.2.0

src/check.js

4

package.json
{
"name": "bip-schnorr",
"version": "0.1.1",
"version": "0.2.0",
"description": "Pure JavaScript implementation of the BIP schnorr signature scheme",

@@ -17,3 +17,3 @@ "main": "./src/index.js",

"scripts": {
"coverage-coveralls": "nyc mocha && nyc report --reporter=text-lcov | coveralls",
"coverage-coveralls": "nyc mocha ./test/schnorr-*.spec.js && nyc report --reporter=text-lcov | coveralls",
"coverage-html": "nyc report --reporter=html",

@@ -20,0 +20,0 @@ "coverage": "nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha ./test/schnorr-*.spec.js",

@@ -47,2 +47,3 @@ # Pure JavaScript implementation of the Schnorr BIP

const bipSchnorr = require('bip-schnorr');
const convert = bipSchnorr.convert;

@@ -88,7 +89,7 @@ // signing

// aggregating signatures (not part of BIP!)
// aggregating signatures (naive Schnorr key aggregation, not part of BIP!)
const privateKey1 = BigInteger.fromHex('B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF');
const privateKey2 = BigInteger.fromHex('C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7');
const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const aggregatedSignature = bipSchnorr.aggregateSignatures([privateKey1, privateKey2], message);
const aggregatedSignature = bipSchnorr.naiveKeyAggregation([privateKey1, privateKey2], message);

@@ -98,5 +99,5 @@ // verifying an aggregated signature

const publicKey2 = Buffer.from('03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B', 'hex');
const sumOfPublicKeys = bipSchnorr.pubKeyToPoint(publicKey1).add(bipSchnorr.publicKeyToPoint(publicKey2));
const sumOfPublicKeys = convert.pubKeyToPoint(publicKey1).add(convert.pubKeyToPoint(publicKey2));
try {
bipSchnorr.verify(sumOfPublicKeys.getEncoded(true), message, aggregatedSignature);
bipSchnorr.verify(convert.pointToBuffer(sumOfPublicKeys), message, aggregatedSignature);
console.log('The signature is valid.');

@@ -106,2 +107,22 @@ } catch (e) {

}
// muSig non-interactive (not part of any BIP yet, see https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures/)
const privateKey1 = BigInteger.fromHex('B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF');
const privateKey2 = BigInteger.fromHex('C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7');
const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const aggregatedSignature = bipSchnorr.muSigNonInteractive([privateKey1, privateKey2], message);
// verifying an aggregated signature
const publicKey1 = Buffer.from('02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', 'hex');
const publicKey2 = Buffer.from('03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B', 'hex');
const L = convert.hash(Buffer.concat([publicKey1, publicKey2]));
const a1 = convert.bufferToInt(convert.hash(Buffer.concat([L, publicKey1])));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, publicKey2])));
const X = convert.pubKeyToPoint(publicKey1).multiply(a1).add(convert.pubKeyToPoint(publicKey2).multiply(a2));
try {
bipSchnorr.verify(convert.pointToBuffer(X), message, aggregatedSignature);
console.log('The signature is valid.');
} catch (e) {
console.error('The signature verification failed: ' + e);
}
```

@@ -120,8 +141,23 @@

### bipSchnorr.aggregateSignatures(privateKeys : BigInteger[], message : Buffer) : Buffer
### bipSchnorr.naiveKeyAggregation(privateKeys : BigInteger[], message : Buffer) : Buffer
Aggregates multiple signatures of different private keys over the same message into a single 64-byte signature.
### bipSchnorr.pubKeyToPoint(pubKey : Buffer) : Point
Returns the point on the `secp256k1` curve that corresponds to the given 33-byte public key.
This is just a demo of how the naive Schnorr multi-signature (or key aggregation scheme) can work.
**This scheme is not secure,** it is prone to so-called rogue-key attacks.
See [Key Aggregation for Schnorr Signatures](https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures/)
by Blockstream.
Use the **muSig** scheme that prevents that attack.
### bipSchnorr.muSigNonInteractive(privateKeys : BigInteger[], message : Buffer) : Buffer
Aggregates multiple signatures of different private keys over the same message into a single 64-byte signature
using a scheme that is safe from rogue-key attacks.
This non-interactive scheme requires the knowledge of all private keys that are participating in the
multi-signature creation. Use the **muSigInteractive** scheme that requires two steps to create
a signature with parties not sharing their private key.
## Implementations in different languages
* [Go implementation](https://github.com/hbakhtiyor/schnorr/)
## Performance

@@ -164,2 +200,2 @@

Done in 333.35s.
```
```
const BigInteger = require('bigi');
const Buffer = require('safe-buffer').Buffer;
const ecurve = require('ecurve');
const sha256 = require('js-sha256');
const randomBytes = require('random-bytes');
const curve = ecurve.getCurveByName('secp256k1');
const check = require('./check');
const convert = require('./convert');
const curve = ecurve.getCurveByName('secp256k1');
const concat = Buffer.concat;
const G = curve.G;
const p = curve.p;
const n = curve.n;
const VERSION = 'v0.1.1';
const zero = BigInteger.ZERO;

@@ -25,21 +26,16 @@ const one = BigInteger.ONE;

const P = G.multiply(privateKey);
const rX = intToBuffer(R.affineX);
const e = getE(rX, P, message);
return Buffer.concat([rX, intToBuffer(k.add(e.multiply(privateKey)).mod(n))]);
const Rx = convert.intToBuffer(R.affineX);
const e = getE(Rx, P, message);
return concat([Rx, convert.intToBuffer(k.add(e.multiply(privateKey)).mod(n))]);
}
function verify(pubKey, message, signature) {
checkVerifyParams(pubKey, message, signature);
check.checkVerifyParams(pubKey, message, signature);
// https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki#verification
const P = pubKeyToPoint(pubKey);
const r = bufferToInt(signature.slice(0, 32));
if (r.compareTo(p) >= 0) {
throw new Error('r is larger than or equal to field size');
}
const s = bufferToInt(signature.slice(32, 64));
if (s.compareTo(n) >= 0) {
throw new Error('s is larger than or equal to curve order');
}
const e = bufferToInt(hash(Buffer.concat([intToBuffer(r), pointToBuffer(P), message]))).mod(n);
const P = convert.pubKeyToPoint(pubKey);
const r = convert.bufferToInt(signature.slice(0, 32));
const s = convert.bufferToInt(signature.slice(32, 64));
check.checkSignatureInput(r, s);
const e = convert.bufferToInt(convert.hash(concat([convert.intToBuffer(r), convert.pointToBuffer(P), message]))).mod(n);
const sG = G.multiply(s);

@@ -54,3 +50,3 @@ const eP = P.multiply(e);

function batchVerify(pubKeys, messages, signatures) {
checkBatchVerifyParams(pubKeys, messages, signatures);
check.checkBatchVerifyParams(pubKeys, messages, signatures);

@@ -61,12 +57,7 @@ // https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki#Batch_Verification

for (let i = 0; i < pubKeys.length; i++) {
const P = pubKeyToPoint(pubKeys[i]);
const r = bufferToInt(signatures[i].slice(0, 32));
if (r.compareTo(p) >= 0) {
throw new Error('r is larger than or equal to field size');
}
const s = bufferToInt(signatures[i].slice(32, 64));
if (s.compareTo(n) >= 0) {
throw new Error('s is larger than or equal to curve order');
}
const e = getE(intToBuffer(r), P, messages[i]);
const P = convert.pubKeyToPoint(pubKeys[i]);
const r = convert.bufferToInt(signatures[i].slice(0, 32));
const s = convert.bufferToInt(signatures[i].slice(32, 64));
check.checkSignatureInput(r, s);
const e = getE(convert.intToBuffer(r), P, messages[i]);
const c = r.pow(three).add(seven).mod(p);

@@ -94,3 +85,3 @@ const y = c.modPow(p.add(one).divide(four), p);

function aggregateSignatures(privateKeys, message) {
function naiveKeyAggregation(privateKeys, message) {
if (!privateKeys || !privateKeys.length) {

@@ -115,4 +106,4 @@ throw new Error('privateKeys must be an array with one or more elements');

}
const rX = intToBuffer(R.affineX);
let e = getE(rX, P, message);
const Rx = convert.intToBuffer(R.affineX);
let e = getE(Rx, P, message);
let s = zero;

@@ -123,5 +114,50 @@ for (let i = 0; i < k0s.length; i++) {

}
return Buffer.concat([rX, intToBuffer(s.mod(n))]);
return concat([Rx, convert.intToBuffer(s.mod(n))]);
}
function muSigNonInteractive(privateKeys, message) {
if (!privateKeys || !privateKeys.length) {
throw new Error('privateKeys must be an array with one or more elements');
}
// https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures/
const rs = [];
const Xs = [];
let R = null;
for (let privateKey of privateKeys) {
const ri = deterministicGetK0(privateKey, message);
const Ri = G.multiply(ri);
const Xi = G.multiply(privateKey);
rs.push(ri);
Xs.push(Xi);
if (R === null) {
R = Ri;
} else {
R = R.add(Ri);
}
}
const L = convert.hash(concat(Xs.map(convert.pointToBuffer)));
const as = [];
let X = null;
for (let Xi of Xs) {
const a = convert.bufferToInt(convert.hash(concat([L, convert.pointToBuffer(Xi)])));
const summand = Xi.multiply(a);
as.push(a);
if (X === null) {
X = summand;
} else {
X = X.add(summand);
}
}
let Rx = convert.intToBuffer(R.affineX);
let e = getE(Rx, X, message);
let s = zero;
for (let i = 0; i < rs.length; i++) {
const ri = getK(R, rs[i]);
s = s.add(ri.add(e.multiply(as[i]).multiply(privateKeys[i])).mod(n));
}
return concat([Rx, convert.intToBuffer(s.mod(n))]);
}
function deterministicGetK0(privateKey, message) {

@@ -137,7 +173,6 @@ if (!BigInteger.isBigInteger(privateKey)) {

}
checkRange(privateKey);
check.checkRange(privateKey);
const concat = Buffer.concat([intToBuffer(privateKey), message]);
const h = hash(concat);
const i = bufferToInt(h);
const h = convert.hash(concat([convert.intToBuffer(privateKey), message]));
const i = convert.bufferToInt(h);
const k0 = i.mod(n);

@@ -158,91 +193,12 @@ if (k0.signum() === 0) {

function getE(rX, P, m) {
return bufferToInt(hash(Buffer.concat([rX, pointToBuffer(P), m]))).mod(n);
function getE(Rx, P, m) {
return convert.bufferToInt(convert.hash(concat([Rx, convert.pointToBuffer(P), m]))).mod(n);
}
function checkVerifyParams(pubKey, message, signature, idx) {
const idxStr = (idx !== undefined ? '[' + idx + ']' : '');
if (!Buffer.isBuffer(pubKey)) {
throw new Error('pubKey' + idxStr + ' must be a Buffer');
}
if (!Buffer.isBuffer(message)) {
throw new Error('message' + idxStr + ' must be a Buffer');
}
if (!Buffer.isBuffer(signature)) {
throw new Error('signature' + idxStr + ' must be a Buffer');
}
if (pubKey.length !== 33) {
throw new Error('pubKey' + idxStr + ' must be 33 bytes long');
}
if (message.length !== 32) {
throw new Error('message' + idxStr + ' must be 32 bytes long');
}
if (signature.length !== 64) {
throw new Error('signature' + idxStr + ' must be 64 bytes long');
}
}
function checkBatchVerifyParams(pubKeys, messages, signatures) {
if (!pubKeys || !pubKeys.length) {
throw new Error('pubKeys must be an array with one or more elements');
}
if (!messages || !messages.length) {
throw new Error('messages must be an array with one or more elements');
}
if (!signatures || !signatures.length) {
throw new Error('signatures must be an array with one or more elements');
}
if (pubKeys.length !== messages.length || messages.length !== signatures.length) {
throw new Error('all parameters must be an array with the same length')
}
for (let i = 0; i < pubKeys.length; i++) {
checkVerifyParams(pubKeys[i], messages[i], signatures[i], i);
}
}
function checkRange(privateKey) {
if (privateKey.compareTo(one) < 0 || privateKey.compareTo(n.subtract(one)) > 0) {
throw new Error('privateKey must be an integer in the range 1..n-1')
}
}
function bufferToInt(buffer) {
return BigInteger.fromBuffer(buffer);
}
function intToBuffer(bigInteger) {
return bigInteger.toBuffer(32);
}
function hash(buffer) {
return Buffer.from(sha256.create().update(buffer).array());
}
function pointToBuffer(point) {
return point.getEncoded(true);
}
function pubKeyToPoint(pubKey) {
if (pubKey.length !== 33) {
throw new Error('pubKey must be 33 bytes long');
}
const pubKeyEven = (pubKey[0] - 0x02) === 0;
const x = bufferToInt(pubKey.slice(1, 33));
const P = curve.pointFromX(!pubKeyEven, x);
if (curve.isInfinity(P)) {
throw new Error('point is at infinity');
}
const pEven = P.affineY.isEven();
if (pubKeyEven !== pEven) {
throw new Error('point does not exist');
}
return P;
}
function randomA() {
let a = null;
for (; ;) {
a = bufferToInt(Buffer.from(randomBytes.sync(32)));
a = convert.bufferToInt(Buffer.from(randomBytes.sync(32)));
try {
checkRange(a);
check.checkRange(a);
return a;

@@ -256,8 +212,7 @@ } catch (e) {

module.exports = {
VERSION,
sign,
verify,
batchVerify,
aggregateSignatures,
pubKeyToPoint,
naiveKeyAggregation,
muSigNonInteractive,
};
const schnorr = require('./bip-schnorr');
schnorr.check = require('./check');
schnorr.convert = require('./convert');
module.exports = schnorr;

@@ -9,3 +9,2 @@ /* global describe, it, beforeEach */

const curve = ecurve.getCurveByName('secp256k1');
const G = curve.G;
const n = curve.n;

@@ -63,7 +62,17 @@

describe('aggregateSignatures', () => {
describe('naiveKeyAggregation', () => {
it('can check parameters', () => {
// when / then
try { bipSchnorr.naiveKeyAggregation(null, m); } catch (e) { assertError(e, 'privateKeys must be an array with one or more elements'); }
try { bipSchnorr.naiveKeyAggregation([], m); } catch (e) { assertError(e, 'privateKeys must be an array with one or more elements'); }
});
});
describe('muSigNonInteractive', () => {
it('can check parameters', () => {
// when / then
try { bipSchnorr.muSigNonInteractive(null, m); } catch (e) { assertError(e, 'privateKeys must be an array with one or more elements'); }
try { bipSchnorr.muSigNonInteractive([], m); } catch (e) { assertError(e, 'privateKeys must be an array with one or more elements'); }
});
});
});

@@ -6,2 +6,3 @@ /* global describe, it, beforeEach */

const bipSchnorr = require('../src/bip-schnorr');
const convert = require('../src/convert');
const randomBytes = require('random-bytes');

@@ -17,2 +18,5 @@ const ecurve = require('ecurve');

const randomInt = (len) => BigInteger.fromBuffer(Buffer.from(randomBytes.sync(len))).mod(n);
const randomBuffer = (len) => Buffer.from(randomBytes.sync(len));
describe('random tests', () => {

@@ -26,5 +30,5 @@ describe('verify', () => {

for (let i = 0; i < NUM_RANDOM_TESTS; i++) {
const d = BigInteger.fromBuffer(Buffer.from(randomBytes.sync(32))).mod(n);
const pubKey = G.multiply(d).getEncoded(true);
const message = Buffer.from(randomBytes.sync(32));
const d = randomInt(32);
const pubKey = convert.pointToBuffer(G.multiply(d));
const message = randomBuffer(32);
privateKeys.push(d);

@@ -62,5 +66,5 @@ pubKeys.push(pubKey);

for (let i = 0; i < NUM_RANDOM_TESTS; i++) {
const d = BigInteger.fromBuffer(Buffer.from(randomBytes.sync(32))).mod(n);
const pubKey = G.multiply(d).getEncoded(true);
const message = Buffer.from(randomBytes.sync(32));
const d = randomInt(32);
const pubKey = convert.pointToBuffer(G.multiply(d));
const message = randomBuffer(32);
const signature = bipSchnorr.sign(d, message);

@@ -89,12 +93,12 @@ pubKeys.push(pubKey);

describe('aggregateSignatures', () => {
describe('naiveKeyAggregation', () => {
for (let i = 1; i <= NUM_RANDOM_TESTS / 2; i++) {
it('can aggregate signatures of two random private keys over same message, run #' + i, () => {
// given
const d1 = BigInteger.fromBuffer(Buffer.from(randomBytes.sync(32))).mod(n);
const d2 = BigInteger.fromBuffer(Buffer.from(randomBytes.sync(32))).mod(n);
const d1 = randomInt(32);
const d2 = randomInt(32);
const pubKey1 = G.multiply(d1);
const pubKey2 = G.multiply(d2);
const message = Buffer.from(randomBytes.sync(32));
const signature = bipSchnorr.aggregateSignatures([d1, d2], message);
const message = randomBuffer(32);
const signature = bipSchnorr.naiveKeyAggregation([d1, d2], message);

@@ -105,3 +109,3 @@ // when

try {
bipSchnorr.verify(pubKey1.add(pubKey2).getEncoded(true), message, signature);
bipSchnorr.verify(convert.pointToBuffer(pubKey1.add(pubKey2)), message, signature);
} catch (e) {

@@ -119,3 +123,3 @@ result = false;

for (let i = 1; i <= NUM_RANDOM_TESTS / 8; i++) {
const message = Buffer.from(randomBytes.sync(32));
const message = randomBuffer(32);

@@ -127,3 +131,3 @@ it('can aggregate signatures of ' + NUM_RANDOM_TESTS + ' random private keys over the same message, run #' + i, (done) => {

for (let i = 0; i < NUM_RANDOM_TESTS; i++) {
const d = BigInteger.fromBuffer(Buffer.from(randomBytes.sync(32))).mod(n);
const d = randomInt(32);
const P = G.multiply(d);

@@ -137,3 +141,3 @@ if (i === 0) {

}
const signature = bipSchnorr.aggregateSignatures(privateKeys, message);
const signature = bipSchnorr.naiveKeyAggregation(privateKeys, message);

@@ -144,3 +148,3 @@ // when

try {
bipSchnorr.verify(sumPubKey.getEncoded(true), message, signature);
bipSchnorr.verify(convert.pointToBuffer(sumPubKey), message, signature);
} catch (e) {

@@ -159,2 +163,83 @@ result = false;

});
describe('muSigNonInteractive', () => {
for (let i = 1; i <= NUM_RANDOM_TESTS / 2; i++) {
it('can aggregate signatures of two random private keys over same message, run #' + i, () => {
// given
const x1 = randomInt(32);
const x2 = randomInt(32);
const X1 = G.multiply(x1);
const X2 = G.multiply(x2);
const L = convert.hash(Buffer.concat([convert.pointToBuffer(X1), convert.pointToBuffer(X2)]));
const a1 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X1)])));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X2)])));
const X = X1.multiply(a1).add(X2.multiply(a2));
const message = randomBuffer(32);
const signature = bipSchnorr.muSigNonInteractive([x1, x2], message);
// when
let result = true;
let error = null;
try {
bipSchnorr.verify(convert.pointToBuffer(X), message, signature);
} catch (e) {
result = false;
error = e;
}
// then
assert.strictEqual(result, true, error);
assert.strictEqual(error, null);
});
}
for (let i = 1; i <= NUM_RANDOM_TESTS / 8; i++) {
const message = randomBuffer(32);
it('can aggregate signatures of ' + NUM_RANDOM_TESTS + ' random private keys over the same message, run #' + i, (done) => {
// given
const privateKeys = [];
const publicKeys = [];
let pubKeyBuf = null;
for (let i = 0; i < NUM_RANDOM_TESTS; i++) {
const xi = randomInt(32);
const Xi = G.multiply(xi);
if (i === 0) {
pubKeyBuf = convert.pointToBuffer(Xi);
} else {
pubKeyBuf = Buffer.concat([pubKeyBuf, convert.pointToBuffer(Xi)]);
}
privateKeys.push(xi);
publicKeys.push(Xi);
}
const L = convert.hash(pubKeyBuf);
let X = null;
for (let i = 0; i < NUM_RANDOM_TESTS; i++) {
const a = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(publicKeys[i])])));
if (i === 0) {
X = publicKeys[i].multiply(a);
} else {
X = X.add(publicKeys[i].multiply(a));
}
}
const signature = bipSchnorr.muSigNonInteractive(privateKeys, message);
// when
let result = true;
let error = null;
try {
bipSchnorr.verify(convert.pointToBuffer(X), message, signature);
} catch (e) {
result = false;
error = e;
}
// then
assert.strictEqual(result, true, error);
assert.strictEqual(error, null);
done();
}).timeout(RANDOM_TEST_TIMEOUT);
}
});
});

@@ -6,2 +6,3 @@ /* global describe, it, beforeEach */

const bipSchnorr = require('../src/bip-schnorr');
const convert = require('../src/convert');
const ecurve = require('ecurve');

@@ -112,3 +113,3 @@

describe('aggregateSignatures', () => {
describe('naiveKeyAggregation', () => {
const vectorsWithPrivateKeys = testVectors

@@ -129,3 +130,3 @@ .filter(vec => vec.d !== null)

const m = Buffer.from(vec1.m, 'hex');
const signature = bipSchnorr.aggregateSignatures([d1, d2], m);
const signature = bipSchnorr.naiveKeyAggregation([d1, d2], m);

@@ -135,3 +136,3 @@ // when

try {
bipSchnorr.verify(P1.add(P2).getEncoded(true), m, signature);
bipSchnorr.verify(convert.pointToBuffer(P1.add(P2)), m, signature);
result = true;

@@ -147,3 +148,3 @@ } catch (e) {

const m = Buffer.from(vec1.m, 'hex');
const signature = bipSchnorr.aggregateSignatures([d2, d3], m);
const signature = bipSchnorr.naiveKeyAggregation([d2, d3], m);

@@ -153,3 +154,3 @@ // when

try {
bipSchnorr.verify(P2.add(P3).getEncoded(true), m, signature);
bipSchnorr.verify(convert.pointToBuffer(P2.add(P3)), m, signature);
result = true;

@@ -165,3 +166,3 @@ } catch (e) {

const m = Buffer.from(vec1.m, 'hex');
const signature = bipSchnorr.aggregateSignatures([d1, d2, d3], m);
const signature = bipSchnorr.naiveKeyAggregation([d1, d2, d3], m);

@@ -171,3 +172,3 @@ // when

try {
bipSchnorr.verify(P.getEncoded(true), m, signature);
bipSchnorr.verify(convert.pointToBuffer(P), m, signature);
result = true;

@@ -184,3 +185,3 @@ } catch (e) {

const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const aggregatedSignature = bipSchnorr.aggregateSignatures([privateKey1, privateKey2], message);
const aggregatedSignature = bipSchnorr.naiveKeyAggregation([privateKey1, privateKey2], message);
assert.strictEqual(aggregatedSignature.toString('hex'), 'd60d7f81c15d57b04f8f6074de17f1b9eef2e0a9c9b2e93550c15b45d6998dc24ef5e393b356e7c334f36cee15e0f5f1e9ce06e7911793ddb9bd922d545b7525');

@@ -191,6 +192,6 @@

const publicKey2 = Buffer.from('03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B', 'hex');
const sumOfPublicKeys = bipSchnorr.pubKeyToPoint(publicKey1).add(bipSchnorr.pubKeyToPoint(publicKey2));
const sumOfPublicKeys = convert.pubKeyToPoint(publicKey1).add(convert.pubKeyToPoint(publicKey2));
let result = false;
try {
bipSchnorr.verify(sumOfPublicKeys.getEncoded(true), message, aggregatedSignature);
bipSchnorr.verify(convert.pointToBuffer(sumOfPublicKeys), message, aggregatedSignature);
result = true;

@@ -200,6 +201,105 @@ } catch (e) {

}
assert.strictEqual(sumOfPublicKeys.getEncoded(true).toString('hex'), '03f0a6305d39a34582ba49a78bdf38ced935b3efce1e889d6820103665f35ee45b');
assert.strictEqual(convert.pointToBuffer(sumOfPublicKeys).toString('hex'), '03f0a6305d39a34582ba49a78bdf38ced935b3efce1e889d6820103665f35ee45b');
assert.strictEqual(result, true);
});
});
describe('muSigNonInteractive', () => {
const vectorsWithPrivateKeys = testVectors
.filter(vec => vec.d !== null)
.filter(vec => BigInteger.fromHex(vec.d).compareTo(BigInteger.ONE) > 0);
const vec1 = vectorsWithPrivateKeys[0];
const x1 = BigInteger.fromHex(vectorsWithPrivateKeys[0].d);
const x2 = BigInteger.fromHex(vectorsWithPrivateKeys[1].d);
const x3 = BigInteger.fromHex(vectorsWithPrivateKeys[2].d);
const X1 = G.multiply(x1);
const X2 = G.multiply(x2);
const X3 = G.multiply(x3);
it('can sign and verify two aggregated signatures over same message', () => {
// given
const m = Buffer.from(vec1.m, 'hex');
const L = convert.hash(Buffer.concat([convert.pointToBuffer(X1), convert.pointToBuffer(X2)]));
const a1 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X1)])));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X2)])));
const X = X1.multiply(a1).add(X2.multiply(a2));
const signature = bipSchnorr.muSigNonInteractive([x1, x2], m);
// when
let result = false;
try {
bipSchnorr.verify(convert.pointToBuffer(X), m, signature);
result = true;
} catch (e) {
result = false;
}
assert.strictEqual(result, true);
});
it('can sign and verify two more aggregated signatures over same message', () => {
// given
const L = convert.hash(Buffer.concat([convert.pointToBuffer(X2), convert.pointToBuffer(X3)]));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X2)])));
const a3 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X3)])));
const X = X2.multiply(a2).add(X3.multiply(a3));
const m = Buffer.from(vec1.m, 'hex');
const signature = bipSchnorr.muSigNonInteractive([x2, x3], m);
// when
let result = false;
try {
bipSchnorr.verify(convert.pointToBuffer(X), m, signature);
result = true;
} catch (e) {
result = false;
}
assert.strictEqual(result, true);
});
it('can sign and verify three aggregated signatures over same message', () => {
// given
const L = convert.hash(Buffer.concat([convert.pointToBuffer(X1), convert.pointToBuffer(X2), convert.pointToBuffer(X3)]));
const a1 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X1)])));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X2)])));
const a3 = convert.bufferToInt(convert.hash(Buffer.concat([L, convert.pointToBuffer(X3)])));
const X = X1.multiply(a1).add(X2.multiply(a2)).add(X3.multiply(a3));
const m = Buffer.from(vec1.m, 'hex');
const signature = bipSchnorr.muSigNonInteractive([x1, x2, x3], m);
// when
let result = false;
try {
bipSchnorr.verify(convert.pointToBuffer(X), m, signature);
result = true;
} catch (e) {
result = false;
}
assert.strictEqual(result, true);
});
it('can aggregate and verify example in README', () => {
const privateKey1 = BigInteger.fromHex('B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF');
const privateKey2 = BigInteger.fromHex('C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7');
const message = Buffer.from('243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89', 'hex');
const aggregatedSignature = bipSchnorr.muSigNonInteractive([privateKey1, privateKey2], message);
assert.strictEqual(aggregatedSignature.toString('hex'), 'd60d7f81c15d57b04f8f6074de17f1b9eef2e0a9c9b2e93550c15b45d6998dc298fde09fcea69e99b195a371d7a7e879a40474c67e4b63fb2cd5c6b7a3058156');
// verifying an aggregated signature
const publicKey1 = Buffer.from('02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', 'hex');
const publicKey2 = Buffer.from('03FAC2114C2FBB091527EB7C64ECB11F8021CB45E8E7809D3C0938E4B8C0E5F84B', 'hex');
const L = convert.hash(Buffer.concat([publicKey1, publicKey2]));
const a1 = convert.bufferToInt(convert.hash(Buffer.concat([L, publicKey1])));
const a2 = convert.bufferToInt(convert.hash(Buffer.concat([L, publicKey2])));
const X = convert.pubKeyToPoint(publicKey1).multiply(a1).add(convert.pubKeyToPoint(publicKey2).multiply(a2));
let result = false;
try {
bipSchnorr.verify(convert.pointToBuffer(X), message, aggregatedSignature);
result = true;
} catch (e) {
result = false;
}
assert.strictEqual(convert.pointToBuffer(X).toString('hex'), '03a6c519a533b1e8ff578672af695a6f7f8cebb29b7d391e5c5fcfb91dcd597fb8');
assert.strictEqual(result, true);
});
});
});

@@ -64,3 +64,3 @@ const bipSchnorr = require('../src/bip-schnorr');

try {
const result = bipSchnorr.aggregateSignatures(privateKeys, messages[0]);
const result = bipSchnorr.naiveKeyAggregation(privateKeys, messages[0]);
if (!result || result.length !== 64) {

@@ -67,0 +67,0 @@ console.error('Aggregating signatures failed!');

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc