PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-lib-mpc/src/tss/ecdsa-dkls
Просмотр файла: util.ts
import { secp256k1 as secp } from '@noble/curves/secp256k1';
import { HDTree, Secp256k1Bip32HdTree, Secp256k1Curve } from '../../curves';
import { bigIntFromBufferBE, bigIntToBufferBE } from '../../util';
import { DeserializedDklsSignature, DeserializedMessages, RetrofitData } from './types';
import { decode } from 'cbor-x';
import * as secp256k1 from 'secp256k1';
import { createHash, Hash } from 'crypto';
import { Dsg } from './dsg';
import { Dkg } from './dkg';
import assert from 'assert';
const delimeter = ':';
/**
* Combines partial signatures from parties participating in DSG.
* @param round4MessagePayloads - round 4 message payloads from participating parties
* @param rHex - hex representation of the r value in the signature
* @returns {DeserializedMessages} - messages to send to other parties for the next round
*/
export function combinePartialSignatures(round4MessagePayloads: Uint8Array[], rHex: string): DeserializedDklsSignature {
const r = bigIntFromBufferBE(Buffer.from(rHex, 'hex').subarray(1));
const s0Arr = round4MessagePayloads.map((p) => decode(p).s_0);
const s1Arr = round4MessagePayloads.map((p) => decode(p).s_1);
const s0BigInts = s0Arr.map((s0) => bigIntFromBufferBE(Buffer.from(s0)));
const s1BigInts = s1Arr.map((s1) => bigIntFromBufferBE(Buffer.from(s1)));
const secp256k1Curve = new Secp256k1Curve();
const s0Sum = s0BigInts.slice(1).reduce((sumSoFar, s0) => secp256k1Curve.scalarAdd(sumSoFar, s0), s0BigInts[0]);
const s1Sum = s1BigInts.slice(1).reduce((sumSoFar, s1) => secp256k1Curve.scalarAdd(sumSoFar, s1), s1BigInts[0]);
const s = secp256k1Curve.scalarMult(s0Sum, secp256k1Curve.scalarInvert(s1Sum));
const sig = new secp.Signature(r, s);
const normalizedSig = sig.normalizeS();
return {
R: new Uint8Array(bigIntToBufferBE(normalizedSig.r, 32)),
S: new Uint8Array(bigIntToBufferBE(normalizedSig.s, 32)),
};
}
/**
* Verify a DKLs Signature and serialize it to recid:r:s:publickey format.
* @param message - message that was signed.
* @param dklsSignature - R and S values of the ECDSA signature.
* @param commonKeychain - public key appended to chaincode in hex.
* @param derivationPath - optional derivation path to derive on the commonkeychain before verification.
* @param hash - optional hash function to apply on message before verifying. Default is sha256.
* @param shouldHash - flag to determine whether message should be hashed before verifying.
* @returns {string} - serialized signature in `recid:r:s:publickey` format
*/
export function verifyAndConvertDklsSignature(
message: Buffer,
dklsSignature: DeserializedDklsSignature,
commonKeychain: string,
derivationPath?: string,
hash?: Hash,
shouldHash = true
): string {
let truePub = '';
if (derivationPath && derivationPath !== 'm') {
const hdTree: HDTree = new Secp256k1Bip32HdTree();
const derivedPub = hdTree.publicDerive(
{
pk: bigIntFromBufferBE(Buffer.from(commonKeychain.slice(0, 66), 'hex')),
chaincode: bigIntFromBufferBE(Buffer.from(commonKeychain.slice(66), 'hex')),
},
derivationPath
);
truePub = bigIntToBufferBE(derivedPub.pk).toString('hex');
} else {
truePub = commonKeychain.slice(0, 66);
}
const messageToVerify = shouldHash ? (hash || createHash('sha256')).update(message).digest() : message;
const pub0 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 0, messageToVerify, true);
const pub1 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 1, messageToVerify, true);
let recId: number;
if (truePub === Buffer.from(pub0).toString('hex')) {
recId = 0;
} else if (truePub === Buffer.from(pub1).toString('hex')) {
recId = 1;
} else {
throw Error('Invalid Signature');
}
return `${recId}${delimeter}${Buffer.from(dklsSignature.R).toString('hex')}${delimeter}${Buffer.from(
dklsSignature.S
).toString('hex')}${delimeter}${truePub}`;
}
export async function executeTillRound(
round: number,
party1Dsg: Dsg,
party2Dsg: Dsg
): Promise<DeserializedMessages[] | DeserializedDklsSignature> {
if (round < 1 || round > 5) {
throw Error('Invalid round number');
}
const party1Round1Message = await party1Dsg.init();
const party2Round1Message = await party2Dsg.init();
const party2Round2Messages = party2Dsg.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [party1Round1Message],
});
const party1Round2Messages = party1Dsg.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [party2Round1Message],
});
if (round === 2) return [party1Round2Messages, party2Round2Messages];
const party1Round3Messages = party1Dsg.handleIncomingMessages({
p2pMessages: party2Round2Messages.p2pMessages,
broadcastMessages: [],
});
const party2Round3Messages = party2Dsg.handleIncomingMessages({
p2pMessages: party1Round2Messages.p2pMessages,
broadcastMessages: [],
});
if (round === 3) return [party1Round3Messages, party2Round3Messages];
const party2Round4Messages = party2Dsg.handleIncomingMessages({
p2pMessages: party1Round3Messages.p2pMessages,
broadcastMessages: [],
});
const party1Round4Messages = party1Dsg.handleIncomingMessages({
p2pMessages: party2Round3Messages.p2pMessages,
broadcastMessages: [],
});
if (round === 4) return [party1Round4Messages, party2Round4Messages];
party1Dsg.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: party2Round4Messages.broadcastMessages,
});
party2Dsg.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: party1Round4Messages.broadcastMessages,
});
assert(Buffer.from(party1Dsg.signature.R).toString('hex') === Buffer.from(party2Dsg.signature.R).toString('hex'));
assert(Buffer.from(party1Dsg.signature.S).toString('hex') === Buffer.from(party2Dsg.signature.S).toString('hex'));
return party1Dsg.signature;
}
export async function generateDKGKeyShares(
retrofitDataA?: RetrofitData,
retrofitDataB?: RetrofitData,
retrofitDataC?: RetrofitData,
seedUser?: Buffer,
seedBackup?: Buffer,
seedBitgo?: Buffer
): Promise<[Dkg, Dkg, Dkg]> {
const user = new Dkg(3, 2, 0, seedUser, retrofitDataA);
const backup = new Dkg(3, 2, 1, seedBackup, retrofitDataB);
const bitgo = new Dkg(3, 2, 2, seedBitgo, retrofitDataC);
// #region round 1
const userRound1Message = await user.initDkg();
const backupRound1Message = await backup.initDkg();
const bitgoRound1Message = await bitgo.initDkg();
const bitgoRound2Messages = bitgo.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [userRound1Message, backupRound1Message],
});
// #endregion
// #region round 2
const userRound2Messages = user.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [bitgoRound1Message, backupRound1Message],
});
const backupRound2Messages = backup.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [userRound1Message, bitgoRound1Message],
});
const bitgoRound3Messages = bitgo.handleIncomingMessages({
p2pMessages: backupRound2Messages.p2pMessages
.filter((m) => m.to === 2)
.concat(userRound2Messages.p2pMessages.filter((m) => m.to === 2)),
broadcastMessages: [],
});
// #endregion
// #region round 3
const userRound3Messages = user.handleIncomingMessages({
p2pMessages: backupRound2Messages.p2pMessages
.filter((m) => m.to === 0)
.concat(bitgoRound2Messages.p2pMessages.filter((m) => m.to === 0)),
broadcastMessages: [],
});
const backupRound3Messages = backup.handleIncomingMessages({
p2pMessages: bitgoRound2Messages.p2pMessages
.filter((m) => m.to === 1)
.concat(userRound2Messages.p2pMessages.filter((m) => m.to === 1)),
broadcastMessages: [],
});
const userRound4Messages = user.handleIncomingMessages({
p2pMessages: backupRound3Messages.p2pMessages
.filter((m) => m.to === 0)
.concat(bitgoRound3Messages.p2pMessages.filter((m) => m.to === 0)),
broadcastMessages: [],
});
const backupRound4Messages = backup.handleIncomingMessages({
p2pMessages: bitgoRound3Messages.p2pMessages
.filter((m) => m.to === 1)
.concat(userRound3Messages.p2pMessages.filter((m) => m.to === 1)),
broadcastMessages: [],
});
const bitgoRound4Messages = bitgo.handleIncomingMessages({
p2pMessages: backupRound3Messages.p2pMessages
.filter((m) => m.to === 2)
.concat(userRound3Messages.p2pMessages.filter((m) => m.to === 2)),
broadcastMessages: [],
});
// #endregion
user.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: bitgoRound4Messages.broadcastMessages.concat(backupRound4Messages.broadcastMessages),
});
bitgo.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: backupRound4Messages.broadcastMessages.concat(userRound4Messages.broadcastMessages),
});
backup.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: bitgoRound4Messages.broadcastMessages.concat(userRound4Messages.broadcastMessages),
});
return [user, backup, bitgo];
}
export async function generate2of2KeyShares(
retrofitDataA?: RetrofitData,
retrofitDataB?: RetrofitData
): Promise<[Dkg, Dkg]> {
const partyA = new Dkg(2, 2, 0, undefined, retrofitDataA);
const partyB = new Dkg(2, 2, 1, undefined, retrofitDataB);
const partyARound1Message = await partyA.initDkg();
const partyBRound1Message = await partyB.initDkg();
const partyARound2Messages = partyA.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [partyBRound1Message],
});
const partyBRound2Messages = partyB.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: [partyARound1Message],
});
const partyARound3Messages = partyA.handleIncomingMessages({
p2pMessages: partyBRound2Messages.p2pMessages.filter((m) => m.to === 0),
broadcastMessages: [],
});
const partyBRound3Messages = partyB.handleIncomingMessages({
p2pMessages: partyARound2Messages.p2pMessages.filter((m) => m.to === 1),
broadcastMessages: [],
});
const partyARound4Messages = partyA.handleIncomingMessages({
p2pMessages: partyBRound3Messages.p2pMessages.filter((m) => m.to === 0),
broadcastMessages: [],
});
const partyBRound4Messages = partyB.handleIncomingMessages({
p2pMessages: partyARound3Messages.p2pMessages.filter((m) => m.to === 1),
broadcastMessages: [],
});
partyA.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: partyBRound4Messages.broadcastMessages,
});
partyB.handleIncomingMessages({
p2pMessages: [],
broadcastMessages: partyARound4Messages.broadcastMessages,
});
return [partyA, partyB];
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!