PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-core/src/account-lib/mpc/tss/ecdsa
Просмотр файла: ecdsa.ts
import * as paillierBigint from 'paillier-bigint';
import * as bigintCryptoUtils from 'bigint-crypto-utils';
import { secp256k1 as secp } from '@noble/curves/secp256k1';
import { createHash, Hash, randomBytes } from 'crypto';
import { bip32 } from '@bitgo/utxo-lib';
import { bigIntFromBufferBE, bigIntFromU8ABE, bigIntToBufferBE, getPaillierPublicKey } from '../../util';
import { Secp256k1Curve } from '../../curves';
import {
EcdsaPaillierProof,
EcdsaRangeProof,
EcdsaTypes,
EcdsaZkVProof,
HashCommitment,
Schnorr,
randomPositiveCoPrimeTo,
hexToBigInt,
minModulusBitLength,
HDTree,
Secp256k1Bip32HdTree,
chaincodeBase,
Shamir,
SchnorrProof,
} from '@bitgo/sdk-lib-mpc';
import {
AShare,
BShare,
DShare,
GShare,
KeyCombined,
KeyShare,
KShare,
MUShare,
NShare,
OShare,
PShare,
PublicUTShare,
RangeProofWithCheckShare,
Signature,
SignCombine,
SignCombineRT,
SignConvert,
SignConvertRT,
SignConvertStep1,
SignConvertStep1Response,
SignConvertStep2,
SignConvertStep2Response,
SignConvertStep3,
SignConvertStep3Response,
SignShareRT,
SShare,
SubkeyShare,
UTShare,
VAShareWithProofs,
VAShare,
WShare,
XShare,
XShareWithChallenges,
YShareWithChallenges,
PublicVAShareWithProofs,
} from './types';
const _5n = BigInt(5);
// Size of alpha and mu shares in bytes expected by the implementation of the protocol
const ALPHAMUSIZE = 768;
/**
* ECDSA TSS implementation supporting 2:n Threshold
*/
export default class Ecdsa {
static curve: Secp256k1Curve = new Secp256k1Curve();
static hdTree: HDTree = new Secp256k1Bip32HdTree();
static shamir: Shamir = new Shamir(Ecdsa.curve);
/**
* Generate shares for participant at index and split keys `(threshold,numShares)` ways.
* @param {number} index participant index
* @param {number} threshold Signing threshold
* @param {number} numShares Number of shares
* @param {Buffer} seed optional 64 byte seed to use for key generation
* @param sync optional sync flag, if true then a synchronous version of Paillier key generation is used that does not spawn Worker threads.
* @returns {Promise<KeyShare>} Returns the private p-share
* and n-shares to be distributed to participants at their corresponding index.
*/
async keyShare(index: number, threshold: number, numShares: number, seed?: Buffer, sync = false): Promise<KeyShare> {
if (!(index > 0 && index <= numShares && threshold <= numShares && threshold === 2)) {
throw 'Invalid KeyShare Config';
}
if (seed && seed.length < 64) {
throw new Error('Seed must have a length of at least 64 bytes');
}
let seedWithValidLength = seed;
if (seed && seed.length > 64) {
// if seed length is greater than 64 bytes, hash seed to 64 bytes.
seedWithValidLength = createHash('sha512').update(seed).digest();
}
// Generate additively homomorphic encryption key.
let paillierKeyPair: paillierBigint.KeyPair;
if (!sync) {
paillierKeyPair = await paillierBigint.generateRandomKeys(minModulusBitLength, true);
} else {
// eslint-disable-next-line no-sync
paillierKeyPair = paillierBigint.generateRandomKeysSync(minModulusBitLength, true);
}
const { publicKey, privateKey } = paillierKeyPair;
// Accept a 64 byte seed and create an extended private key from that seed
const secretKey = seedWithValidLength && bip32.fromSeed(seedWithValidLength);
const u =
(secretKey && secretKey.privateKey && bigIntFromU8ABE(new Uint8Array(secretKey.privateKey))) ??
Ecdsa.curve.scalarRandom();
const y = Ecdsa.curve.basePointMult(u);
const chaincode = (secretKey && secretKey.chainCode) ?? randomBytes(32);
// Compute secret shares of the private key
const { shares: uShares, v } = Ecdsa.shamir.split(u, threshold, numShares);
const currentParticipant: PShare = {
i: index,
t: threshold,
c: numShares,
l: bigIntToBufferBE(privateKey.lambda, 192).toString('hex'),
m: bigIntToBufferBE(privateKey.mu, 192).toString('hex'),
n: bigIntToBufferBE(publicKey.n, 384).toString('hex'),
y: bigIntToBufferBE(y, 33).toString('hex'),
u: bigIntToBufferBE(uShares[index], 32).toString('hex'),
uu: u.toString(),
chaincode: chaincode.toString('hex'),
};
const keyShare: KeyShare = {
pShare: currentParticipant,
nShares: {},
};
for (const share in uShares) {
const participantIndex = parseInt(share, 10);
if (participantIndex !== index) {
keyShare.nShares[participantIndex] = {
i: participantIndex,
j: currentParticipant['i'],
n: publicKey.n.toString(16),
y: bigIntToBufferBE(y, 33).toString('hex'),
v: bigIntToBufferBE(v[0], 33).toString('hex'),
u: bigIntToBufferBE(uShares[participantIndex], 32).toString('hex'),
chaincode: chaincode.toString('hex'),
} as NShare;
}
}
return keyShare;
}
/**
* Combine data shared during the key generation protocol.
* @param {KeyShare} participantShares private p-share and
* n-shares received from all other participants.
* @returns {KeyCombined} Returns the participant private x-share
* and y-shares to be used when generating signing shares.
*/
keyCombine(pShare: PShare, nShares: NShare[]): KeyCombined {
const allShares = [pShare, ...nShares];
// Compute the public key.
const y = allShares.map((participant) => hexToBigInt(participant['y'])).reduce(Ecdsa.curve.pointAdd);
// Add secret shares
const x = allShares.map((participant) => hexToBigInt(participant['u'])).reduce(Ecdsa.curve.scalarAdd);
// Verify shares.
for (const share of nShares) {
if (share.v) {
try {
Ecdsa.shamir.verify(hexToBigInt(share.u), [hexToBigInt(share.y), hexToBigInt(share.v)], pShare.i);
} catch (err) {
throw new Error(`Could not verify share from participant ${share.j}. Verification error: ${err}`);
}
}
}
// Generate Schnorr proof of knowledge of the discrete log of X = xG.
const X = Ecdsa.curve.basePointMult(x);
const proofContext = createHash('sha256').update(bigIntToBufferBE(y, Ecdsa.curve.pointBytes)).digest();
const schnorrProofX = Schnorr.createSchnorrProof(X, x, Ecdsa.curve, proofContext);
// Chaincode will be used in future when we add support for key derivation for ecdsa
const chaincodes = [pShare, ...nShares].map(({ chaincode }) => bigIntFromBufferBE(Buffer.from(chaincode, 'hex')));
const chaincode = chaincodes.reduce(
(acc, chaincode) =>
(acc + chaincode) % BigInt('0x010000000000000000000000000000000000000000000000000000000000000000') // 2^256
);
const participants: KeyCombined = {
xShare: {
i: pShare.i,
l: pShare.l,
m: pShare.m,
n: pShare.n,
y: bigIntToBufferBE(y, 33).toString('hex'),
x: bigIntToBufferBE(x, 32).toString('hex'),
schnorrProofX: schnorrProofX,
chaincode: bigIntToBufferBE(chaincode, 32).toString('hex'),
},
yShares: {},
};
for (const share in nShares) {
const participantIndex = nShares[share]['j'];
participants.yShares[participantIndex] = {
i: pShare.i,
j: nShares[share]['j'],
n: nShares[share]['n'],
};
}
return participants;
}
/**
* Derive shares for a BIP-32 subkey.
* @param {PShare} The user's p-share.
* @param {NShare[]} The n-shares received from the other participants.
* @param {string} The BIP-32 path to derive.
* @returns {SubkeyShare} Returns the private x-share and n-shares to
* be distributed to participants at their corresponding index.
*/
keyDerive(pShare: PShare, nShares: NShare[], path: string): SubkeyShare {
const yValues = [pShare, ...nShares].map((share) => hexToBigInt(share.y));
const y = yValues.reduce((partial, share) => Ecdsa.curve.pointAdd(partial, share));
const u = BigInt(pShare.uu);
let contribChaincode = hexToBigInt(pShare.chaincode);
const chaincodes = [contribChaincode, ...nShares.map(({ chaincode }) => hexToBigInt(chaincode))];
const chaincode = chaincodes.reduce((acc, chaincode) => (acc + chaincode) % chaincodeBase);
// Verify shares.
for (const share of nShares) {
if (share.v) {
try {
Ecdsa.shamir.verify(hexToBigInt(share.u), [hexToBigInt(share.y), hexToBigInt(share.v)], pShare.i);
} catch (err) {
throw new Error(`Could not verify share from participant ${share.j}. Verification error: ${err}`);
}
}
}
// Derive subkey.
const subkey = Ecdsa.hdTree.privateDerive({ pk: y, sk: u, chaincode }, path);
// Calculate new public key contribution.
const contribY = Ecdsa.curve.basePointMult(subkey.sk);
// Calculate new chaincode contribution.
const chaincodeDelta = (chaincodeBase + subkey.chaincode - chaincode) % chaincodeBase;
contribChaincode = (contribChaincode + chaincodeDelta) % chaincodeBase;
// Calculate new u values.
const { shares: split_u, v } = Ecdsa.shamir.split(subkey.sk, pShare.t || 2, pShare.c || 3);
// Calculate new signing key.
const x = [split_u[pShare.i], ...nShares.map(({ u }) => hexToBigInt(u))].reduce(Ecdsa.curve.scalarAdd);
// Generate Schnorr proof of knowledge of the discrete log of X = xG.
const X = Ecdsa.curve.basePointMult(x);
const proofContext = createHash('sha256').update(bigIntToBufferBE(subkey.pk, Ecdsa.curve.pointBytes)).digest();
const schnorrProofX = Schnorr.createSchnorrProof(X, x, Ecdsa.curve, proofContext);
const P_i: XShare = {
i: pShare.i,
l: pShare.l,
m: pShare.m,
n: pShare.n,
y: bigIntToBufferBE(subkey.pk, 33).toString('hex'),
x: bigIntToBufferBE(x, 32).toString('hex'),
schnorrProofX: schnorrProofX,
chaincode: bigIntToBufferBE(subkey.chaincode, 32).toString('hex'),
};
const shares: SubkeyShare = {
xShare: P_i,
nShares: {},
};
for (let ind = 0; ind < nShares.length; ind++) {
const P_j = nShares[ind];
shares.nShares[P_j.j] = {
i: P_j.j,
j: P_i.i,
n: P_i.n,
u: bigIntToBufferBE(split_u[P_j.j], 32).toString('hex'),
y: bigIntToBufferBE(contribY, 32).toString('hex'),
v: bigIntToBufferBE(v[0], 32).toString('hex'),
chaincode: bigIntToBufferBE(contribChaincode, 32).toString('hex'),
};
}
return shares;
}
/**
* Verify Schnorr proof of knowledge of the discrete log of X_i = x_i * G.
* @param Y The combined public key.
* @param VSSs The VSS shares received from all participants.
* @param index The i of X_i.
* @param proof The schnorr proof.
* @returns True if it's a valid proof with regards to Y and VSSs.
*/
verifySchnorrProofX(Y: bigint, VSSs: bigint[][], index: number, proof: SchnorrProof): boolean {
if (index < 1 || index > VSSs.length) {
throw new Error('Invalid value supplied for index');
}
// Calculate X_i from public information.
let X_i = Y;
VSSs.forEach((VSS) => {
VSS.forEach((v) => {
X_i = Ecdsa.curve.pointAdd(X_i, Ecdsa.curve.pointMultiply(v, BigInt(index)));
});
});
const proofContext = createHash('sha256').update(bigIntToBufferBE(Y, Ecdsa.curve.pointBytes)).digest();
return Schnorr.verifySchnorrProof(X_i, proof, Ecdsa.curve, proofContext);
}
/**
* Derives a child common keychain from common keychain
*
* @param {commonKeychain} The common keychain as a hex string.
* @param {path} The BIP-32 path to derive.
* @return {string} The derived common keychain as a hex string.
*/
deriveUnhardened(commonKeychain: string, path: string): string {
if (Ecdsa.hdTree === undefined) {
throw new Error("Can't derive key without HDTree implementation");
}
const keychain = Buffer.from(commonKeychain, 'hex');
const derivedPublicKeychain = Ecdsa.hdTree.publicDerive(
{
pk: bigIntFromBufferBE(keychain.slice(0, 33)),
chaincode: bigIntFromBufferBE(keychain.slice(33)),
},
path
);
const derivedPk = bigIntToBufferBE(derivedPublicKeychain.pk, 33).toString('hex');
const derivedChaincode = bigIntToBufferBE(derivedPublicKeychain.chaincode, 32).toString('hex');
return derivedPk + derivedChaincode;
}
/**
* Appends a given range proof challenge to the shares previously created
* by #keyCombine. Generates a new challenge if not provided.
* @param {XShare | YShare} share Private xShare or yShare of the signing operation
* @param rangeProofChallenge - challenge generated via generateNtilde
* @param paillierProofChallenge
* @returns {KeyCombined} The share with amended challenge values
*/
appendChallenge<T>(
share: T,
rangeProofChallenge: EcdsaTypes.SerializedNtilde,
paillierProofChallenge: EcdsaTypes.SerializedPaillierChallenge
): T & EcdsaTypes.SerializedEcdsaChallenges {
const { ntilde, h1, h2 } = rangeProofChallenge;
return {
...share,
ntilde,
h1,
h2,
p: paillierProofChallenge.p,
};
}
/**
* Create signing shares.
* @param {xShare} xShare Private xShare of current participant signer
* @param {YShare} yShare yShare corresponding to the other participant signer
* @returns {SignShareRT} Returns the participant private w-share
* and k-share to be distributed to other participant signer
*/
async signShare(xShare: XShareWithChallenges, yShare: YShareWithChallenges): Promise<SignShareRT> {
const pk = getPaillierPublicKey(hexToBigInt(xShare.n));
const k = Ecdsa.curve.scalarRandom();
const rk = await randomPositiveCoPrimeTo(pk.n);
const ck = pk.encrypt(k, rk);
const gamma = Ecdsa.curve.scalarRandom();
const d = Ecdsa.curve.scalarMult(Ecdsa.curve.scalarSub(BigInt(yShare.j), BigInt(xShare.i)), BigInt(xShare.i));
const w = [
Ecdsa.curve.scalarMult(BigInt(yShare.j), BigInt(xShare.i)),
hexToBigInt(xShare['x']),
Ecdsa.curve.scalarInvert(d),
].reduce(Ecdsa.curve.scalarMult);
const { ntilde: ntildea, h1: h1a, h2: h2a } = xShare;
const wShare: WShare = {
i: xShare.i,
l: xShare.l,
m: xShare.m,
n: xShare.n,
y: xShare.y,
ntilde: ntildea,
h1: h1a,
h2: h2a,
p: xShare.p,
k: bigIntToBufferBE(k, 32).toString('hex'),
ck: bigIntToBufferBE(ck, 768).toString('hex'),
w: bigIntToBufferBE(w, 32).toString('hex'),
gamma: bigIntToBufferBE(gamma, 32).toString('hex'),
};
const { ntilde: ntildeb, h1: h1b, h2: h2b } = yShare;
const proof = await EcdsaRangeProof.prove(
Ecdsa.curve,
minModulusBitLength,
pk,
{
ntilde: hexToBigInt(ntildeb),
h1: hexToBigInt(h1b),
h2: hexToBigInt(h2b),
},
ck,
k,
rk
);
// create paillier challenge proof based on the other signers challenge
// only send sigma if we also send challenge p
const sigma = EcdsaPaillierProof.prove(
hexToBigInt(xShare.n),
hexToBigInt(xShare.l),
EcdsaTypes.deserializePaillierChallenge({ p: yShare.p }).p
);
const proofShare = {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
u: bigIntToBufferBE(proof.u, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
};
const kShare: KShare = {
// this share will be sent to the other participant,
// so we need to swap the i and j values here
// so that they know it's their kShare, produced by us
i: yShare.j,
j: xShare.i,
n: pk.n.toString(16),
ntilde: ntildea,
h1: h1a,
h2: h2a,
p: xShare.p,
k: bigIntToBufferBE(ck, 768).toString('hex'),
sigma: EcdsaTypes.serializePaillierChallengeProofs({ sigma: sigma }).sigma,
proof: proofShare,
};
return {
wShare,
kShare,
};
}
/**
* Perform multiplicitive-to-additive (MtA) share conversion with another signer.
* Connection 1.2 in https://lucid.app/lucidchart/7061785b-bc5c-4002-b546-3f4a3612fc62/edit?page=IAVmvYO4FvKc#
* If signer A completed signShare initially (input to this fn), then this step is completed by signer B.
* @param {SignConvert} shares
* @returns {SignConvertRT}
*/
async signConvertStep1(shares: SignConvertStep1): Promise<SignConvertStep1Response> {
const receivedKShare = shares.kShare;
const xShare = shares.xShare; // currentParticipant secret xShare
const yShare: YShareWithChallenges = {
...shares.yShare,
ntilde: receivedKShare.ntilde,
h1: receivedKShare.h1,
h2: receivedKShare.h2,
p: receivedKShare.p,
};
const signShare = await this.signShare(xShare, yShare);
const shareParticipant = signShare.wShare;
if (shareParticipant.i !== receivedKShare.i) {
throw new Error('Shares from same participant');
}
if (!receivedKShare.proof) {
throw new Error('Unexpected missing proof on aShareToBeSent');
}
// the other participants paillier public key
const n = hexToBigInt(receivedKShare.n);
const pka = getPaillierPublicKey(n);
// the other participant's range proof challenge
const ntildea = hexToBigInt(receivedKShare.ntilde);
const h1a = hexToBigInt(receivedKShare.h1);
const h2a = hexToBigInt(receivedKShare.h2);
// the current participant's range proof challenge
const ntildeb = hexToBigInt(shareParticipant.ntilde);
const h1b = hexToBigInt(shareParticipant.h1);
const h2b = hexToBigInt(shareParticipant.h2);
const k = hexToBigInt(receivedKShare.k);
// the current participants paillier proof challenge
const shareParticipantPaillierChallenge = EcdsaTypes.deserializePaillierChallenge({ p: shareParticipant.p });
// the other signing parties proof to the current participants paillier proof challenge
const receivedPaillierChallengeProof = EcdsaTypes.deserializePaillierChallengeProofs({
sigma: receivedKShare.sigma,
});
if (
!(await EcdsaPaillierProof.verify(n, shareParticipantPaillierChallenge.p, receivedPaillierChallengeProof.sigma))
) {
throw new Error('Could not verify signing A share paillier proof');
}
if (
!EcdsaRangeProof.verify(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildeb,
h1: h1b,
h2: h2b,
},
{
z: hexToBigInt(receivedKShare.proof.z),
u: hexToBigInt(receivedKShare.proof.u),
w: hexToBigInt(receivedKShare.proof.w),
s: hexToBigInt(receivedKShare.proof.s),
s1: hexToBigInt(receivedKShare.proof.s1),
s2: hexToBigInt(receivedKShare.proof.s2),
},
k
)
) {
throw new Error('Could not verify signing A share proof');
}
// MtA $k_j, \gamma_i$.
const beta0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
const beta = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(beta0)), 32).toString('hex');
const g = hexToBigInt(shareParticipant.gamma);
const rb = await randomPositiveCoPrimeTo(pka.n);
const cb = pka.encrypt(beta0, rb);
const alpha = pka.addition(pka.multiply(k, g), cb);
const alphaToBeSent = bigIntToBufferBE(alpha, ALPHAMUSIZE).toString('hex');
// Prove $\gamma_i \in Z_{N^2}$.
const gx = Ecdsa.curve.basePointMult(g);
let proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
k,
alpha,
g,
beta0,
rb,
gx
);
const gammaProofToBeSent: RangeProofWithCheckShare = {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(gx, 33).toString('hex'),
};
// MtA $k_j, w_i$.
const nu0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
const nu = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(nu0)), 32).toString('hex');
const w = hexToBigInt(shareParticipant.w);
const rn = await randomPositiveCoPrimeTo(pka.n);
const cn = pka.encrypt(nu0, rn);
const mu = pka.addition(pka.multiply(k, w), cn);
const muToBeSent = bigIntToBufferBE(mu, ALPHAMUSIZE).toString('hex');
// Prove $\w_i \in Z_{N^2}$.
const wx = Ecdsa.curve.basePointMult(w);
proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
k,
hexToBigInt(muToBeSent),
w,
nu0,
rn,
wx
);
const wProofToBeSent: RangeProofWithCheckShare = {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(wx, 33).toString('hex'),
};
const nToBeSent = signShare.kShare.n;
const ntildeToBeSent = bigIntToBufferBE(ntildeb, 384).toString('hex');
const h1ToBeSent = bigIntToBufferBE(h1b, 384).toString('hex');
const h2ToBeSent = bigIntToBufferBE(h2b, 384).toString('hex');
const kToBeSent = signShare.kShare.k;
const proofToBeSent = signShare.kShare.proof;
const [iToBeSent, jToBeSent] = [receivedKShare.j, receivedKShare.i];
return {
aShare: {
i: iToBeSent,
j: jToBeSent,
ntilde: ntildeToBeSent,
h1: h1ToBeSent,
h2: h2ToBeSent,
n: nToBeSent,
k: kToBeSent,
alpha: alphaToBeSent,
mu: muToBeSent,
proof: proofToBeSent,
gammaProof: gammaProofToBeSent,
wProof: wProofToBeSent,
// provide the share participants proof
// to the paillier challenge in the receivedKShare from the other signer
sigma: signShare.kShare.sigma,
},
bShare: {
...shareParticipant,
beta,
nu,
},
};
}
/**
* Perform multiplicitive-to-additive (MtA) share conversion with another
* signer.
* Connection 2.1 in https://lucid.app/lucidchart/7061785b-bc5c-4002-b546-3f4a3612fc62/edit?page=IAVmvYO4FvKc#
* If signer B completed signConvertStep1, then this step is completed by signer A.
* @param {SignConvert} shares
* @returns {SignConvertRT}
*/
async signConvertStep2(shares: SignConvertStep2): Promise<SignConvertStep2Response> {
const receivedAShare = shares.aShare;
if (!receivedAShare.gammaProof) {
throw new Error('Unexpected missing gammaProof on aShareToBeSent');
}
if (!receivedAShare.wProof) {
throw new Error('Unexpected missing wProof on aShareToBeSent');
}
const n = hexToBigInt(receivedAShare.n); // Paillier pub from other signer
// current participant public key
const pka = getPaillierPublicKey(hexToBigInt(shares.wShare.n));
const ntildea = hexToBigInt(shares.wShare.ntilde);
const h1a = hexToBigInt(shares.wShare.h1);
const h2a = hexToBigInt(shares.wShare.h2);
const ck = hexToBigInt(shares.wShare.ck);
const shareParticipantPaillierChallenge = EcdsaTypes.deserializePaillierChallenge({ p: shares.wShare.p });
const receivedPaillierChallengeProof = EcdsaTypes.deserializePaillierChallengeProofs({
sigma: shares.aShare.sigma,
});
if (!EcdsaPaillierProof.verify(n, shareParticipantPaillierChallenge.p, receivedPaillierChallengeProof.sigma)) {
throw new Error('could not verify signing share for paillier proof');
}
// Verify $\gamma_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(receivedAShare.gammaProof.z),
zprm: hexToBigInt(receivedAShare.gammaProof.zprm),
t: hexToBigInt(receivedAShare.gammaProof.t),
v: hexToBigInt(receivedAShare.gammaProof.v),
w: hexToBigInt(receivedAShare.gammaProof.w),
s: hexToBigInt(receivedAShare.gammaProof.s),
s1: hexToBigInt(receivedAShare.gammaProof.s1),
s2: hexToBigInt(receivedAShare.gammaProof.s2),
t1: hexToBigInt(receivedAShare.gammaProof.t1),
t2: hexToBigInt(receivedAShare.gammaProof.t2),
u: hexToBigInt(receivedAShare.gammaProof.u),
},
ck,
hexToBigInt(receivedAShare.alpha),
hexToBigInt(receivedAShare.gammaProof.x)
)
) {
throw new Error('could not verify signing share for gamma proof');
}
// Verify $\w_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(receivedAShare.wProof.z),
zprm: hexToBigInt(receivedAShare.wProof.zprm),
t: hexToBigInt(receivedAShare.wProof.t),
v: hexToBigInt(receivedAShare.wProof.v),
w: hexToBigInt(receivedAShare.wProof.w),
s: hexToBigInt(receivedAShare.wProof.s),
s1: hexToBigInt(receivedAShare.wProof.s1),
s2: hexToBigInt(receivedAShare.wProof.s2),
t1: hexToBigInt(receivedAShare.wProof.t1),
t2: hexToBigInt(receivedAShare.wProof.t2),
u: hexToBigInt(receivedAShare.wProof.u),
},
ck,
hexToBigInt(receivedAShare.mu),
hexToBigInt(receivedAShare.wProof.x)
)
) {
throw new Error('could not verify share for wProof');
}
const sk = new paillierBigint.PrivateKey(hexToBigInt(shares.wShare.l), hexToBigInt(shares.wShare.m), pka);
const gShareAlpha = bigIntToBufferBE(
Ecdsa.curve.scalarReduce(sk.decrypt(hexToBigInt(receivedAShare.alpha))),
32
).toString('hex');
const gShareMu = bigIntToBufferBE(
Ecdsa.curve.scalarReduce(sk.decrypt(hexToBigInt(receivedAShare.mu))), // recheck encrypted number
32
).toString('hex');
if (!receivedAShare.proof) {
throw new Error('Unexpected missing proof on aShareToBeSent');
}
const pkb = getPaillierPublicKey(n);
const ntildeb = hexToBigInt(receivedAShare.ntilde);
const h1b = hexToBigInt(receivedAShare.h1);
const h2b = hexToBigInt(receivedAShare.h2);
const k = hexToBigInt(receivedAShare.k);
if (
!EcdsaRangeProof.verify(
Ecdsa.curve,
minModulusBitLength,
pkb,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(receivedAShare.proof.z),
u: hexToBigInt(receivedAShare.proof.u),
w: hexToBigInt(receivedAShare.proof.w),
s: hexToBigInt(receivedAShare.proof.s),
s1: hexToBigInt(receivedAShare.proof.s1),
s2: hexToBigInt(receivedAShare.proof.s2),
},
k
)
) {
throw new Error('Could not verify signing A share proof');
}
// MtA $k_j, \gamma_i$.
const beta0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
const gShareBeta = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(beta0)), 32).toString('hex');
const g = hexToBigInt(shares.wShare.gamma);
const rb = await randomPositiveCoPrimeTo(pkb.n);
const cb = pkb.encrypt(beta0, rb);
const alpha = pkb.addition(pkb.multiply(k, g), cb);
const alphaToBeSent = bigIntToBufferBE(alpha, ALPHAMUSIZE).toString('hex');
// Prove $\gamma_i \in Z_{N^2}$.
const gx = Ecdsa.curve.basePointMult(g);
let proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pkb,
{
ntilde: ntildeb,
h1: h1b,
h2: h2b,
},
k,
alpha,
g,
beta0,
rb,
gx
);
const gammaProofToBeSent: RangeProofWithCheckShare = {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(gx, 33).toString('hex'),
};
// MtA $k_j, w_i$.
const nu0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
const gShareNu = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(nu0)), 32).toString('hex');
const w = hexToBigInt(shares.wShare.w);
const rn = await randomPositiveCoPrimeTo(pkb.n);
const cn = pkb.encrypt(nu0, rn);
const mu = pkb.addition(pkb.multiply(k, w), cn);
const muToBeSent = bigIntToBufferBE(mu, ALPHAMUSIZE).toString('hex');
// Prove $\w_i \in Z_{N^2}$.
const wx = Ecdsa.curve.basePointMult(w);
proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pkb,
{
ntilde: ntildeb,
h1: h1b,
h2: h2b,
},
k,
hexToBigInt(muToBeSent),
w,
nu0,
rn,
wx
);
const wProofToBeSent: RangeProofWithCheckShare = {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(wx, 33).toString('hex'),
};
const [iToBeSent, jToBeSent] = [receivedAShare.j, receivedAShare.i];
return {
muShare: {
i: iToBeSent,
j: jToBeSent,
alpha: alphaToBeSent,
mu: muToBeSent,
gammaProof: gammaProofToBeSent,
wProof: wProofToBeSent,
},
gShare: {
i: shares.wShare.i,
n: shares.wShare.n,
y: shares.wShare.y,
k: shares.wShare.k,
w: shares.wShare.w,
gamma: shares.wShare.gamma,
alpha: gShareAlpha,
mu: gShareMu,
beta: gShareBeta,
nu: gShareNu,
},
};
}
/**
* Perform multiplicitive-to-additive (MtA) share conversion with another signer.
* Connection 2.2 in https://lucid.app/lucidchart/7061785b-bc5c-4002-b546-3f4a3612fc62/edit?page=IAVmvYO4FvKc#
* If signer A completed signConvertStep2, then this step is completed by signer B.
* @param {SignConvert} shares
* @returns {SignConvertRT}
*/
async signConvertStep3(shares: SignConvertStep3): Promise<SignConvertStep3Response> {
const receivedMuShare = shares.muShare;
if (!receivedMuShare.gammaProof) {
throw new Error('Unexpected missing gammaProof on aShareToBeSent');
}
if (!receivedMuShare.wProof) {
throw new Error('Unexpected missing wProof on aShareToBeSent');
}
const pka = getPaillierPublicKey(hexToBigInt(shares.bShare.n));
const ntildea = hexToBigInt(shares.bShare.ntilde);
const h1a = hexToBigInt(shares.bShare.h1);
const h2a = hexToBigInt(shares.bShare.h2);
const ck = hexToBigInt(shares.bShare.ck);
// Verify $\gamma_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(receivedMuShare.gammaProof.z),
zprm: hexToBigInt(receivedMuShare.gammaProof.zprm),
t: hexToBigInt(receivedMuShare.gammaProof.t),
v: hexToBigInt(receivedMuShare.gammaProof.v),
w: hexToBigInt(receivedMuShare.gammaProof.w),
s: hexToBigInt(receivedMuShare.gammaProof.s),
s1: hexToBigInt(receivedMuShare.gammaProof.s1),
s2: hexToBigInt(receivedMuShare.gammaProof.s2),
t1: hexToBigInt(receivedMuShare.gammaProof.t1),
t2: hexToBigInt(receivedMuShare.gammaProof.t2),
u: hexToBigInt(receivedMuShare.gammaProof.u),
},
ck,
hexToBigInt(receivedMuShare.alpha),
hexToBigInt(receivedMuShare.gammaProof.x)
)
) {
throw new Error('could not verify signing share for gamma proof');
}
// Verify $\w_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(receivedMuShare.wProof.z),
zprm: hexToBigInt(receivedMuShare.wProof.zprm),
t: hexToBigInt(receivedMuShare.wProof.t),
v: hexToBigInt(receivedMuShare.wProof.v),
w: hexToBigInt(receivedMuShare.wProof.w),
s: hexToBigInt(receivedMuShare.wProof.s),
s1: hexToBigInt(receivedMuShare.wProof.s1),
s2: hexToBigInt(receivedMuShare.wProof.s2),
t1: hexToBigInt(receivedMuShare.wProof.t1),
t2: hexToBigInt(receivedMuShare.wProof.t2),
u: hexToBigInt(receivedMuShare.wProof.u),
},
ck,
hexToBigInt(receivedMuShare.mu),
hexToBigInt(receivedMuShare.wProof.x)
)
) {
throw new Error('could not verify share for wProof');
}
const sk = new paillierBigint.PrivateKey(hexToBigInt(shares.bShare.l), hexToBigInt(shares.bShare.m), pka);
const alpha = sk.decrypt(hexToBigInt(receivedMuShare.alpha));
const gShareAlpha = bigIntToBufferBE(Ecdsa.curve.scalarReduce(alpha), 32).toString('hex');
const mu = sk.decrypt(hexToBigInt(receivedMuShare.mu as string)); // recheck encrypted number
const gShareMu = bigIntToBufferBE(Ecdsa.curve.scalarReduce(mu), 32).toString('hex');
const [iToBeSent, jToBeSent] = [receivedMuShare.j, receivedMuShare.i];
return {
gShare: {
i: shares.bShare.i,
n: shares.bShare.n,
y: shares.bShare.y,
k: shares.bShare.k,
w: shares.bShare.w,
gamma: shares.bShare.gamma,
alpha: gShareAlpha,
mu: gShareMu,
beta: shares.bShare.beta,
nu: shares.bShare.nu,
},
signIndex: {
i: iToBeSent,
j: jToBeSent,
},
};
}
/**
* Perform multiplicitive-to-additive (MtA) share conversion with another signer.
* @deprecated - use one of [signConvertStep1, signConvertStep2, signConvertStep3] instead
* @param {SignConvert} shares
* @returns {SignConvertRT}
*/
async signConvert(shares: SignConvert): Promise<SignConvertRT> {
let shareParticipant: Partial<BShare> | Partial<GShare>, shareToBeSent: Partial<AShare> | MUShare;
let isGammaShare = false;
let kShare: Partial<KShare> = {};
if (shares.xShare && shares.yShare && shares.kShare) {
const xShare = shares.xShare; // currentParticipant secret xShare
const yShare: YShareWithChallenges = {
...shares.yShare,
ntilde: shares.kShare.ntilde,
h1: shares.kShare.h1,
h2: shares.kShare.h2,
p: shares.kShare.p,
};
const signShare = await this.signShare(xShare, yShare);
kShare = signShare.kShare;
shareToBeSent = { ...shares.kShare } as Partial<AShare>;
shareParticipant = { ...signShare.wShare } as Partial<BShare>;
} else if ((shares.bShare && shares.muShare) || (shares.aShare && shares.wShare)) {
isGammaShare = true;
shareToBeSent = shares.aShare ? ({ ...shares.aShare } as MUShare) : ({ ...shares.muShare } as MUShare);
shareParticipant = shares.wShare ? ({ ...shares.wShare } as Partial<GShare>) : ({ ...shares.bShare } as GShare);
} else {
throw new Error('Invalid config for Sign Convert');
}
if (shareParticipant.i !== shareToBeSent.i) {
throw new Error('Shares from same participant');
}
if ((shareToBeSent as AShare).alpha) {
const bShareParticipant = shareParticipant as BShare;
const aShareToBeSent = shareToBeSent as AShare;
if (!aShareToBeSent.gammaProof) {
throw new Error('Unexpected missing gammaProof on aShareToBeSent');
}
if (!aShareToBeSent.wProof) {
throw new Error('Unexpected missing wProof on aShareToBeSent');
}
const pka = getPaillierPublicKey(hexToBigInt(bShareParticipant.n));
let ntildea, h1a, h2a, ck;
if (bShareParticipant.ntilde) {
ntildea = hexToBigInt(bShareParticipant.ntilde);
h1a = hexToBigInt(bShareParticipant.h1);
h2a = hexToBigInt(bShareParticipant.h2);
ck = hexToBigInt(bShareParticipant.ck);
}
// Verify $\gamma_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(aShareToBeSent.gammaProof.z),
zprm: hexToBigInt(aShareToBeSent.gammaProof.zprm),
t: hexToBigInt(aShareToBeSent.gammaProof.t),
v: hexToBigInt(aShareToBeSent.gammaProof.v),
w: hexToBigInt(aShareToBeSent.gammaProof.w),
s: hexToBigInt(aShareToBeSent.gammaProof.s),
s1: hexToBigInt(aShareToBeSent.gammaProof.s1),
s2: hexToBigInt(aShareToBeSent.gammaProof.s2),
t1: hexToBigInt(aShareToBeSent.gammaProof.t1),
t2: hexToBigInt(aShareToBeSent.gammaProof.t2),
u: hexToBigInt(aShareToBeSent.gammaProof.u),
},
ck,
hexToBigInt(aShareToBeSent.alpha),
hexToBigInt(aShareToBeSent.gammaProof.x)
)
) {
throw new Error('could not verify signing share for gamma proof');
}
// Verify $\w_i \in Z_{N^2}$.
if (
!EcdsaRangeProof.verifyWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
{
z: hexToBigInt(aShareToBeSent.wProof.z),
zprm: hexToBigInt(aShareToBeSent.wProof.zprm),
t: hexToBigInt(aShareToBeSent.wProof.t),
v: hexToBigInt(aShareToBeSent.wProof.v),
w: hexToBigInt(aShareToBeSent.wProof.w),
s: hexToBigInt(aShareToBeSent.wProof.s),
s1: hexToBigInt(aShareToBeSent.wProof.s1),
s2: hexToBigInt(aShareToBeSent.wProof.s2),
t1: hexToBigInt(aShareToBeSent.wProof.t1),
t2: hexToBigInt(aShareToBeSent.wProof.t2),
u: hexToBigInt(aShareToBeSent.wProof.u),
},
ck,
hexToBigInt(aShareToBeSent.mu),
hexToBigInt(aShareToBeSent.wProof.x)
)
) {
throw new Error('could not verify share for wProof');
}
const sk = new paillierBigint.PrivateKey(
hexToBigInt(bShareParticipant.l as string),
hexToBigInt(bShareParticipant.m as string),
pka
);
const gShareParticipant = shareParticipant as GShare;
const muShareToBeSent = shareToBeSent as MUShare;
const alpha = sk.decrypt(hexToBigInt(aShareToBeSent.alpha));
gShareParticipant.alpha = bigIntToBufferBE(Ecdsa.curve.scalarReduce(alpha), 32).toString('hex');
const mu = sk.decrypt(hexToBigInt(aShareToBeSent.mu as string)); // recheck encrypted number
gShareParticipant.mu = bigIntToBufferBE(Ecdsa.curve.scalarReduce(mu), 32).toString('hex');
const partialShareParticipant = shareParticipant as Partial<GShare>;
const partialShareToBeSent = muShareToBeSent as Partial<MUShare>;
delete partialShareParticipant.l;
delete partialShareParticipant.m;
delete partialShareToBeSent.alpha;
delete partialShareToBeSent.mu;
}
if ((shareToBeSent as AShare).k) {
const bShareParticipant = shareParticipant as BShare;
const aShareToBeSent = shareToBeSent as AShare;
if (!aShareToBeSent.proof) {
throw new Error('Unexpected missing proof on aShareToBeSent');
}
const n = hexToBigInt(aShareToBeSent.n); // Paillier pub from other signer
const pka = getPaillierPublicKey(n);
const ntildea = hexToBigInt(aShareToBeSent.ntilde);
const h1a = hexToBigInt(aShareToBeSent.h1);
const h2a = hexToBigInt(aShareToBeSent.h2);
const ntildeb = hexToBigInt(bShareParticipant.ntilde);
const h1b = hexToBigInt(bShareParticipant.h1);
const h2b = hexToBigInt(bShareParticipant.h2);
const k = hexToBigInt(aShareToBeSent.k);
if (
!EcdsaRangeProof.verify(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildeb,
h1: h1b,
h2: h2b,
},
{
z: hexToBigInt(aShareToBeSent.proof.z),
u: hexToBigInt(aShareToBeSent.proof.u),
w: hexToBigInt(aShareToBeSent.proof.w),
s: hexToBigInt(aShareToBeSent.proof.s),
s1: hexToBigInt(aShareToBeSent.proof.s1),
s2: hexToBigInt(aShareToBeSent.proof.s2),
},
k
)
) {
throw new Error('Could not verify signing A share proof');
}
// MtA $k_j, \gamma_i$.
const beta0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
bShareParticipant.beta = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(beta0)), 32).toString(
'hex'
);
const g = hexToBigInt(bShareParticipant.gamma);
const rb = await randomPositiveCoPrimeTo(pka.n);
const cb = pka.encrypt(beta0, rb);
const alpha = pka.addition(pka.multiply(k, g), cb);
aShareToBeSent.alpha = bigIntToBufferBE(alpha, 32).toString('hex');
// Prove $\gamma_i \in Z_{N^2}$.
const gx = Ecdsa.curve.basePointMult(g);
let proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
k,
alpha,
g,
beta0,
rb,
gx
);
Object.assign(aShareToBeSent, {
gammaProof: {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(gx, 33).toString('hex'),
},
});
// MtA $k_j, w_i$.
const nu0 = bigintCryptoUtils.randBetween(Ecdsa.curve.order() ** _5n);
shareParticipant.nu = bigIntToBufferBE(Ecdsa.curve.scalarNegate(Ecdsa.curve.scalarReduce(nu0)), 32).toString(
'hex'
);
const w = hexToBigInt(bShareParticipant.w);
const rn = await randomPositiveCoPrimeTo(pka.n);
const cn = pka.encrypt(nu0, rn);
const mu = pka.addition(pka.multiply(k, w), cn);
shareToBeSent.mu = bigIntToBufferBE(mu, 32).toString('hex');
// Prove $\w_i \in Z_{N^2}$.
const wx = Ecdsa.curve.basePointMult(w);
proof = await EcdsaRangeProof.proveWithCheck(
Ecdsa.curve,
minModulusBitLength,
pka,
{
ntilde: ntildea,
h1: h1a,
h2: h2a,
},
k,
hexToBigInt(aShareToBeSent.mu),
w,
nu0,
rn,
wx
);
Object.assign(shareToBeSent, {
wProof: {
z: bigIntToBufferBE(proof.z, 384).toString('hex'),
zprm: bigIntToBufferBE(proof.zprm, 384).toString('hex'),
t: bigIntToBufferBE(proof.t, 384).toString('hex'),
v: bigIntToBufferBE(proof.v, 768).toString('hex'),
w: bigIntToBufferBE(proof.w, 384).toString('hex'),
s: bigIntToBufferBE(proof.s, 384).toString('hex'),
s1: bigIntToBufferBE(proof.s1, 96).toString('hex'),
s2: bigIntToBufferBE(proof.s2, 480).toString('hex'),
t1: bigIntToBufferBE(proof.t1, 224).toString('hex'),
t2: bigIntToBufferBE(proof.t2, 480).toString('hex'),
u: bigIntToBufferBE(proof.u, 33).toString('hex'),
x: bigIntToBufferBE(wx, 33).toString('hex'),
},
});
if ((shareParticipant as GShare).alpha) {
const partialShareParticipant = shareParticipant as Partial<BShare>;
const partialShareToBeSent = shareToBeSent as Partial<AShare>;
delete partialShareParticipant.ntilde;
delete partialShareParticipant.h1;
delete partialShareParticipant.h2;
delete partialShareParticipant.ck;
delete partialShareToBeSent.n;
delete partialShareToBeSent.ntilde;
delete partialShareToBeSent.h1;
delete partialShareToBeSent.h2;
delete partialShareToBeSent.k;
delete partialShareToBeSent.proof;
} else {
Object.assign(shareToBeSent, {
n: kShare.n,
ntilde: bigIntToBufferBE(ntildeb, 384).toString('hex'),
h1: bigIntToBufferBE(h1b, 384).toString('hex'),
h2: bigIntToBufferBE(h2b, 384).toString('hex'),
k: kShare.k,
proof: kShare.proof,
});
}
}
if (!('alpha' in shareToBeSent) && !('k' in shareToBeSent)) {
const partialShareParticipant = shareParticipant as Partial<BShare>;
delete partialShareParticipant.ntilde;
delete partialShareParticipant.h1;
delete partialShareParticipant.h2;
delete partialShareParticipant.ck;
const muShareToBeSent = shareToBeSent as MUShare;
shareToBeSent = {
i: muShareToBeSent.i,
j: muShareToBeSent.j,
} as MUShare;
}
[shareToBeSent.i, shareToBeSent.j] = [shareToBeSent.j, shareToBeSent.i];
if (isGammaShare) {
return {
muShare: shareToBeSent as MUShare,
gShare: shareParticipant as GShare,
};
}
return {
aShare: shareToBeSent as AShare,
bShare: shareParticipant as BShare,
};
}
/**
* Combine gamma shares to get the private omicron / delta shares
* @param {SignCombine} shares
* @returns {SignCombineRT}
*/
signCombine(shares: SignCombine): SignCombineRT {
const gShare = shares.gShare;
const S = shares.signIndex;
const gamma = hexToBigInt(gShare.gamma);
const alpha = hexToBigInt(gShare.alpha);
const beta = hexToBigInt(gShare.beta);
const mu = hexToBigInt(gShare.mu);
const nu = hexToBigInt(gShare.nu);
const k = hexToBigInt(gShare.k);
const w = hexToBigInt(gShare.w);
const delta = Ecdsa.curve.scalarAdd(Ecdsa.curve.scalarMult(k, gamma), Ecdsa.curve.scalarAdd(alpha, beta));
const omicron = Ecdsa.curve.scalarAdd(Ecdsa.curve.scalarMult(k, w), Ecdsa.curve.scalarAdd(mu, nu));
const Gamma = Ecdsa.curve.basePointMult(gamma);
return {
oShare: {
i: gShare.i,
y: gShare.y,
k: bigIntToBufferBE(k, 32).toString('hex'),
omicron: bigIntToBufferBE(omicron, 32).toString('hex'),
delta: bigIntToBufferBE(delta, 32).toString('hex'),
Gamma: bigIntToBufferBE(Gamma, 33).toString('hex'),
},
dShare: {
i: S.i,
j: gShare.i,
delta: bigIntToBufferBE(delta, 32).toString('hex'),
Gamma: bigIntToBufferBE(Gamma, 33).toString('hex'),
},
};
}
/**
* Sign a message.
* @param {Buffer} M Message to be signed
* @param {OShare} oShare private omicron share of current participant
* @param {DShare} dShare delta share received from the other participant
* @param {Hash} hash hashing algorithm implementing Node`s standard crypto hash interface
* @param shouldHash if true, we hash the provided buffer before signing
* @returns {VAShare}
*/
sign(M: Buffer, oShare: OShare, dShare: DShare, hash?: Hash, shouldHash = true): VAShare {
const m = shouldHash ? (hash || createHash('sha256')).update(M).digest() : M;
const delta = Ecdsa.curve.scalarAdd(hexToBigInt(oShare.delta), hexToBigInt(dShare.delta));
const R = Ecdsa.curve.pointMultiply(
Ecdsa.curve.pointAdd(hexToBigInt(oShare.Gamma), hexToBigInt(dShare.Gamma)),
Ecdsa.curve.scalarInvert(delta)
);
const pointR = secp.ProjectivePoint.fromHex(bigIntToBufferBE(R, 32));
const r = pointR.x;
const s = Ecdsa.curve.scalarAdd(
Ecdsa.curve.scalarMult(bigIntFromU8ABE(m), hexToBigInt(oShare.k)),
Ecdsa.curve.scalarMult(r, hexToBigInt(oShare.omicron))
);
const l = Ecdsa.curve.scalarRandom();
const rho = Ecdsa.curve.scalarRandom();
const V = Ecdsa.curve.pointAdd(Ecdsa.curve.pointMultiply(R, s), Ecdsa.curve.basePointMult(l));
const A = Ecdsa.curve.basePointMult(rho);
const comDecom_V_A = HashCommitment.createCommitment(
Buffer.concat([bigIntToBufferBE(V, Ecdsa.curve.pointBytes), bigIntToBufferBE(A, Ecdsa.curve.pointBytes)])
);
return {
i: oShare.i,
y: oShare.y,
R: pointR.toHex(true),
s: bigIntToBufferBE(s, 32).toString('hex'),
m: m,
l: l,
rho: rho,
V: V,
A: A,
comDecomVA: comDecom_V_A,
};
}
/**
* Generate proofs of V_i and A_i values.
* @param {Buffer} M Message to commit to as part of the context of the proof.
* This doesn't need to be the same message that was signed in the sign function above.
* But it should be the same for all participants for the purpose of providing proof context.
* @param {VAShare} vaShare The VAShare to prove.
* @returns {VAShareWithProofs}
*/
generateVAProofs(M: Buffer, vaShare: VAShare): VAShareWithProofs {
const s = hexToBigInt(vaShare.s);
const R = bigIntFromU8ABE(secp.ProjectivePoint.fromHex(vaShare.R).toRawBytes(true));
const proofContext = createHash('sha256').update(M).update(bigIntToBufferBE(R, Ecdsa.curve.pointBytes)).digest();
const zkVProof = EcdsaZkVProof.createZkVProof(vaShare.V, s, vaShare.l, R, Ecdsa.curve, proofContext);
const schnorrProof = Schnorr.createSchnorrProof(vaShare.A, vaShare.rho, Ecdsa.curve, proofContext);
return {
...vaShare,
proofContext: proofContext,
zkVProofV: zkVProof,
schnorrProofA: schnorrProof,
};
}
/**
* Verify V_i and A_i values of all other participants during signing phase 5 steps 5A and 5B.
* @param {VAShareWithProofs} vaShare V_i, A_i info including SShare values of the currenct participant
* @param {PublicVAShareWithProofs[]} publicVAShares public V_i, A_i info of all other participants
* @returns {UTShare} U_i, T_i info of the current participant if all verifications pass
*/
verifyVAShares(vaShare: VAShareWithProofs, publicVAShares: PublicVAShareWithProofs[]): UTShare {
publicVAShares.forEach((publicVAShare) => {
if (
!HashCommitment.verifyCommitment(publicVAShare.comDecomVA.commitment, {
secret: Buffer.concat([
bigIntToBufferBE(publicVAShare.V, Ecdsa.curve.pointBytes),
bigIntToBufferBE(publicVAShare.A, Ecdsa.curve.pointBytes),
]),
blindingFactor: publicVAShare.comDecomVA.decommitment.blindingFactor,
})
) {
throw new Error('Could not verify commitment of V_i and A_i');
}
if (
!Schnorr.verifySchnorrProof(publicVAShare.A, publicVAShare.schnorrProofA, Ecdsa.curve, vaShare.proofContext)
) {
throw new Error('Could not verify Schnorr proof of A_i');
}
if (
!EcdsaZkVProof.verifyZkVProof(
publicVAShare.V,
publicVAShare.zkVProofV,
hexToBigInt(vaShare.R),
Ecdsa.curve,
vaShare.proofContext
)
) {
throw new Error('Could not verify ZK proof of V_i');
}
});
const y = hexToBigInt(vaShare.y);
// r is R's x coordinate. R is in compressed form, so we need to slice off the first byte.
const r = hexToBigInt(vaShare.R.slice(2));
// Calculate aggregation of all V_i and A_i.
let V = Ecdsa.curve.pointAdd(
Ecdsa.curve.pointAdd(
Ecdsa.curve.basePointMult(Ecdsa.curve.scalarNegate(bigIntFromU8ABE(vaShare.m))),
Ecdsa.curve.pointMultiply(y, Ecdsa.curve.scalarNegate(r))
),
vaShare.V
);
let A = vaShare.A;
publicVAShares.forEach((publicVAShare) => {
V = Ecdsa.curve.pointAdd(V, publicVAShare.V);
A = Ecdsa.curve.pointAdd(A, publicVAShare.A);
});
// Calculate U_i = rho_i * V and T_i = l_i * A.
const U = Ecdsa.curve.pointMultiply(V, vaShare.rho);
const T = Ecdsa.curve.pointMultiply(A, vaShare.l);
const comDecom_U_T = HashCommitment.createCommitment(
Buffer.concat([bigIntToBufferBE(U, Ecdsa.curve.pointBytes), bigIntToBufferBE(T, Ecdsa.curve.pointBytes)])
);
return {
...vaShare,
U,
T,
comDecomUT: comDecom_U_T,
};
}
/**
* Verify U_i and V_i values of all other participants during signing phase 5 steps 5C and 5D.
* @param {UTShare} utShare U_i, T_i info including SShare values of the currenct participant
* @param {PublicUTShare[]} publicUTShares public U_i, T_i info of all other participants
* @returns {SShare} SShare of the current participant if all verifications pass
*/
verifyUTShares(utShare: UTShare, publicUTShares: PublicUTShare[]): SShare {
let sigmaU = utShare.U;
let sigmaT = utShare.T;
publicUTShares.forEach((publicUTShare) => {
if (
!HashCommitment.verifyCommitment(publicUTShare.comDecomUT.commitment, {
secret: Buffer.concat([
bigIntToBufferBE(publicUTShare.U, Ecdsa.curve.pointBytes),
bigIntToBufferBE(publicUTShare.T, Ecdsa.curve.pointBytes),
]),
blindingFactor: publicUTShare.comDecomUT.decommitment.blindingFactor,
})
) {
throw new Error('Could not verify commitment of U_i and T_i');
}
sigmaU = Ecdsa.curve.pointAdd(sigmaU, publicUTShare.U);
sigmaT = Ecdsa.curve.pointAdd(sigmaT, publicUTShare.T);
});
if (sigmaU !== sigmaT) {
throw new Error('Sum of all U_i does not match sum of all T_i');
}
return { ...utShare };
}
/**
* Construct full signature by combining Sign Shares
* @param {SShare[]} shares
* @returns {Signature}
*/
constructSignature(shares: SShare[]): Signature {
// Every R must match.
const R = shares[0]['R'];
const isRMatching = shares.map((share) => share['R'] === R).reduce((a, b) => a && b);
if (!isRMatching) {
throw new Error('R value should be consistent across all shares');
}
let s = shares.map((share) => hexToBigInt(share['s'])).reduce(Ecdsa.curve.scalarAdd);
const recid = (R.slice(0, 2) === '03' ? 1 : 0) ^ (s > Ecdsa.curve.order() / BigInt(2) ? 1 : 0);
// Normalize s.
s = s > Ecdsa.curve.order() / BigInt(2) ? Ecdsa.curve.order() - s : s;
return {
y: shares[0]['y'],
r: R.slice(2),
s: bigIntToBufferBE(s, 32).toString('hex'),
recid: recid,
};
}
/**
* Verify ecdsa signatures
* @param {Buffer} message
* @param {Signature } signature
* @param {Hash} hash hashing algorithm implementing Node`s standard crypto hash interface
* @param {boolean} shouldHash if true, we hash the provided buffer before verifying
* @returns {boolean} True if signature is valid; False otherwise
*/
verify(message: Buffer, signature: Signature, hash?: Hash, shouldHash = true): boolean {
const messageToVerify = shouldHash ? (hash || createHash('sha256')).update(message).digest() : message;
return Ecdsa.curve.verify(
messageToVerify,
Buffer.concat([
Buffer.from([signature['recid']]),
bigIntToBufferBE(hexToBigInt(signature['r']), 32),
bigIntToBufferBE(hexToBigInt(signature['s']), 32),
]),
hexToBigInt(signature['y'])
);
}
/**
* Deserializes a challenge and it's proofs from hex strings to bigint
* @deprecated use sdk-lib-mpc EcdsaTypes.deserializeNtilde instead
*/
static deserializeNtilde(challenge: EcdsaTypes.SerializedNtilde): EcdsaTypes.DeserializedNtilde {
return EcdsaTypes.deserializeNtilde(challenge);
}
/**
* Serializes a challenge and it's proofs from big int to hex strings.
* @deprecated use sdk-lib-mpc EcdsaTypes.deserializeNtilde instead
* @param challenge
*/
static serializeNtilde(challenge: EcdsaTypes.DeserializedNtilde): EcdsaTypes.SerializedNtilde {
return EcdsaTypes.serializeNtilde(challenge);
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!