Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine partial signatures util support #4430

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 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,33 @@ import {
bigIntToBufferBE,
clamp,
getPaillierPublicKey,
DklsUtils,
DklsTypes,
} from '@bitgo/sdk-lib-mpc';

/**
* Combines serialized partial signatures from parties participating in DSG.
* @param round4DsgMessages - round 4 serialized broadcast messages payloads from participating parties
* @returns {DklsTypes.SerializedDklsSignature} - serialized final signature
*/
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
Loading