|
| 1 | +import { Eddsa } from '@bitgo/sdk-core'; |
| 2 | +import * as fs from 'fs'; |
| 3 | +import assert = require('assert'); |
| 4 | +import { bigIntFromBufferBE, bigIntFromBufferLE, Ed25519Bip32HdTree } from '@bitgo/sdk-lib-mpc'; |
| 5 | +import sjcl = require('sjcl'); |
| 6 | + |
| 7 | +const sampleMessage = "Hello, World!"; |
| 8 | +const derivationPath = "m/0"; |
| 9 | +// TODO: Replace the following variables with your own values. Moreover, copy the encrypted user and backup keys from the key card into the userKey.txt and backupKey.txt files. |
| 10 | +const commonKeyChain = "<Public key from key card>"; |
| 11 | +const walletPassphrase = "<Wallet passphrase>"; |
| 12 | + |
| 13 | + |
| 14 | +async function testRecoveryEddsaTss() { |
| 15 | + const userKey = fs.readFileSync('userKey.txt', 'utf8').replace(/(\r\n|\n|\r)/gm, ""); |
| 16 | + const backupKey = fs.readFileSync('backupKey.txt', 'utf8').replace(/(\r\n|\n|\r)/gm, ""); |
| 17 | + // Produce a signature. |
| 18 | + const hdTree = await Ed25519Bip32HdTree.initialize(); |
| 19 | + const MPC = await Eddsa.initialize(hdTree); |
| 20 | + const userSigningMaterial = JSON.parse(sjcl.decrypt(walletPassphrase, userKey)); |
| 21 | + const backupSigningMaterial = JSON.parse(sjcl.decrypt(walletPassphrase, backupKey)); |
| 22 | + // Combine the key shares from backup -> user, bitgo -> user, and the user's private share to form the backup signing key offset by the derivation path. |
| 23 | + const userSubkey = MPC.keyDerive( |
| 24 | + userSigningMaterial.uShare, |
| 25 | + [userSigningMaterial.bitgoYShare, userSigningMaterial.backupYShare], |
| 26 | + derivationPath, |
| 27 | + ); |
| 28 | + // Combine the offset key shares from user -> backup, bitgo -> backup, and the backup's private share to form the backup signing key. |
| 29 | + const backupSubkey = MPC.keyCombine(backupSigningMaterial.uShare, [ |
| 30 | + userSubkey.yShares[2], |
| 31 | + backupSigningMaterial.bitgoYShare, |
| 32 | + ]); |
| 33 | + const messageBuffer = Buffer.from(sampleMessage, 'utf8'); |
| 34 | + // Partial Sign the message with the user and backup signing keys. |
| 35 | + const userSignShare = MPC.signShare(messageBuffer, userSubkey.pShare, [userSubkey.yShares[2]]); |
| 36 | + const backupSignShare = MPC.signShare(messageBuffer, backupSubkey.pShare, [backupSubkey.jShares[1]]); |
| 37 | + const userSign = MPC.sign( |
| 38 | + messageBuffer, |
| 39 | + userSignShare.xShare, |
| 40 | + [backupSignShare.rShares[1]], |
| 41 | + [userSigningMaterial.bitgoYShare] |
| 42 | + ); |
| 43 | + const backupSign = MPC.sign( |
| 44 | + messageBuffer, |
| 45 | + backupSignShare.xShare, |
| 46 | + [userSignShare.rShares[2]], |
| 47 | + [backupSigningMaterial.bitgoYShare] |
| 48 | + ); |
| 49 | + // Combine partial signatures to form the final signature. |
| 50 | + const signature = MPC.signCombine([userSign, backupSign]); |
| 51 | + const signatureBuffer = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); |
| 52 | + // Deriving the public key at path m/0 from the common key chain. |
| 53 | + const derivedPub = hdTree.publicDerive( |
| 54 | + { |
| 55 | + pk: bigIntFromBufferLE(Buffer.from(commonKeyChain.slice(0, 64), 'hex')), |
| 56 | + chaincode: bigIntFromBufferBE(Buffer.from(commonKeyChain.slice(64), 'hex')), |
| 57 | + }, |
| 58 | + derivationPath |
| 59 | + ); |
| 60 | + // Verify the signature. |
| 61 | + const isSignatureValid = Eddsa.curve.verify(messageBuffer, signatureBuffer, derivedPub.pk); |
| 62 | + assert(isSignatureValid, "Signature is not valid."); |
| 63 | +} |
| 64 | + |
| 65 | +testRecoveryEddsaTss().catch((e) => console.error(e)); |
0 commit comments