Skip to content

Commit

Permalink
feat(sdk-lib-mpc): combine partial signatures util support
Browse files Browse the repository at this point in the history
Ticket: HSM-341
  • Loading branch information
islamaminBitGo committed Apr 16, 2024
1 parent 23869a7 commit 4a8aa15
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 8 deletions.
20 changes: 20 additions & 0 deletions modules/sdk-core/src/account-lib/mpc/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,28 @@ import {
bigIntToBufferBE,
clamp,
getPaillierPublicKey,
DklsUtils,
DklsTypes,
} from '@bitgo/sdk-lib-mpc';

export function combineRound4DklsDsgMessages(
round4DsgMessages: DklsTypes.SerializedBroadcastMessage[]
): DklsTypes.SerializedDklsSignature {
const round4DsgMessagesDeser = round4DsgMessages.map(DklsTypes.deserializeBroadcastMessage);
const signatureR = round4DsgMessagesDeser.find((m) => m.signatureR !== undefined)?.signatureR;
if (!signatureR) {
throw Error('None of the round 4 Dkls messages contain a Signature.R value.');
}
const signatureDeser = DklsUtils.combinePartialSignatures(
round4DsgMessagesDeser.map((m) => m.payload),
Buffer.from(signatureR).toString('hex')
);
return {
R: Buffer.from(signatureDeser.R).toString('hex'),
S: Buffer.from(signatureDeser.S).toString('hex'),
};
}

/**
* @deprecated - use exported methods from @bitgo/sdk-lib-mpc instead
*/
Expand Down
7 changes: 4 additions & 3 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/dsg.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SignSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
import { DeserializedBroadcastMessage, DeserializedMessages, DklsSignature, DsgState } from './types';
import { DeserializedBroadcastMessage, DeserializedDklsSignature, DeserializedMessages, DsgState } from './types';
import { decode } from 'cbor';

export class Dsg {
protected dsgSession: SignSession | undefined;
protected dsgSessionBytes: Uint8Array;
private _signature: DklsSignature | undefined;
private _signature: DeserializedDklsSignature | undefined;
protected keyShare: Keyshare;
protected messageHash: Buffer;
protected derivationPath: string;
Expand Down Expand Up @@ -73,7 +73,7 @@ export class Dsg {
}
}

get signature(): DklsSignature {
get signature(): DeserializedDklsSignature {
if (!this._signature) {
throw Error('Can not request signature. Signature not produced yet.');
}
Expand Down Expand Up @@ -131,6 +131,7 @@ export class Dsg {
{
payload: nextRoundMessages[0].payload,
from: nextRoundMessages[0].from_id,
signatureR: decode(this.dsgSession.toBytes()).round.WaitMsg4.r,
},
],
p2pMessages: [],
Expand Down
1 change: 1 addition & 0 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * as DklsDkg from './dkg';
export * as DklsDsg from './dsg';
export * as DklsTypes from './types';
export * as DklsComms from './commsLayer';
export * as DklsUtils from './util';
11 changes: 8 additions & 3 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { decode } from 'cbor';
interface BroadcastMessage<T> {
payload: T;
from: number;
signatureR?: T;
}

// P2P message meant to be sent to a specific party
Expand Down Expand Up @@ -47,14 +48,16 @@ export type PartyGpgKey = {
partyId: number;
gpgKey: string;
};
export type DklsSignature = {
R: Uint8Array;
S: Uint8Array;
export type DklsSignature<T> = {
R: T;
S: T;
};
export type SerializedBroadcastMessage = BroadcastMessage<string>;
export type DeserializedBroadcastMessage = BroadcastMessage<Uint8Array>;
export type SerializedP2PMessage = P2PMessage<string, string>;
export type DeserializedP2PMessage = P2PMessage<Uint8Array, Uint8Array>;
export type SerializedDklsSignature = DklsSignature<string>;
export type DeserializedDklsSignature = DklsSignature<Uint8Array>;
export type AuthEncP2PMessage = P2PMessage<AuthEncMessage, string>;
export type AuthBroadcastMessage = BroadcastMessage<AuthMessage>;
export type SerializedMessages = {
Expand Down Expand Up @@ -113,6 +116,7 @@ export function deserializeBroadcastMessage(message: SerializedBroadcastMessage)
return {
from: message.from,
payload: new Uint8Array(Buffer.from(message.payload, 'base64')),
signatureR: message.signatureR ? new Uint8Array(Buffer.from(message.signatureR, 'base64')) : undefined,
};
}

Expand All @@ -137,6 +141,7 @@ export function serializeBroadcastMessage(message: DeserializedBroadcastMessage)
return {
from: message.from,
payload: Buffer.from(message.payload).toString('base64'),
signatureR: message.signatureR ? Buffer.from(message.signatureR).toString('base64') : undefined,
};
}

Expand Down
29 changes: 29 additions & 0 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Signature } from '@noble/secp256k1';
import { Secp256k1Curve } from '../../curves';
import { bigIntFromBufferBE, bigIntToBufferBE } from '../../util';
import { DeserializedDklsSignature } from './types';
import { decode } from 'cbor';

/**
* 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 Signature(r, s);
const normalizedSig = sig.normalizeS();
return {
R: new Uint8Array(bigIntToBufferBE(normalizedSig.r)),
S: new Uint8Array(bigIntToBufferBE(normalizedSig.s)),
};
}
14 changes: 12 additions & 2 deletions modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDsg.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { DklsDsg } from '../../../../src/tss/ecdsa-dkls';
import { DklsDsg, DklsUtils } from '../../../../src/tss/ecdsa-dkls';
import * as fs from 'fs';
import * as crypto from 'crypto';
import should from 'should';
import { Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
import { decode } from 'cbor';
import { Secp256k1Bip32HdTree, bigIntFromBufferBE, bigIntToBufferBE } from '../../../../src';

import * as secp256k1 from 'secp256k1';

describe('DKLS Dsg 2x3', function () {
Expand Down Expand Up @@ -91,6 +90,17 @@ describe('DKLS Dsg 2x3', function () {
broadcastMessages: party2Round4Messages.broadcastMessages,
});
should.exist(party1.signature);
should.exist(party1Round4Messages.broadcastMessages[0].signatureR);
const combinedSigUsingUtil = DklsUtils.combinePartialSignatures(
[party1Round4Messages.broadcastMessages[0].payload, party2Round4Messages.broadcastMessages[0].payload],
Buffer.from(party1Round4Messages.broadcastMessages[0].signatureR!).toString('hex')
);
(
(combinedSigUsingUtil.R.every((p) => party1.signature.R.includes(p)) &&
party1.signature.R.every((p) => combinedSigUsingUtil.R.includes(p))) ||
(party1.signature.S.every((p) => combinedSigUsingUtil.S.includes(p)) &&
combinedSigUsingUtil.S.every((p) => party1.signature.S.includes(p)))
).should.equal(true);
// ////////////
party2.handleIncomingMessages({
p2pMessages: [],
Expand Down

0 comments on commit 4a8aa15

Please sign in to comment.