From e3bc07cbf6838c62bce5f3faf5cffd1c40915174 Mon Sep 17 00:00:00 2001 From: Zhongxi Shen Date: Fri, 17 Apr 2026 14:48:46 -0600 Subject: [PATCH 1/2] feat: add round domain separator to adata TICKET: HSM-1513 --- .../src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts index 55f1d9b725..bc7059fc6c 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts @@ -51,6 +51,10 @@ import { EcdsaMPCv2KeyGenSendFn, KeyGenSenderForEnterprise } from './ecdsaMPCv2K import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../../../tss/bitgoPubKeys'; export class EcdsaMPCv2Utils extends BaseEcdsaUtils { + private static readonly DKLS23_SIGNING_USER_GPG_KEY = 'DKLS23_SIGNING_USER_GPG_KEY'; + private static readonly DKLS23_SIGNING_ROUND1_STATE = 'DKLS23_SIGNING_ROUND1_STATE'; + private static readonly DKLS23_SIGNING_ROUND2_STATE = 'DKLS23_SIGNING_ROUND2_STATE'; + /** @inheritdoc */ async createKeychains(params: { passphrase: string; @@ -964,17 +968,20 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { * @param {string} bitgoPublicGpgKey - the BitGo public GPG key * @param {string} encryptedUserGpgPrvKey - the encrypted user GPG private key * @param {string} walletPassphrase - the wallet passphrase + * @param {string} adata - the additional data to validate the GPG keys * @returns {Promise<{ bitgoGpgKey: pgp.Key; userGpgKey: pgp.SerializedKeyPair }>} - the BitGo and user GPG keys */ private async getBitgoAndUserGpgKeys( bitgoPublicGpgKey: string, encryptedUserGpgPrvKey: string, - walletPassphrase: string + walletPassphrase: string, + adata: string ): Promise<{ bitgoGpgKey: pgp.Key; userGpgKey: pgp.SerializedKeyPair; }> { const bitgoGpgKey = await pgp.readKey({ armoredKey: bitgoPublicGpgKey }); + this.validateAdata(adata, encryptedUserGpgPrvKey, EcdsaMPCv2Utils.DKLS23_SIGNING_USER_GPG_KEY); const userDecryptedKey = await pgp.readKey({ armoredKey: this.bitgo.decrypt({ input: encryptedUserGpgPrvKey, password: walletPassphrase }), }); @@ -995,7 +1002,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { * @returns void * @throws {Error} if the adata or cyphertext is invalid */ - private validateAdata(adata: string, cyphertext: string): void { + private validateAdata(adata: string, cyphertext: string, roundDomainSeparator: string): void { let cypherJson; try { cypherJson = JSON.parse(cyphertext); @@ -1003,7 +1010,10 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { throw new Error('Failed to parse cyphertext to JSON, got: ' + cyphertext); } // using decodeURIComponent to handle special characters - if (decodeURIComponent(cypherJson.adata) !== decodeURIComponent(adata)) { + if ( + decodeURIComponent(cypherJson.adata) !== decodeURIComponent(`${roundDomainSeparator}:${adata}`) && + decodeURIComponent(cypherJson.adata) !== decodeURIComponent(adata) + ) { throw new Error('Adata does not match cyphertext adata'); } } @@ -1124,13 +1134,17 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { const userSignerBroadcastMsg1 = await userSigner.init(); const signatureShareRound1 = await getSignatureShareRoundOne(userSignerBroadcastMsg1, userGpgKey); const session = userSigner.getSession(); - const encryptedRound1Session = this.bitgo.encrypt({ input: session, password: walletPassphrase, adata }); + const encryptedRound1Session = this.bitgo.encrypt({ + input: session, + password: walletPassphrase, + adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND1_STATE}:${adata}`, + }); const userGpgPubKey = userGpgKey.publicKey; const encryptedUserGpgPrvKey = this.bitgo.encrypt({ input: userGpgKey.privateKey, password: walletPassphrase, - adata, + adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_USER_GPG_KEY}:${adata}`, }); return { signatureShareRound1, userGpgPubKey, encryptedRound1Session, encryptedUserGpgPrvKey }; @@ -1155,7 +1169,8 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { const { bitgoGpgKey, userGpgKey } = await this.getBitgoAndUserGpgKeys( bitgoPublicGpgKey, encryptedUserGpgPrvKey, - walletPassphrase + walletPassphrase, + adata ); const signatureShares = txRequest.transactions?.[0].signatureShares; @@ -1172,9 +1187,9 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { bitgoGpgKey ); + this.validateAdata(adata, encryptedRound1Session, EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND1_STATE); const round1Session = this.bitgo.decrypt({ input: encryptedRound1Session, password: walletPassphrase }); - this.validateAdata(adata, encryptedRound1Session); const userKeyShare = Buffer.from(prv, 'base64'); const userSigner = new DklsDsg.Dsg(userKeyShare, 0, derivationPath, hashBuffer); await userSigner.setSession(round1Session); @@ -1195,7 +1210,11 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { bitgoGpgKey ); const session = userSigner.getSession(); - const encryptedRound2Session = this.bitgo.encrypt({ input: session, password: walletPassphrase, adata }); + const encryptedRound2Session = this.bitgo.encrypt({ + input: session, + password: walletPassphrase, + adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND2_STATE}:${adata}`, + }); return { signatureShareRound2, @@ -1223,7 +1242,8 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { const { bitgoGpgKey, userGpgKey } = await this.getBitgoAndUserGpgKeys( bitgoPublicGpgKey, encryptedUserGpgPrvKey, - walletPassphrase + walletPassphrase, + adata ); const signatureShares = txRequest.transactions?.[0].signatureShares; @@ -1245,8 +1265,9 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { broadcastMessages: [], }); + this.validateAdata(adata, encryptedRound2Session, EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND2_STATE); const round2Session = this.bitgo.decrypt({ input: encryptedRound2Session, password: walletPassphrase }); - this.validateAdata(adata, encryptedRound2Session); + const userKeyShare = Buffer.from(prv, 'base64'); const userSigner = new DklsDsg.Dsg(userKeyShare, 0, derivationPath, hashBuffer); await userSigner.setSession(round2Session); From 3e57f35913ef67be7439e8c7bce990a964a4e66a Mon Sep 17 00:00:00 2001 From: Zhongxi Shen Date: Wed, 22 Apr 2026 17:35:46 -0600 Subject: [PATCH 2/2] test: add ecdsaMPCv2 offline signing tests TICKET: HSM-1513 --- .../unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | 414 ++++++++++++++++++ .../unit/bitgo/utils/tss/ecdsa/gpgKeys.ts | 34 ++ 2 files changed, 448 insertions(+) create mode 100644 modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts create mode 100644 modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/gpgKeys.ts diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts new file mode 100644 index 0000000000..c9f7a575d6 --- /dev/null +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts @@ -0,0 +1,414 @@ +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { Hash, randomBytes } from 'crypto'; +import createKeccakHash from 'keccak'; +import { + MPCv2PartyFromStringOrNumber, + MPCv2SignatureShareRound1Input, + MPCv2SignatureShareRound1Output, + MPCv2SignatureShareRound2Input, + MPCv2SignatureShareRound2Output, + MPCv2SignatureShareRound3Input, +} from '@bitgo/public-types'; +import { DklsComms, DklsDsg, DklsTypes, DklsUtils } from '@bitgo/sdk-lib-mpc'; +import * as sjcl from '@bitgo/sjcl'; +import { + BitGoBase, + EcdsaMPCv2Utils, + IBaseCoin, + SignatureShareRecord, + SignatureShareType, + TxRequest, +} from '../../../../../../src'; +import { bitgoGpgKey } from './gpgKeys'; + +describe('ECDSA MPC v2', async () => { + let userShare: Buffer; + let bitgoShare: Buffer; + + before('generate key shares for testing', async () => { + const [userDkgSession, backupDkgSession, bitgoDkgSession] = await DklsUtils.generateDKGKeyShares(); + assert.ok(userDkgSession); + assert.ok(backupDkgSession); + assert.ok(bitgoDkgSession); + + userShare = userDkgSession.getKeyShare(); + bitgoShare = bitgoDkgSession.getKeyShare(); + }); + + let ecdsaMPCv2Utils: EcdsaMPCv2Utils; + + before('initialize EcdsaMPCv2Utils', async () => { + const mockBg = {} as BitGoBase; + mockBg.getEnv = sinon.stub().returns('test'); + mockBg.encrypt = sinon.stub().callsFake((params) => { + const salt = randomBytes(8); + const iv = randomBytes(16); + return sjcl.encrypt(params.password, params.input, { + salt: [bytesToWord(salt.subarray(0, 4)), bytesToWord(salt.subarray(4))], + iv: [ + bytesToWord(iv.subarray(0, 4)), + bytesToWord(iv.subarray(4, 8)), + bytesToWord(iv.subarray(8, 12)), + bytesToWord(iv.subarray(12, 16)), + ], + adata: params.adata, + }); + }); + mockBg.decrypt = sinon.stub().callsFake((params) => { + return sjcl.decrypt(params.password, params.input); + }); + + const mockCoin = {} as IBaseCoin; + mockCoin.getHashFunction = sinon.stub().callsFake(() => createKeccakHash('keccak256') as Hash); + + ecdsaMPCv2Utils = new EcdsaMPCv2Utils(mockBg, mockCoin); + }); + + it('should sign a message hash using ECDSA MPC v2 offline rounds', async () => { + const walletID = '62fe536a6b4cf70007acb48c0e7bb0b0'; + const tMessage = 'testMessage'; + const derivationPath = 'm/0'; + const walletPassphrase = 'testPass'; + + // round 1 + const reqMPCv2SigningRound1 = { + txRequest: { + txRequestId: '123456', + apiVersion: 'full', + walletId: walletID, + transactions: [ + { + unsignedTx: { + derivationPath, + signableHex: tMessage, + }, + signatureShares: [], + }, + ], + }, + prv: userShare.toString('base64'), + walletPassphrase, + }; + + const resMPCv2SigningRound1 = await ecdsaMPCv2Utils.createOfflineRound1Share(reqMPCv2SigningRound1 as any); + resMPCv2SigningRound1.should.have.property('signatureShareRound1'); + resMPCv2SigningRound1.should.have.property('userGpgPubKey'); + resMPCv2SigningRound1.should.have.property('encryptedRound1Session'); + resMPCv2SigningRound1.should.have.property('encryptedUserGpgPrvKey'); + + const encryptedRound1Session = resMPCv2SigningRound1.encryptedRound1Session; + const encryptedUserGpgPrvKey = resMPCv2SigningRound1.encryptedUserGpgPrvKey; + + const hashBuffer = createKeccakHash('keccak256').update(Buffer.from(tMessage, 'hex')).digest(); + const bitgoSession = new DklsDsg.Dsg(bitgoShare, 2, derivationPath, hashBuffer); + + const txRequestRound1 = await signBitgoMPCv2Round1( + bitgoSession, + reqMPCv2SigningRound1.txRequest as any, + resMPCv2SigningRound1.signatureShareRound1, + resMPCv2SigningRound1.userGpgPubKey + ); + assert.ok( + txRequestRound1.transactions && + txRequestRound1.transactions.length === 1 && + txRequestRound1.transactions[0].signatureShares.length === 2, + 'txRequestRound2.transactions is not an array of length 1 with 2 signatureShares' + ); + + // round 2 + const reqMPCv2SigningRound2 = { + txRequest: txRequestRound1, + encryptedRound1Session, + encryptedUserGpgPrvKey, + bitgoPublicGpgKey: bitgoGpgKey.public, + prv: userShare.toString('base64'), + walletPassphrase, + }; + + const resMPCv2SigningRound2 = await ecdsaMPCv2Utils.createOfflineRound2Share(reqMPCv2SigningRound2 as any); + resMPCv2SigningRound2.should.have.property('signatureShareRound2'); + resMPCv2SigningRound2.should.have.property('encryptedRound2Session'); + + const encryptedRound2Session = resMPCv2SigningRound2.encryptedRound2Session; + + const { txRequest: txRequestRound2, bitgoMsg4 } = await signBitgoMPCv2Round2( + bitgoSession, + reqMPCv2SigningRound2.txRequest, + resMPCv2SigningRound2.signatureShareRound2, + resMPCv2SigningRound1.userGpgPubKey + ); + assert.ok( + txRequestRound2.transactions && + txRequestRound2.transactions.length === 1 && + txRequestRound2.transactions[0].signatureShares.length === 4, + 'txRequestRound2.transactions is not an array of length 1 with 4 signatureShares' + ); + bitgoMsg4.should.have.property('signatureR'); + + // round 3 + const reqMPCv2SigningRound3 = { + txRequest: txRequestRound2, + encryptedRound2Session, + encryptedUserGpgPrvKey, + bitgoPublicGpgKey: bitgoGpgKey.public, + prv: userShare.toString('base64'), + walletPassphrase, + }; + + const resMPCv2SigningRound3 = await ecdsaMPCv2Utils.createOfflineRound3Share(reqMPCv2SigningRound3 as any); + resMPCv2SigningRound3.should.have.property('signatureShareRound3'); + + // A bogus round 3 signing request containing encrypted session from round 1 instead of round 2 should fail. + const bogusReqMPCv2SigningRound3 = { + ...reqMPCv2SigningRound3, + encryptedRound2Session: encryptedRound1Session, + }; + await ecdsaMPCv2Utils + .createOfflineRound3Share(bogusReqMPCv2SigningRound3 as any) + .should.be.rejectedWith('Adata does not match cyphertext adata'); + + const { userMsg4 } = await signBitgoMPCv2Round3( + bitgoSession, + resMPCv2SigningRound3.signatureShareRound3, + resMPCv2SigningRound1.userGpgPubKey + ); + + // signature generation and validation + assert.ok(userMsg4.data.msg4.signatureR === bitgoMsg4.signatureR, 'User and BitGo signaturesR do not match'); + + const deserializedBitgoMsg4 = DklsTypes.deserializeMessages({ + p2pMessages: [], + broadcastMessages: [bitgoMsg4], + }); + + const deserializedUserMsg4 = DklsTypes.deserializeMessages({ + p2pMessages: [], + broadcastMessages: [ + { + from: userMsg4.data.msg4.from, + payload: userMsg4.data.msg4.message, + }, + ], + }); + + const combinedSigUsingUtil = DklsUtils.combinePartialSignatures( + [deserializedUserMsg4.broadcastMessages[0].payload, deserializedBitgoMsg4.broadcastMessages[0].payload], + Buffer.from(userMsg4.data.msg4.signatureR, 'base64').toString('hex') + ); + + const convertedSignature = DklsUtils.verifyAndConvertDklsSignature( + Buffer.from(tMessage, 'hex'), + combinedSigUsingUtil, + DklsTypes.getCommonKeychain(userShare), + derivationPath, + createKeccakHash('keccak256') as Hash + ); + assert.ok(convertedSignature, 'Signature is not valid'); + assert.ok(convertedSignature.split(':').length === 4, 'Signature is not valid'); + }); +}); + +function bytesToWord(bytes?: Uint8Array | number[]): number { + if (!(bytes instanceof Uint8Array) || bytes.length !== 4) { + throw new Error('bytes must be a Uint8Array with length 4'); + } + + return bytes.reduce((num, byte) => num * 0x100 + byte, 0); +} + +function getUserPartyGpgKeyPublic(userPubKey: string): DklsTypes.PartyGpgKey { + return { + partyId: 0, + gpgKey: userPubKey, + }; +} + +function getBitGoPartyGpgKeyPrv(bitgoPrvKey: string): DklsTypes.PartyGpgKey { + return { + partyId: 2, + gpgKey: bitgoPrvKey, + }; +} + +async function signBitgoMPCv2Round1( + bitgoSession: DklsDsg.Dsg, + txRequest: TxRequest, + userShare: SignatureShareRecord, + userGPGPubKey: string +): Promise { + assert.ok( + txRequest.transactions && txRequest.transactions.length === 1, + 'txRequest.transactions is not an array of length 1' + ); + txRequest.transactions[0].signatureShares.push(userShare); + // Do the actual signing on BitGo's side based on User's messages + const signatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound1Input; + const deserializedMessages = DklsTypes.deserializeMessages({ + p2pMessages: [], + broadcastMessages: [ + { + from: signatureShare.data.msg1.from, + payload: signatureShare.data.msg1.message, + }, + ], + }); + const bitgoToUserRound1BroadcastMsg = await bitgoSession.init(); + const bitgoToUserRound2Msg = bitgoSession.handleIncomingMessages({ + p2pMessages: [], + broadcastMessages: deserializedMessages.broadcastMessages, + }); + const serializedBitGoToUserRound1And2Msgs = DklsTypes.serializeMessages({ + p2pMessages: bitgoToUserRound2Msg.p2pMessages, + broadcastMessages: [bitgoToUserRound1BroadcastMsg], + }); + + const authEncMessages = await DklsComms.encryptAndAuthOutgoingMessages( + serializedBitGoToUserRound1And2Msgs, + [getUserPartyGpgKeyPublic(userGPGPubKey)], + [getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)] + ); + + const bitgoToUserSignatureShare: MPCv2SignatureShareRound1Output = { + type: 'round1Output', + data: { + msg1: { + from: authEncMessages.broadcastMessages[0].from as MPCv2PartyFromStringOrNumber, + signature: authEncMessages.broadcastMessages[0].payload.signature, + message: authEncMessages.broadcastMessages[0].payload.message, + }, + msg2: { + from: authEncMessages.p2pMessages[0].from as MPCv2PartyFromStringOrNumber, + to: authEncMessages.p2pMessages[0].to as MPCv2PartyFromStringOrNumber, + encryptedMessage: authEncMessages.p2pMessages[0].payload.encryptedMessage, + signature: authEncMessages.p2pMessages[0].payload.signature, + }, + }, + }; + txRequest.transactions[0].signatureShares.push({ + from: SignatureShareType.BITGO, + to: SignatureShareType.USER, + share: JSON.stringify(bitgoToUserSignatureShare), + }); + return txRequest; +} + +async function signBitgoMPCv2Round2( + bitgoSession: DklsDsg.Dsg, + txRequest: TxRequest, + userShare: SignatureShareRecord, + userGPGPubKey: string +): Promise<{ txRequest: TxRequest; bitgoMsg4: DklsTypes.SerializedBroadcastMessage }> { + assert.ok( + txRequest.transactions && txRequest.transactions.length === 1, + 'txRequest.transactions is not an array of length 1' + ); + txRequest.transactions[0].signatureShares.push(userShare); + + // Do the actual signing on BitGo's side based on User's messages + const parsedSignatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound2Input; + const serializedMessages = await DklsComms.decryptAndVerifyIncomingMessages( + { + p2pMessages: [ + { + from: parsedSignatureShare.data.msg2.from, + to: parsedSignatureShare.data.msg2.to, + payload: { + encryptedMessage: parsedSignatureShare.data.msg2.encryptedMessage, + signature: parsedSignatureShare.data.msg2.signature, + }, + }, + { + from: parsedSignatureShare.data.msg3.from, + to: parsedSignatureShare.data.msg3.to, + payload: { + encryptedMessage: parsedSignatureShare.data.msg3.encryptedMessage, + signature: parsedSignatureShare.data.msg3.signature, + }, + }, + ], + broadcastMessages: [], + }, + [getUserPartyGpgKeyPublic(userGPGPubKey)], + [getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)] + ); + const deserializedMessages2 = DklsTypes.deserializeMessages({ + p2pMessages: [serializedMessages.p2pMessages[0]], + broadcastMessages: [], + }); + + const bitgoToUserRound3Msg = bitgoSession.handleIncomingMessages(deserializedMessages2); + const serializedBitGoToUserRound3Msgs = DklsTypes.serializeMessages(bitgoToUserRound3Msg); + + const authEncMessages = await DklsComms.encryptAndAuthOutgoingMessages( + serializedBitGoToUserRound3Msgs, + [getUserPartyGpgKeyPublic(userGPGPubKey)], + [getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)] + ); + + const bitgoToUserSignatureShare: MPCv2SignatureShareRound2Output = { + type: 'round2Output', + data: { + msg3: { + from: authEncMessages.p2pMessages[0].from as MPCv2PartyFromStringOrNumber, + to: authEncMessages.p2pMessages[0].to as MPCv2PartyFromStringOrNumber, + encryptedMessage: authEncMessages.p2pMessages[0].payload.encryptedMessage, + signature: authEncMessages.p2pMessages[0].payload.signature, + }, + }, + }; + + // handling user msg3 but not returning bitgo msg4 since its stored on bitgo side only + const deserializedMessages3 = DklsTypes.deserializeMessages({ + p2pMessages: [serializedMessages.p2pMessages[1]], + broadcastMessages: [], + }); + const deserializedBitgoMsg4 = bitgoSession.handleIncomingMessages(deserializedMessages3); + const serializedBitGoToUserRound4Msgs = DklsTypes.serializeMessages(deserializedBitgoMsg4); + + txRequest.transactions[0].signatureShares.push({ + from: SignatureShareType.BITGO, + to: SignatureShareType.USER, + share: JSON.stringify(bitgoToUserSignatureShare), + }); + return { txRequest, bitgoMsg4: serializedBitGoToUserRound4Msgs.broadcastMessages[0] }; +} + +async function signBitgoMPCv2Round3( + bitgoSession: DklsDsg.Dsg, + userShare: SignatureShareRecord, + userGPGPubKey: string +): Promise<{ userMsg4: MPCv2SignatureShareRound3Input }> { + const parsedSignatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound3Input; + const msg4 = parsedSignatureShare.data.msg4; + const signatureRAuthMessage = + msg4.signatureR && msg4.signatureRSignature + ? { message: msg4.signatureR, signature: msg4.signatureRSignature } + : undefined; + const serializedMessages = await DklsComms.decryptAndVerifyIncomingMessages( + { + p2pMessages: [], + broadcastMessages: [ + { + from: msg4.from, + payload: { + message: msg4.message, + signature: msg4.signature, + }, + signatureR: signatureRAuthMessage, + }, + ], + }, + [getUserPartyGpgKeyPublic(userGPGPubKey)], + [getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)] + ); + const deserializedMessages = DklsTypes.deserializeMessages({ + p2pMessages: [], + broadcastMessages: [serializedMessages.broadcastMessages[0]], + }); + bitgoSession.handleIncomingMessages(deserializedMessages); + + return { + userMsg4: parsedSignatureShare, + }; +} diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/gpgKeys.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/gpgKeys.ts new file mode 100644 index 0000000000..a95732c559 --- /dev/null +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/gpgKeys.ts @@ -0,0 +1,34 @@ +export const bitgoGpgKey = { + private: + '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + + '\n' + + 'xXQEZo2rshMFK4EEAAoCAwQC6HQa7PXiX2nnpZr/asCcEbgCOcjsR8gcSI8v\n' + + 'vMADk59KsFweg+kIzCR3UqfMe2uG6JHwOYpvDREHp/hqtA+hAAD/XgjiTu4D\n' + + '0d9YzSx3ZP8lUAcruvJbyMIIlr26QIeHq5kRQM0FYml0Z2/CjAQQEwgAPgWC\n' + + 'Zo2rsgQLCQcICZA8EZGJCCwPOAMVCAoEFgACAQIZAQKbAwIeARYhBLSGUeOq\n' + + 'bM5ym4aSnjwRkYkILA84AABXoQD+KkO5kWGw8GgWN142t+pGULPLzGo6353r\n' + + 'H8FwgKxe9ikBAKEjJI17aVlozG0RzFVxctBLLVqjYO5tBZQhoQbHHkGdx3gE\n' + + 'Zo2rshIFK4EEAAoCAwTBwmMa+htUmjUoqlKTuQaoWcY0Det+ee/6fV9+vnis\n' + + 'EyphRUFXnA0K0LyGpSnNlqKisSoArwUkZTiWwTbMWjTdAwEIBwABAJmAlxnB\n' + + 'IZ5bw88Duvw0yaRRcgXt5tDP0z23l6cvJWgKEJbCeAQYEwgAKgWCZo2rsgmQ\n' + + 'PBGRiQgsDzgCmwwWIQS0hlHjqmzOcpuGkp48EZGJCCwPOAAA3/4BAIozuxF1\n' + + 'JEoSQXe8YFIFqowwCiVwr2K6NqqRn+mGM1NjAQCYWIsZq+4+UCBIKScVknTG\n' + + 'uu2Utd5ZMyNYZTWCxLk9+g==\n' + + '=iXOB\n' + + '-----END PGP PRIVATE KEY BLOCK-----\n', + public: + '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' + + '\n' + + 'xk8EZo2rshMFK4EEAAoCAwQC6HQa7PXiX2nnpZr/asCcEbgCOcjsR8gcSI8v\n' + + 'vMADk59KsFweg+kIzCR3UqfMe2uG6JHwOYpvDREHp/hqtA+hzQViaXRnb8KM\n' + + 'BBATCAA+BYJmjauyBAsJBwgJkDwRkYkILA84AxUICgQWAAIBAhkBApsDAh4B\n' + + 'FiEEtIZR46psznKbhpKePBGRiQgsDzgAAFehAP4qQ7mRYbDwaBY3Xja36kZQ\n' + + 's8vMajrfnesfwXCArF72KQEAoSMkjXtpWWjMbRHMVXFy0EstWqNg7m0FlCGh\n' + + 'BsceQZ3OUwRmjauyEgUrgQQACgIDBMHCYxr6G1SaNSiqUpO5BqhZxjQN6355\n' + + '7/p9X36+eKwTKmFFQVecDQrQvIalKc2WoqKxKgCvBSRlOJbBNsxaNN0DAQgH\n' + + 'wngEGBMIACoFgmaNq7IJkDwRkYkILA84ApsMFiEEtIZR46psznKbhpKePBGR\n' + + 'iQgsDzgAAN/+AQCKM7sRdSRKEkF3vGBSBaqMMAolcK9iujaqkZ/phjNTYwEA\n' + + 'mFiLGavuPlAgSCknFZJ0xrrtlLXeWTMjWGU1gsS5Pfo=\n' + + '=7uRX\n' + + '-----END PGP PUBLIC KEY BLOCK-----\n', +};