Skip to content

Commit

Permalink
Merge pull request #4462 from BitGo/retrofit-v1-v2-tss
Browse files Browse the repository at this point in the history
feat(sdk-lib-mpc): support mpcv1 to mpcv2 retrofit
  • Loading branch information
islamaminBitGo authored May 3, 2024
2 parents 971051e + 1438571 commit aa0ffce
Show file tree
Hide file tree
Showing 12 changed files with 469 additions and 35 deletions.
24 changes: 9 additions & 15 deletions modules/sdk-core/src/account-lib/mpc/tss/ecdsa/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export type SerializedNtilde = EcdsaTypes.SerializedNtilde;
* @deprecated use SerializedNtildeWithProofs from sdk-lib-mpc instead
*/
export type SerializedNtildeWithProofs = EcdsaTypes.SerializedNtildeWithProofs;
/**
* @deprecated use XShare from sdk-lib-mpc instead
*/
export type XShare = EcdsaTypes.XShare;

// Private share of the user generated during key generation
export type PShare = {
Expand Down Expand Up @@ -66,19 +70,9 @@ export type KeyShare = {
nShares: Record<number, NShare>;
};

// Private XShare of the current participant
export type XShare = {
i: number;
l: string;
m: string;
n: string;
y: string; // combined public key
x: string; // combined secret
schnorrProofX: SchnorrProof; // schnorr proof of knowledge of x
chaincode: string;
};

export type XShareWithChallenges = XShare & EcdsaTypes.SerializedNtilde & EcdsaTypes.SerializedPaillierChallenge;
export type XShareWithChallenges = EcdsaTypes.XShare &
EcdsaTypes.SerializedNtilde &
EcdsaTypes.SerializedPaillierChallenge;

// YShares used during signature generation
export type YShare = SignIndex & {
Expand All @@ -88,7 +82,7 @@ export type YShare = SignIndex & {
export type YShareWithChallenges = YShare & EcdsaTypes.SerializedNtilde & EcdsaTypes.SerializedPaillierChallenge;

export interface KeyCombined {
xShare: XShare;
xShare: EcdsaTypes.XShare;
yShares: Record<number, YShare>;
}

Expand All @@ -98,7 +92,7 @@ export type KeyCombinedWithNtilde = {
};

export type SubkeyShare = {
xShare: XShare;
xShare: EcdsaTypes.XShare;
nShares: Record<number, NShare>;
};

Expand Down
2 changes: 1 addition & 1 deletion modules/sdk-lib-mpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@wasmer/wasi": "^1.2.2",
"bigint-crypto-utils": "3.1.4",
"bigint-mod-arith": "3.1.2",
"cbor": "^9.0.1",
"cbor-x": "1.5.9",
"libsodium-wrappers-sumo": "^0.7.9",
"openpgp": "5.10.1",
"paillier-bigint": "3.3.0",
Expand Down
74 changes: 67 additions & 7 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/dkg.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { KeygenSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
import { DeserializedBroadcastMessage, DeserializedMessages, DkgState } from './types';
import { decode } from 'cbor';
import { DeserializedBroadcastMessage, DeserializedMessages, DkgState, RetrofitData } from './types';
import { decode, encode } from 'cbor-x';
import { bigIntToBufferBE } from '../../util';
import { Secp256k1Curve } from '../../curves';

export class Dkg {
protected dkgSession: KeygenSession | undefined;
protected dkgSessionBytes: Uint8Array;
protected dkgKeyShare: Keyshare;
protected keyShareBuff: Buffer;
protected n: number;
protected t: number;
protected chainCodeCommitment: Uint8Array | undefined;
protected partyIdx: number;
protected dkgState: DkgState = DkgState.Uninitialized;
protected dklsKeyShareRetrofitObject: Keyshare | undefined;
protected retrofitData: RetrofitData | undefined;

constructor(n: number, t: number, partyIdx: number) {
constructor(n: number, t: number, partyIdx: number, retrofitData?: RetrofitData) {
this.n = n;
this.t = t;
this.partyIdx = partyIdx;
this.chainCodeCommitment = undefined;
this.retrofitData = retrofitData;
}

private _restoreSession() {
Expand All @@ -25,6 +31,49 @@ export class Dkg {
}
}

private _createDKLsRetrofitKeyShare() {
if (this.retrofitData) {
if (!this.retrofitData.xShare.y || !this.retrofitData.xShare.chaincode || !this.retrofitData.xShare.x) {
throw Error('xShare must have a public key, private share value, and a chaincode.');
}
if (this.retrofitData.bigSiList.length !== this.n - 1) {
throw Error("bigSiList should contain the other parties' Si's");
}
const bigSList: Array<Array<number>> = [];
const xiList: Array<Array<number>> = [];
let j = 0;
for (let i = 0; i < this.n; i++) {
if (i === this.partyIdx) {
const secp256k1 = new Secp256k1Curve();
bigSList.push(
Array.from(bigIntToBufferBE(secp256k1.basePointMult(BigInt('0x' + this.retrofitData.xShare.x))))
);
} else {
bigSList.push(Array.from(Buffer.from(this.retrofitData.bigSiList[j], 'hex')));
j++;
}
xiList.push(Array.from(bigIntToBufferBE(BigInt(i + 1), 32)));
}
const dklsKeyShare = {
total_parties: this.n,
threshold: this.t,
rank_list: new Array(this.n).fill(0),
party_id: this.partyIdx,
public_key: Array.from(Buffer.from(this.retrofitData.xShare.y, 'hex')),
root_chain_code: Array.from(Buffer.from(this.retrofitData.xShare.chaincode, 'hex')),
final_session_id: Array(32).fill(0),
seed_ot_receivers: new Array(this.n - 1).fill(Array(32832).fill(0)),
seed_ot_senders: new Array(this.n - 1).fill(Array(32768).fill(0)),
sent_seed_list: [Array(32).fill(0)],
rec_seed_list: [Array(32).fill(0)],
s_i: Array.from(Buffer.from(this.retrofitData.xShare.x, 'hex')),
big_s_list: bigSList,
x_i_list: this.retrofitData.xiList ? this.retrofitData.xiList : xiList,
};
this.dklsKeyShareRetrofitObject = Keyshare.fromBytes(encode(dklsKeyShare));
}
}

private _deserializeState() {
if (!this.dkgSession) {
throw Error('Session not intialized');
Expand Down Expand Up @@ -63,7 +112,12 @@ export class Dkg {
const initDkls = require('@silencelaboratories/dkls-wasm-ll-web');
await initDkls.default();
}
this.dkgSession = new KeygenSession(this.n, this.t, this.partyIdx);
this._createDKLsRetrofitKeyShare();
if (this.dklsKeyShareRetrofitObject) {
this.dkgSession = KeygenSession.initKeyRotation(this.dklsKeyShareRetrofitObject);
} else {
this.dkgSession = new KeygenSession(this.n, this.t, this.partyIdx);
}
try {
const payload = this.dkgSession.createFirstMessage().payload;
this._deserializeState();
Expand All @@ -77,9 +131,10 @@ export class Dkg {
}

getKeyShare(): Buffer {
const keyShareBuff = Buffer.from(this.dkgKeyShare.toBytes());
this.dkgKeyShare.free();
return keyShareBuff;
if (!this.keyShareBuff) {
throw Error('Can not get key share, DKG is not complete yet.');
}
return this.keyShareBuff;
}

handleIncomingMessages(messagesForIthRound: DeserializedMessages): DeserializedMessages {
Expand Down Expand Up @@ -117,6 +172,11 @@ export class Dkg {
}
if (this.dkgState === DkgState.Round4) {
this.dkgKeyShare = this.dkgSession.keyshare();
if (this.dklsKeyShareRetrofitObject) {
this.dkgKeyShare.finishKeyRotation(this.dklsKeyShareRetrofitObject);
}
this.keyShareBuff = Buffer.from(this.dkgKeyShare.toBytes());
this.dkgKeyShare.free();
this.dkgState = DkgState.Complete;
return { broadcastMessages: [], p2pMessages: [] };
} else {
Expand Down
2 changes: 1 addition & 1 deletion modules/sdk-lib-mpc/src/tss/ecdsa-dkls/dsg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SignSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
import { DeserializedBroadcastMessage, DeserializedDklsSignature, DeserializedMessages, DsgState } from './types';
import { decode } from 'cbor';
import { decode } from 'cbor-x';

export class Dsg {
protected dsgSession: SignSession | undefined;
Expand Down
8 changes: 7 additions & 1 deletion modules/sdk-lib-mpc/src/tss/ecdsa-dkls/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from 'assert';
import { decode } from 'cbor';
import { decode } from 'cbor-x';
import { XShare } from '../ecdsa/types';

// Broadcast message meant to be sent to multiple parties
interface BroadcastMessage<T> {
Expand Down Expand Up @@ -52,6 +53,11 @@ export type DklsSignature<T> = {
R: T;
S: T;
};
export type RetrofitData = {
bigSiList: string[];
xShare: Partial<XShare>;
xiList?: string[];
};
export type SerializedBroadcastMessage = BroadcastMessage<string>;
export type DeserializedBroadcastMessage = BroadcastMessage<Uint8Array>;
export type SerializedP2PMessage = P2PMessage<string, string>;
Expand Down
2 changes: 1 addition & 1 deletion modules/sdk-lib-mpc/src/tss/ecdsa-dkls/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Signature } from '@noble/secp256k1';
import { HDTree, Secp256k1Bip32HdTree, Secp256k1Curve } from '../../curves';
import { bigIntFromBufferBE, bigIntToBufferBE } from '../../util';
import { DeserializedDklsSignature } from './types';
import { decode } from 'cbor';
import { decode } from 'cbor-x';
import * as secp256k1 from 'secp256k1';
import { Hash, createHash } from 'crypto';

Expand Down
13 changes: 13 additions & 0 deletions modules/sdk-lib-mpc/src/tss/ecdsa/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
// Ntilde Proof where both alpha and t are a set of 128 proofs each.
import { SchnorrProof } from '../../types';
import { bigIntToHex, convertBigIntArrToHexArr, convertHexArrToBigIntArr, hexToBigInt } from '../../util';

// Private XShare of the current participant
export type XShare = {
i: number;
l: string;
m: string;
n: string;
y: string; // combined public key
x: string; // combined secret
schnorrProofX: SchnorrProof; // schnorr proof of knowledge of x
chaincode: string;
};

interface NtildeProof<T> {
alpha: T[];
t: T[];
Expand Down
Loading

0 comments on commit aa0ffce

Please sign in to comment.