Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/HSM-362/save-less-on-key-card'
Browse files Browse the repository at this point in the history
  • Loading branch information
islamaminBitGo committed May 17, 2024
2 parents e9ee257 + 63512d4 commit b5de9be
Show file tree
Hide file tree
Showing 14 changed files with 361 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,21 @@ describe('TSS Ecdsa MPCv2 Utils:', async function () {

it('should create TSS key chains', async function () {
const nockPromises = [
nockKeychain({ coin: coinName, keyChain: { id: '1', pub: '1', type: 'tss' }, source: 'user' }),
nockKeychain({ coin: coinName, keyChain: { id: '2', pub: '2', type: 'tss' }, source: 'backup' }),
nockKeychain({ coin: coinName, keyChain: { id: '3', pub: '3', type: 'tss' }, source: 'bitgo' }),
nockKeychain({
coin: coinName,
keyChain: { id: '1', pub: '1', type: 'tss', reducedEncryptedPrv: '' },
source: 'user',
}),
nockKeychain({
coin: coinName,
keyChain: { id: '2', pub: '2', type: 'tss', reducedEncryptedPrv: '' },
source: 'backup',
}),
nockKeychain({
coin: coinName,
keyChain: { id: '3', pub: '3', type: 'tss', reducedEncryptedPrv: '' },
source: 'bitgo',
}),
];
const [nockedUserKeychain, nockedBackupKeychain, nockedBitGoKeychain] = await Promise.all(nockPromises);

Expand All @@ -141,13 +153,15 @@ describe('TSS Ecdsa MPCv2 Utils:', async function () {
ECDSAUtils.MPCv2PartiesEnum.USER,
'test',
Buffer.from('test'),
Buffer.from('test'),
'passphrase',
'test'
);
const backupKeychainPromise = tssUtils.createParticipantKeychain(
ECDSAUtils.MPCv2PartiesEnum.BACKUP,
'test',
Buffer.from('test'),
Buffer.from('test'),
'passphrase',
'test'
);
Expand All @@ -158,9 +172,9 @@ describe('TSS Ecdsa MPCv2 Utils:', async function () {
bitgoKeychainPromise,
]);

userKeychain.should.deepEqual(nockedUserKeychain);
backupKeychain.should.deepEqual(nockedBackupKeychain);
bitgoKeychain.should.deepEqual(nockedBitGoKeychain);
({ ...userKeychain, reducedEncryptedPrv: '' }).should.deepEqual(nockedUserKeychain);
({ ...backupKeychain, reducedEncryptedPrv: '' }).should.deepEqual(nockedBackupKeychain);
({ ...bitgoKeychain, reducedEncryptedPrv: '' }).should.deepEqual(nockedBitGoKeychain);
});
});

Expand Down
14 changes: 14 additions & 0 deletions modules/key-card/src/generateQrData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ function getPubFromKey(key: Keychain): string | undefined {
}

function generateUserQrData(userKeychain: Keychain, userMasterKey?: string): QrDataEntry {
if (userKeychain.reducedEncryptedPrv) {
return {
title: 'A: User Key',
description: 'This is your private key, encrypted with your wallet password.',
data: userKeychain.reducedEncryptedPrv,
};
}
if (userKeychain.encryptedPrv) {
return {
title: 'A: User Key',
Expand Down Expand Up @@ -47,6 +54,13 @@ function generateBackupQrData(
} = {}
): QrDataEntry {
const title = 'B: Backup Key';
if (backupKeychain.reducedEncryptedPrv) {
return {
title,
description: 'This is your backup private key, encrypted with your wallet password.',
data: backupKeychain.reducedEncryptedPrv,
};
}
if (backupKeychain.encryptedPrv) {
return {
title,
Expand Down
3 changes: 3 additions & 0 deletions modules/sdk-core/src/bitgo/keychain/iKeychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export interface Keychain {
prv?: string;
provider?: string;
encryptedPrv?: string;
// Required for MPCV2 keys where we reduce the amount of data needed for the keycard.
// This is only generated client side and is not sent to WP
reducedEncryptedPrv?: string;
derivationPath?: string;
derivedFromParentWithSeed?: string;
commonPub?: string;
Expand Down
23 changes: 20 additions & 3 deletions modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Hash } from 'crypto';
import createKeccakHash from 'keccak';
import { DklsDkg, DklsTypes, DklsComms, DklsDsg } from '@bitgo/sdk-lib-mpc';

import { Keychain, KeyType } from '../../../keychain';
import { AddKeychainOptions, Keychain, KeyType } from '../../../keychain';
import { KeychainsTriplet } from '../../../baseCoin';
import { generateGPGKeyPair } from '../../opengpgUtils';
import { BaseEcdsaUtils } from './base';
Expand Down Expand Up @@ -278,6 +278,8 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {

const userPrivateMaterial = userSession.getKeyShare();
const backupPrivateMaterial = backupSession.getKeyShare();
const userReducedPrivateMaterial = userSession.getReducedKeyShare();
const backupReducedPrivateMaterial = backupSession.getReducedKeyShare();

const userCommonKeychain = DklsTypes.getCommonKeychain(userPrivateMaterial);
const backupCommonKeychain = DklsTypes.getCommonKeychain(backupPrivateMaterial);
Expand All @@ -288,12 +290,14 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
const userKeychainPromise = this.addUserKeychain(
bitgoCommonKeychain,
userPrivateMaterial,
userReducedPrivateMaterial,
params.passphrase,
params.originalPasscodeEncryptionCode
);
const backupKeychainPromise = this.addBackupKeychain(
bitgoCommonKeychain,
userPrivateMaterial,
backupReducedPrivateMaterial,
params.passphrase,
params.originalPasscodeEncryptionCode
);
Expand All @@ -318,21 +322,30 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
participantIndex: MPCv2Party,
commonKeychain: string,
privateMaterial?: Buffer,
reducedPrivateMaterial?: Buffer,
passphrase?: string,
originalPasscodeEncryptionCode?: string
): Promise<Keychain> {
let source: string;
let encryptedPrv: string | undefined = undefined;
let reducedEncryptedPrv: string | undefined = undefined;
switch (participantIndex) {
case MPCv2PartiesEnum.USER:
case MPCv2PartiesEnum.BACKUP:
source = participantIndex === MPCv2PartiesEnum.USER ? 'user' : 'backup';
assert(privateMaterial, `Private material is required for ${source} keychain`);
assert(reducedPrivateMaterial, `Reduced private material is required for ${source} keychain`);
assert(passphrase, `Passphrase is required for ${source} keychain`);
encryptedPrv = this.bitgo.encrypt({
input: privateMaterial.toString('base64'),
password: passphrase,
});
reducedEncryptedPrv = this.bitgo.encrypt({
// Buffer.toString('base64') can not be used here as it does not work on the browser.
// The browser deals with a Buffer as Uint8Array, therefore in the browser .toString('base64') just creates a comma seperated string of the array values.
input: btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(reducedPrivateMaterial)))),
password: passphrase,
});
break;
case MPCv2PartiesEnum.BITGO:
source = 'bitgo';
Expand All @@ -341,7 +354,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
throw new Error('Invalid participant index');
}

const recipientKeychainParams = {
const recipientKeychainParams: AddKeychainOptions = {
source,
keyType: 'tss' as KeyType,
commonKeychain,
Expand All @@ -350,19 +363,21 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
};

const keychains = this.baseCoin.keychains();
return keychains.add(recipientKeychainParams);
return { ...(await keychains.add(recipientKeychainParams)), reducedEncryptedPrv: reducedEncryptedPrv };
}

private async addUserKeychain(
commonKeychain: string,
privateMaterial: Buffer,
reducedPrivateMaterial: Buffer,
passphrase: string,
originalPasscodeEncryptionCode?: string
): Promise<Keychain> {
return this.createParticipantKeychain(
MPCv2PartiesEnum.USER,
commonKeychain,
privateMaterial,
reducedPrivateMaterial,
passphrase,
originalPasscodeEncryptionCode
);
Expand All @@ -371,13 +386,15 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
private async addBackupKeychain(
commonKeychain: string,
privateMaterial: Buffer,
reducedPrivateMaterial: Buffer,
passphrase: string,
originalPasscodeEncryptionCode?: string
): Promise<Keychain> {
return this.createParticipantKeychain(
MPCv2PartiesEnum.BACKUP,
commonKeychain,
privateMaterial,
reducedPrivateMaterial,
passphrase,
originalPasscodeEncryptionCode
);
Expand Down
4 changes: 4 additions & 0 deletions modules/sdk-lib-mpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"@wasmer/wasi": "^1.2.2",
"bigint-crypto-utils": "3.1.4",
"bigint-mod-arith": "3.1.2",
"fp-ts": "2.16.2",
"io-ts": "2.1.3",
"cbor-x": "1.5.9",
"libsodium-wrappers-sumo": "^0.7.9",
"openpgp": "5.10.1",
Expand All @@ -52,6 +54,8 @@
"devDependencies": {
"@types/lodash": "^4.14.151",
"@types/node": "^20.11.19",
"@types/sjcl": "1.0.34",
"sjcl": "1.0.8",
"nyc": "^15.0.0",
"secp256k1": "5.0.0"
}
Expand Down
18 changes: 17 additions & 1 deletion modules/sdk-lib-mpc/src/tss/ecdsa-dkls/dkg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KeygenSession, Keyshare, Message } from '@silencelaboratories/dkls-wasm-ll-node';
import { DeserializedBroadcastMessage, DeserializedMessages, DkgState, RetrofitData } from './types';
import { DeserializedBroadcastMessage, DeserializedMessages, DkgState, ReducedKeyShare, RetrofitData } from './types';
import { decode, encode } from 'cbor-x';
import { bigIntToBufferBE } from '../../util';
import { Secp256k1Curve } from '../../curves';
Expand Down Expand Up @@ -137,6 +137,22 @@ export class Dkg {
return this.keyShareBuff;
}

getReducedKeyShare(): Buffer {
if (!this.keyShareBuff) {
throw Error('Can not get key share, DKG is not complete yet.');
}
const decodedKeyshare = decode(this.keyShareBuff);
const reducedKeyShare: ReducedKeyShare = {
bigSList: decodedKeyshare.big_s_list,
xList: decodedKeyshare.x_i_list,
rootChainCode: decodedKeyshare.root_chain_code,
prv: decodedKeyshare.s_i,
pub: decodedKeyshare.public_key,
};
const encodedKeyShare = encode(reducedKeyShare);
return encodedKeyShare;
}

handleIncomingMessages(messagesForIthRound: DeserializedMessages): DeserializedMessages {
let nextRoundMessages: Message[] = [];
let nextRoundDeserializedMessages: DeserializedMessages = { broadcastMessages: [], p2pMessages: [] };
Expand Down
14 changes: 13 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-x';
import * as t from 'io-ts';
import { XShare } from '../ecdsa/types';

// Broadcast message meant to be sent to multiple parties
Expand Down Expand Up @@ -56,8 +57,19 @@ export type DklsSignature<T> = {
export type RetrofitData = {
bigSiList: string[];
xShare: Partial<XShare>;
xiList?: string[];
xiList?: number[][];
};

export const ReducedKeyShareType = t.type({
bigSList: t.array(t.array(t.number)),
xList: t.array(t.array(t.number)),
rootChainCode: t.array(t.number),
prv: t.array(t.number),
pub: t.array(t.number),
});

export type ReducedKeyShare = t.TypeOf<typeof ReducedKeyShareType>;

export type SerializedBroadcastMessage = BroadcastMessage<string>;
export type DeserializedBroadcastMessage = BroadcastMessage<Uint8Array>;
export type SerializedP2PMessage = P2PMessage<string, string>;
Expand Down
5 changes: 5 additions & 0 deletions modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import assert from 'assert';
import { DklsDkg, DklsTypes } from '../../../../src/tss/ecdsa-dkls';
import { isRight } from 'fp-ts/Either';
import {
decryptAndVerifyIncomingMessages,
encryptAndAuthOutgoingMessages,
} from '../../../../src/tss/ecdsa-dkls/commsLayer';
import {
PartyGpgKey,
ReducedKeyShareType,
RetrofitData,
deserializeMessages,
serializeMessages,
Expand Down Expand Up @@ -88,6 +90,9 @@ describe('DKLS Dkg 2x3', function () {
const userKeyShare = user.getKeyShare();
const backupKeyShare = backup.getKeyShare();
const bitgoKeyShare = bitgo.getKeyShare();
const userReducedKeyShare = user.getReducedKeyShare();
const decodeReducedKeyshare = ReducedKeyShareType.decode(decode(userReducedKeyShare));
assert(isRight(decodeReducedKeyshare));
assert.deepEqual(decode(userKeyShare).public_key, decode(bitgoKeyShare).public_key);
assert.deepEqual(decode(backupKeyShare).public_key, decode(bitgoKeyShare).public_key);
assert.deepEqual(DklsTypes.getCommonKeychain(userKeyShare), DklsTypes.getCommonKeychain(bitgoKeyShare));
Expand Down

0 comments on commit b5de9be

Please sign in to comment.