The library provides the following actions:
- 2-party ECDSA secp256k1: generation and signing
- Key share refresh
Apple M1, 8GB RAM (node v18.4.0)
KeyGen no pre-generated Paillier keys x 0 ops/sec @ 1275ms/op ± 15.34% (min: 712ms, max: 2178ms)
KeyGen with pre-generated Paillier keys x 2 ops/sec @ 399ms/op
Signature x 10 ops/sec @ 98ms/op
RAM: rss=175.1mb heap=46.1mb used=22.6mb ext=0.9mb arr=0.5mb
Intel i5-6500, 32GB RAM (Windows 10, node v18.7.0)
KeyGen no pre-generated Paillier keys x 0 ops/sec @ 6209ms/op ± 16.78% (min: 3331ms, max: 10618ms)
KeyGen with pre-generated Paillier keys x 0 ops/sec @ 2025ms/op
Signature x 1 ops/sec @ 539ms/op
RAM: rss=68.5mb heap=30.4mb used=17.1mb ext=1.1mb arr=0.7mb
npm install @silencelaboratories/ecdsa-tss
Then either require (Node.js CommonJS):
const ecdsaTSS = require("@silencelaboratories/ecdsa-tss");
or import (JavaScript ES module):
import * as ecdsaTSS from "@silencelaboratories/ecdsa-tss";
An example of KeyGen:
import {
IP1KeyShare,
IP2KeyShare,
P1KeyGen,
P2KeyGen,
randBytes,
} from "@silencelaboratories/ecdsa-tss";
export async function performKeygen(): Promise<
[IP1KeyShare, IP2KeyShare] | null
> {
const sessionId = "some session id";
const x1 = await randBytes(32);
const x2 = await randBytes(32);
const p1 = new P1KeyGen(sessionId, x1);
await p1.init();
const p2 = new P2KeyGen(sessionId, x2);
// Round 1
const msg1 = await p1.processMessage(null);
const msg2 = await p2.processMessage(msg1.msg_to_send);
// Round 2
const msg3 = await p1.processMessage(msg2.msg_to_send);
const p1KeyShare = msg3.p1_key_share;
let msg4 = await p2.processMessage(msg3.msg_to_send);
const p2KeyShare = msg4.p2_key_share;
if (!p1KeyShare || !p2KeyShare) {
return null;
}
return [p1KeyShare, p2KeyShare];
}
An example of Sign:
import {
P1Signature,
P2Signature,
IP1KeyShare,
IP2KeyShare,
randBytes,
IP1SignatureResult,
IP2SignatureResult,
} from "@silencelaboratories/ecdsa-tss";
async function signature() {
const keyshares = await performKeygen();
if (keyshares === null) {
throw new Error("Keygen failed");
}
const sessionId = "session id for signature";
const messageHash = await randBytes(32);
const p1 = new P1Signature(sessionId, messageHash, keyshares[0]);
const p2 = new P2Signature(sessionId, messageHash, keyshares[1]);
// Round 1
const msg1 = await p1.processMessage(null);
const msg2 = await p2.processMessage(msg1.msg_to_send);
// Round 2
const msg3 = await p1.processMessage(msg2.msg_to_send);
const msg4 = await p2.processMessage(msg3.msg_to_send);
// Round 3
const msg5 = await p1.processMessage(msg4.msg_to_send);
const p1Sign = msg5.signature;
const msg6 = await p2.processMessage(msg5.msg_to_send);
const p2Sign = msg6.signature;
if (!p1Sign || !p2Sign) {
return null;
}
console.log("p1Sign", "0x" + p1Sign);
console.log("p2Sign", "0x" + p2Sign);
}
An example of Key Refresh:
import {
P1KeyGen,
P2KeyGen,
IP1KeyShare,
IP2KeyShare,
randBytes,
IP1KeyGenResult,
IP2KeyGenResult,
} from "@silencelaboratories/ecdsa-tss";
async function refresh() {
const keyshares = await performKeygen();
if (!keyshares) {
throw new Error("Failed to generate keyshares");
}
console.log("P1 keyshare pubkey:", "0x" + keyshares[0].public_key);
console.log("P2 keyshare pubkey:", "0x" + keyshares[1].public_key);
const sessionId = "session id for key generation action";
/// Initialize with old keyshare
const p1 = P1KeyGen.getInstanceForKeyRefresh(sessionId, keyshares[0]);
await p1.init();
/// Initialize with old keyshare
const p2 = P2KeyGen.getInstanceForKeyRefresh(sessionId, keyshares[1]);
// Round 1
const msg1 = await p1.processMessage(null);
const msg2 = await p2.processMessage(msg1.msg_to_send);
// Round 2
const msg3 = await p1.processMessage(msg2.msg_to_send);
const p1KeyShare = msg3.p1_key_share;
let msg4 = await p2.processMessage(msg3.msg_to_send);
const p2KeyShare = msg4.p2_key_share;
if (!p1KeyShare || !p2KeyShare) {
return null;
}
console.log("Successfully refreshed keyshares!");
console.log(
"Public key after refresh (should remain the same as before): ",
p1KeyShare.public_key
);
}