Skip to content

Commit

Permalink
Merge pull request #5 from cashubtc/allow-multiple-private-keys
Browse files Browse the repository at this point in the history
allow multiple private keys when signing proofs
  • Loading branch information
callebtc authored Nov 5, 2024
2 parents 9226e38 + 2e5954b commit 61c0cf1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 3 deletions.
26 changes: 24 additions & 2 deletions src/client/NUT11.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,36 @@ export const signBlindedMessage = (B_: string, privateKey: PrivKey): Uint8Array
return sig;
};

export const getSignedProofs = (proofs: Array<Proof>, privateKey: string): Array<Proof> => {
export const getSignedProofs = (
proofs: Array<Proof>,
privateKey: string | string[]
): Array<Proof> => {
let keypairs: Array<{ priv: string; pub: string }> = [];
let pk = '';

if (privateKey instanceof Array) {
for (const k of privateKey) {
keypairs.push({ priv: k, pub: bytesToHex(schnorr.getPublicKey(k)) });
}
} else {
pk = privateKey;
}

return proofs.map((p) => {
try {
const parsed: Secret = parseSecret(p.secret);
if (parsed[0] !== 'P2PK') {
throw new Error('unknown secret type');
}
return getSignedProof(p, hexToBytes(privateKey));
if (keypairs.length) {
const matchingKey = keypairs.find((pair) => parsed[1].data === pair.pub)?.priv;
if (!matchingKey) {
throw new Error('no matching key found');
} else {
pk = matchingKey;
}
}
return getSignedProof(p, hexToBytes(pk));
} catch (error) {
return p;
}
Expand Down
62 changes: 61 additions & 1 deletion test/client/NUT11.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { schnorr } from '@noble/curves/secp256k1';
import { createP2PKsecret, getSignedProof } from '../../src/client/NUT11.js';
import { createP2PKsecret, getSignedProof, getSignedProofs } from '../../src/client/NUT11.js';
import { bytesToHex } from '@noble/curves/abstract/utils';
import { Proof, pointFromHex } from '../../src/common';
import { parseSecret } from '../../src/common/NUT11.js';
Expand Down Expand Up @@ -33,6 +33,66 @@ describe('test create p2pk secret', () => {
const verify = verifyP2PKSig(signedProof);
expect(verify).toBe(true);
});

test('sign and verify proofs', async () => {
const secretStr = `["P2PK",{"nonce":"76f5bf3e36273bf1a09006ef32d4551c07a34e218c2fc84958425ad00abdfe06","data":"${bytesToHex(
PUBKEY
)}"}]`;
const proof1: Proof = {
amount: 1,
C: pointFromHex('034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be'),
id: '00000000000',
secret: new TextEncoder().encode(secretStr)
};

const proof2: Proof = {
amount: 1,
C: pointFromHex('034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be'),
id: '00000000000',
secret: new TextEncoder().encode(secretStr)
};

const proofs = [proof1, proof2];

const signedProofs = getSignedProofs(proofs, bytesToHex(PRIVKEY));
const verify0 = verifyP2PKSig(signedProofs[0]);
const verify1 = verifyP2PKSig(signedProofs[1]);
expect(verify0).toBe(true);
expect(verify1).toBe(true);
});

test('sign and verify proofs, different keys', async () => {
const PRIVKEY2 = schnorr.utils.randomPrivateKey();
const PUBKEY2 = schnorr.getPublicKey(PRIVKEY2);

const secretStr = `["P2PK",{"nonce":"76f5bf3e36273bf1a09006ef32d4551c07a34e218c2fc84958425ad00abdfe06","data":"${bytesToHex(
PUBKEY
)}"}]`;
const secretStr2 = `["P2PK",{"nonce":"76f5bf3e36273bf1a09006ef32d4551c07a34e218c2fc84958425ad00abdfe06","data":"${bytesToHex(
PUBKEY2
)}"}]`;
const proof1: Proof = {
amount: 1,
C: pointFromHex('034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be'),
id: '00000000000',
secret: new TextEncoder().encode(secretStr)
};

const proof2: Proof = {
amount: 1,
C: pointFromHex('034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be'),
id: '00000000000',
secret: new TextEncoder().encode(secretStr2)
};

const proofs = [proof1, proof2];

const signedProofs = getSignedProofs(proofs, [bytesToHex(PRIVKEY), bytesToHex(PRIVKEY2)]);
const verify0 = verifyP2PKSig(signedProofs[0]);
const verify1 = verifyP2PKSig(signedProofs[1]);
expect(verify0).toBe(true);
expect(verify1).toBe(true);
});
test('sign and verify blindedMessage', async () => {
const blindedMessage = createRandomBlindedMessage(PRIVKEY);
const verify = verifyP2PKSigOutput(blindedMessage, bytesToHex(PUBKEY));
Expand Down

0 comments on commit 61c0cf1

Please sign in to comment.