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.5.0 to 0.6.0

src/mu-sig.js

4

package.json
{
"name": "bip-schnorr",
"version": "0.5.0",
"description": "Pure JavaScript implementation of the BIP schnorr signature scheme",
"version": "0.6.0",
"description": "Pure JavaScript implementation of the BIP schnorr signature scheme and the muSig multi-signature scheme",
"main": "./src/index.js",

@@ -6,0 +6,0 @@ "engines": {

@@ -17,2 +17,5 @@ # Pure JavaScript implementation of BIP340 Schnorr Signatures for secp256k1

The MuSig implementation is based upon the C implementation in the
[secp256k1-zkp fork](https://github.com/ElementsProject/secp256k1-zkp)
I am by no means an expert in high performance JavaScript or the underlying cryptography.

@@ -89,2 +92,155 @@ This library is slow, not peer reviewed at all, not tested (outside of passing the official test vectors) against

### muSig
```javascript
const Buffer = require('safe-buffer').Buffer;
const BigInteger = require('bigi');
const randomBytes = require('random-bytes');
const randomBuffer = (len) => Buffer.from(randomBytes.sync(len));
const schnorr = require('bip-schnorr');
const convert = schnorr.convert;
const muSig = schnorr.muSig;
// data known to every participant
const publicData = {
pubKeys: [
Buffer.from('846f34fdb2345f4bf932cb4b7d278fb3af24f44224fb52ae551781c3a3cad68a', 'hex'),
Buffer.from('cd836b1d42c51d80cef695a14502c21d2c3c644bc82f6a7052eb29247cf61f4f', 'hex'),
Buffer.from('b8c1765111002f09ba35c468fab273798a9058d1f8a4e276f45a1f1481dd0bdb', 'hex'),
],
message: convert.hash(Buffer.from('muSig is awesome!', 'utf8')),
pubKeyHash: null,
pubKeyCombined: null,
commitments: [],
nonces: [],
nonceCombined: null,
partialSignatures: [],
signature: null,
};
// data only known by the individual party, these values are never shared
// between the signers!
const signerPrivateData = [
// signer 1
{
privateKey: BigInteger.fromHex('add2b25e2d356bec3770305391cbc80cab3a40057ad836bcb49ef3eed74a3fee'),
session: null,
},
// signer 2
{
privateKey: BigInteger.fromHex('0a1645eef5a10e1f5011269abba9fd85c4f0cc70820d6f102fb7137f2988ad78'),
session: null,
},
// signer 3
{
privateKey: BigInteger.fromHex('2031e7fed15c770519707bb092a6337215530e921ccea42030c15d86e8eaf0b8'),
session: null,
}
];
// -----------------------------------------------------------------------
// Step 1: Combine the public keys
// The public keys P_i are combined into the combined public key P.
// This can be done by every signer individually or by the initializing
// party and then be distributed to every participant.
// -----------------------------------------------------------------------
publicData.pubKeyHash = muSig.computeEll(publicData.pubKeys);
publicData.pubKeyCombined = muSig.pubKeyCombine(publicData.pubKeys, publicData.pubKeyHash);
// -----------------------------------------------------------------------
// Step 2: Create the private signing session
// Each signing party does this in private. The session ID *must* be
// unique for every call to sessionInitialize, otherwise it's trivial for
// an attacker to extract the secret key!
// -----------------------------------------------------------------------
signerPrivateData.forEach((data, idx) => {
const sessionId = randomBuffer(32); // must never be reused between sessions!
data.session = muSig.sessionInitialize(
sessionId,
data.privateKey,
publicData.message,
publicData.pubKeyCombined,
publicData.pubKeyHash,
idx
);
});
const signerSession = signerPrivateData[0].session;
// -----------------------------------------------------------------------
// Step 3: Exchange commitments (communication round 1)
// The signers now exchange the commitments H(R_i). This is simulated here
// by copying the values from the private data to public data array.
// -----------------------------------------------------------------------
for (let i = 0; i < publicData.pubKeys.length; i++) {
publicData.commitments[i] = signerPrivateData[i].session.commitment;
}
// -----------------------------------------------------------------------
// Step 4: Get nonces (communication round 2)
// Now that everybody has commited to the session, the nonces (R_i) can be
// exchanged. Again, this is simulated by copying.
// -----------------------------------------------------------------------
for (let i = 0; i < publicData.pubKeys.length; i++) {
publicData.nonces[i] = signerPrivateData[i].session.nonce;
}
// -----------------------------------------------------------------------
// Step 5: Combine nonces
// The nonces can now be combined into R. Each participant should do this
// and keep track of whether the nonce was negated or not. This is needed
// for the later steps.
// -----------------------------------------------------------------------
publicData.nonceCombined = muSig.sessionNonceCombine(signerSession, publicData.nonces);
signerPrivateData.forEach(data => (data.session.combinedNonceParity = signerSession.combinedNonceParity));
// -----------------------------------------------------------------------
// Step 6: Generate partial signatures
// Every participant can now create their partial signature s_i over the
// given message.
// -----------------------------------------------------------------------
signerPrivateData.forEach(data => {
data.session.partialSignature = muSig.partialSign(data.session, publicData.message, publicData.nonceCombined, publicData.pubKeyCombined);
});
// -----------------------------------------------------------------------
// Step 7: Exchange partial signatures (communication round 3)
// The partial signature of each signer is exchanged with the other
// participants. Simulated here by copying.
// -----------------------------------------------------------------------
for (let i = 0; i < publicData.pubKeys.length; i++) {
publicData.partialSignatures[i] = signerPrivateData[i].session.partialSignature;
}
// -----------------------------------------------------------------------
// Step 8: Verify individual partial signatures
// Every participant should verify the partial signatures received by the
// other participants.
// -----------------------------------------------------------------------
for (let i = 0; i < publicData.pubKeys.length; i++) {
muSig.partialSigVerify(
signerSession,
publicData.partialSignatures[i],
publicData.nonceCombined,
i,
publicData.pubKeys[i],
publicData.nonces[i]
);
}
// -----------------------------------------------------------------------
// Step 9: Combine partial signatures
// Finally, the partial signatures can be combined into the full signature
// (s, R) that can be verified against combined public key P.
// -----------------------------------------------------------------------
publicData.signature = muSig.partialSigCombine(publicData.nonceCombined, publicData.partialSignatures);
// -----------------------------------------------------------------------
// Step 10: Verify signature
// The resulting signature can now be verified as a normal Schnorr
// signature (s, R) over the message m and public key P.
// -----------------------------------------------------------------------
const pkCombined = convert.intToBuffer(publicData.pubKeyCombined.affineX);
schnorr.verify(pkCombined, publicData.message, publicData.signature);
```
## API

@@ -101,2 +257,32 @@

### schnorr.muSig.computeEll(pubKeys : Buffer[]) : Buffer
Generate `ell` which is the hash over all public keys participating in a muSig session.
### schnorr.muSig.pubKeyCombine(pubKeys : Buffer[], pubKeyHash : Buffer) : Point
Creates the special rogue-key-resistant combined public key `P` by applying the MuSig coefficient
to each public key `P_i` before adding them together.
### schnorr.muSig.sessionInitialize(sessionId : Buffer, privateKey : BigInteger, message : Buffer, pubKeyCombined : Point, ell : Buffer, idx : number) : Session
Creates a signing session. Each participant must create a session and *must not share* the content
of the session apart from the commitment and later the nonce.
**It is absolutely necessary that the session ID
is unique for every call of `sessionInitialize`. Otherwise
it's trivial for an attacker to extract the secret key!**
### schnorr.muSig.sessionNonceCombine(session : Session, nonces : Buffer[]) : Buffer
Combines multiple nonces `R_i` into the combined nonce `R`.
### schnorr.muSig.partialSign(session : Session, message : Buffer, nonceCombined : Buffer, pubKeyCombined : Buffer) : BigInteger
Creates a partial signature `s_i` for a participant.
### schnorr.muSig.partialSigVerify(session : Session, partialSig : BigInteger, nonceCombined : Buffer, idx : number, pubKey : Buffer, nonce : Buffer) : void
Verifies a partial signature `s_i` against the participant's public key `P_i`.
Throws an `Error` if verification fails.
### schnorr.muSig.partialSigCombine(nonceCombined : Buffer, partialSigs : BigInteger[]) : Buffer
Combines multiple partial signatures into a Schnorr signature `(s, R)` that can be verified against
the combined public key `P`.
## Implementations in different languages

@@ -103,0 +289,0 @@ * [Go implementation](https://github.com/hbakhtiyor/schnorr/)

@@ -26,3 +26,3 @@ const BigInteger = require('bigi');

function bip340CheckPubKeyArr(pubKeys) {
function checkPubKeyArr(pubKeys) {
checkArray('pubKeys', pubKeys);

@@ -48,2 +48,9 @@ for (let i = 0; i < pubKeys.length; i++) {

function checkNonceArr(nonces) {
checkArray('nonces', nonces);
for (let i = 0; i < nonces.length; i++) {
checkBuffer('nonce', nonces[i], 32, i);
}
}
function checkPrivateKey(privateKey, idx) {

@@ -69,3 +76,3 @@ const idxStr = (idx !== undefined ? '[' + idx + ']' : '');

function checkBatchVerifyParams(pubKeys, messages, signatures) {
bip340CheckPubKeyArr(pubKeys);
checkPubKeyArr(pubKeys);
checkMessageArr(messages);

@@ -78,2 +85,15 @@ checkSignatureArr(signatures);

function checkSessionParams(sessionId, privateKey, message, pubKeyCombined, ell) {
checkSignParams(privateKey, message);
checkBuffer('sessionId', sessionId, 32);
checkPoint('pubKeyCombined', pubKeyCombined);
checkBuffer('ell', ell, 32);
}
function checkPoint(name, P) {
if (!P.curve || P.curve.isInfinity(P)) {
throw new Error(name + ' must be a point on the curve')
}
}
function checkRange(name, scalar) {

@@ -111,2 +131,3 @@ if (scalar.compareTo(one) < 0 || scalar.compareTo(n.subtract(one)) > 0) {

module.exports = {
checkSessionParams,
checkSignParams,

@@ -118,3 +139,6 @@ checkVerifyParams,

checkPointExists,
checkPubKeyArr,
checkArray,
checkNonceArr,
checkAux,
};
const schnorr = require('./schnorr');
schnorr.check = require('./check');
schnorr.convert = require('./convert');
schnorr.muSig = require('./mu-sig');
schnorr.taproot = require('./taproot');
module.exports = schnorr;
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