diff --git a/modules/sdk-lib-mpc/.mocharc.js b/modules/sdk-lib-mpc/.mocharc.js index 38ad92e834..bc895dd373 100644 --- a/modules/sdk-lib-mpc/.mocharc.js +++ b/modules/sdk-lib-mpc/.mocharc.js @@ -2,7 +2,7 @@ module.exports = { require: 'ts-node/register', - timeout: '20000', + timeout: '200000', reporter: 'min', 'reporter-option': ['cdn=true', 'json=false'], exit: true, diff --git a/modules/sdk-lib-mpc/package.json b/modules/sdk-lib-mpc/package.json index 82603f95be..f9d02cf00a 100644 --- a/modules/sdk-lib-mpc/package.json +++ b/modules/sdk-lib-mpc/package.json @@ -43,7 +43,7 @@ "@wasmer/wasi": "^1.2.2", "bigint-crypto-utils": "3.1.4", "bigint-mod-arith": "3.1.2", - "cbor": "^9.0.1", + "cbor-x": "1.5.8", "libsodium-wrappers-sumo": "^0.7.9", "openpgp": "5.10.1", "paillier-bigint": "3.3.0" diff --git a/modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts b/modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts index 1ab7946fd9..d1d5b6fdfb 100644 --- a/modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts +++ b/modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts @@ -3,9 +3,14 @@ import { decryptAndVerifyIncomingMessages, encryptAndAuthOutgoingMessages, } from '../../../../src/tss/ecdsa-dkls/commsLayer'; +import { bigIntToBufferBE, Secp256k1Curve } from '../../../../src'; +import { Ecdsa } from '@bitgo/sdk-core'; import { PartyGpgKey, deserializeMessages, serializeMessages } from '../../../../src/tss/ecdsa-dkls/types'; import * as openpgp from 'openpgp'; -import { decode } from 'cbor'; +import { decode, encode } from 'cbor-x'; + +import * as secp256k1 from 'secp256k1'; +import { KeygenSession, Keyshare, Message, SignSession } from '@silencelaboratories/dkls-wasm-ll-node'; describe('DKLS Dkg 2x3', function () { it(`should create key shares`, async function () { @@ -75,7 +80,151 @@ describe('DKLS Dkg 2x3', function () { p2pMessages: [], broadcastMessages: bitgoRound4Messages.broadcastMessages.concat(userRound4Messages.broadcastMessages), }); - const backupKeyShare = decode(backup.getKeyShare()); + const MPC = new Ecdsa(); + const sec256k1 = new Secp256k1Curve(); + const [A, B, C] = await Promise.all([MPC.keyShare(1, 2, 3), MPC.keyShare(2, 2, 3), MPC.keyShare(3, 2, 3)]); + const aKeyCombine = MPC.keyCombine(A.pShare, [B.nShares[1], C.nShares[1]]); + const bKeyCombine = MPC.keyCombine(B.pShare, [A.nShares[2], C.nShares[2]]); + const cKeyCombine = MPC.keyCombine(C.pShare, [A.nShares[3], B.nShares[3]]); + const x_i_list = [new Array(32).fill(0), new Array(32).fill(0), new Array(32).fill(0)]; + x_i_list[0][31] = 1; + x_i_list[1][31] = 2; + x_i_list[2][31] = 3; + const aPub = bigIntToBufferBE(sec256k1.basePointMult(BigInt('0x' + aKeyCombine.xShare.x))); + const bPub = bigIntToBufferBE(sec256k1.basePointMult(BigInt('0x' + bKeyCombine.xShare.x))); + const cPub = bigIntToBufferBE(sec256k1.basePointMult(BigInt('0x' + cKeyCombine.xShare.x))); + const keyShare = backup.getKeyShare(); + const backupKeyShare = decode(keyShare); + const aKeyShare = { + total_parties: 3, + threshold: 2, + rank_list: [0, 0, 0], + party_id: 0, + public_key: Array.from(Buffer.from(aKeyCombine.xShare.y, 'hex')), + root_chain_code: Array.from(Buffer.from(aKeyCombine.xShare.chaincode, 'hex')), + final_session_id: Array(32).fill(0), + seed_ot_receivers: [Array(32832).fill(0), Array(32832).fill(0)], + seed_ot_senders: [Array(32768).fill(0), Array(32768).fill(0)], + sent_seed_list: [Array(32).fill(0)], + rec_seed_list: [Array(32).fill(0)], + s_i: Array.from(Buffer.from(aKeyCombine.xShare.x, 'hex')), + big_s_list: [Array.from(aPub), Array.from(bPub), Array.from(cPub)], + x_i_list: x_i_list, + }; + const bKeyShare = { + total_parties: 3, + threshold: 2, + rank_list: [0, 0, 0], + party_id: 1, + public_key: Array.from(Buffer.from(bKeyCombine.xShare.y, 'hex')), + root_chain_code: Array.from(Buffer.from(bKeyCombine.xShare.chaincode, 'hex')), + final_session_id: Array(32).fill(0), + seed_ot_receivers: [Array(32832).fill(0), Array(32832).fill(0)], + seed_ot_senders: [Array(32768).fill(0), Array(32768).fill(0)], + sent_seed_list: [Array(32).fill(0)], + rec_seed_list: [Array(32).fill(0)], + s_i: Array.from(Buffer.from(bKeyCombine.xShare.x, 'hex')), + big_s_list: [Array.from(aPub), Array.from(bPub), Array.from(cPub)], + x_i_list: x_i_list, + }; + const cKeyShare = { + total_parties: 3, + threshold: 2, + rank_list: [0, 0, 0], + party_id: 2, + public_key: Array.from(Buffer.from(cKeyCombine.xShare.y, 'hex')), + root_chain_code: Array.from(Buffer.from(cKeyCombine.xShare.chaincode, 'hex')), + final_session_id: Array(32).fill(0), + seed_ot_receivers: [Array(32832).fill(0), Array(32832).fill(0)], + seed_ot_senders: [Array(32768).fill(0), Array(32768).fill(0)], + sent_seed_list: [Array(32).fill(0)], + rec_seed_list: [Array(32).fill(0)], + s_i: Array.from(Buffer.from(cKeyCombine.xShare.x, 'hex')), + big_s_list: [Array.from(aPub), Array.from(bPub), Array.from(cPub)], + x_i_list: x_i_list, + }; + console.log(backupKeyShare.public_key); + const sLAKeyShare = Keyshare.fromBytes(encode(aKeyShare)); + const sLBKeyShare = Keyshare.fromBytes(encode(bKeyShare)); + const sLCKeyShare = Keyshare.fromBytes(encode(cKeyShare)); + console.log(sLAKeyShare.publicKey); + console.log(sLBKeyShare.publicKey); + console.log(sLCKeyShare.publicKey); + + function key_rotation(oldshares: Keyshare[]) { + return oldshares.map((p) => KeygenSession.initKeyRotation(p)); + } + + function filterMessages(msgs: Message[], party: number): Message[] { + return msgs.filter((m) => m.from_id != party).map((m) => m.clone()); + } + + function selectMessages(msgs: Message[], party: number): Message[] { + return msgs.filter((m) => m.to_id == party).map((m) => m.clone()); + } + + function dkg_inner(parties: KeygenSession[]): Keyshare[] { + const msg1: Message[] = parties.map((p) => p.createFirstMessage()); + const msg2: Message[] = parties.flatMap((p, pid) => p.handleMessages(filterMessages(msg1, pid))); + + // after handling batch msg1, all parties calculate final session id, + // and not we have to calculate commitments for chain_code_sid + const commitments = parties.map((p) => p.calculateChainCodeCommitment()); + + const msg3: Message[] = parties.flatMap((p, pid) => p.handleMessages(selectMessages(msg2, pid))); + const msg4: Message[] = parties.flatMap((p, pid) => p.handleMessages(selectMessages(msg3, pid), commitments)); + + parties.flatMap((p, pid) => p.handleMessages(filterMessages(msg4, pid))); + + return parties.map((p) => p.keyshare()); // deallocates session object + } + const shares = [sLAKeyShare, sLBKeyShare, sLCKeyShare]; + const rotation_parties = key_rotation(shares); + const new_shares = dkg_inner(rotation_parties); + + new_shares.forEach((s, i) => s.finishKeyRotation(shares[i])); + function dsg(shares: Keyshare[], t: number, messageHash: Uint8Array) { + const parties: SignSession[] = []; + + // for simplicity we always use the first T shares. + for (let i = 0; i < t; i++) { + parties.push(new SignSession(shares[i], 'm')); + } + + const msg1: Message[] = parties.map((p) => p.createFirstMessage()); + const msg2: Message[] = parties.flatMap((p, pid) => p.handleMessages(filterMessages(msg1, pid))); + const msg3: Message[] = parties.flatMap((p, pid) => p.handleMessages(selectMessages(msg2, pid))); + + parties.flatMap((p, pid) => p.handleMessages(selectMessages(msg3, pid))); + + const msg4: Message[] = parties.map((p) => p.lastMessage(messageHash)); + + const signs = parties.map((p, pid) => p.combine(filterMessages(msg4, pid))); + + return signs; + } + const messageHash = new Uint8Array(32); + const sig = dsg(new_shares, 2, messageHash); + const pub1 = secp256k1.ecdsaRecover( + Buffer.concat([Buffer.from(sig[0][0]), Buffer.from(sig[0][1])]), + 0, + messageHash, + true + ); + const pub2 = secp256k1.ecdsaRecover( + Buffer.concat([Buffer.from(sig[0][0]), Buffer.from(sig[0][1])]), + 1, + messageHash, + true + ); + console.log(pub1); + console.log(pub2); + ( + (pub1.every((p) => Buffer.from(aKeyCombine.xShare.y, 'hex').includes(p)) && + Buffer.from(aKeyCombine.xShare.y, 'hex').every((p) => pub1.includes(p))) || + (pub2.every((p) => Buffer.from(aKeyCombine.xShare.y, 'hex').includes(p)) && + Buffer.from(aKeyCombine.xShare.y, 'hex').every((p) => pub2.includes(p))) + ).should.equal(true); decode(user.getKeyShare()).public_key.should.deepEqual(backupKeyShare.public_key); decode(bitgo.getKeyShare()).public_key.should.deepEqual(backupKeyShare.public_key); }); diff --git a/yarn.lock b/yarn.lock index b6c5f5e686..97e80b8385 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1062,6 +1062,36 @@ resolved "https://registry.npmjs.org/@brandonblack/musig/-/musig-0.0.1-alpha.1.tgz" integrity sha512-00RbByQG85lSzrkDjCblzrUc2n1LJAPPrEMHS4oMg+QckE0kzjd26JytT6yx6tNU2+aOXfK7O4kGW/sKVL67cw== +"@cbor-extract/cbor-extract-darwin-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz#8d65cb861a99622e1b4a268e2d522d2ec6137338" + integrity sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w== + +"@cbor-extract/cbor-extract-darwin-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz#9fbec199c888c5ec485a1839f4fad0485ab6c40a" + integrity sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w== + +"@cbor-extract/cbor-extract-linux-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz#bf77e0db4a1d2200a5aa072e02210d5043e953ae" + integrity sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ== + +"@cbor-extract/cbor-extract-linux-arm@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz#491335037eb8533ed8e21b139c59f6df04e39709" + integrity sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q== + +"@cbor-extract/cbor-extract-linux-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz#672574485ccd24759bf8fb8eab9dbca517d35b97" + integrity sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw== + +"@cbor-extract/cbor-extract-win32-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz#4b3f07af047f984c082de34b116e765cb9af975f" + integrity sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w== + "@celo/base@2.3.0": version "2.3.0" resolved "https://registry.npmjs.org/@celo/base/-/base-2.3.0.tgz" @@ -7576,6 +7606,27 @@ catharsis@^0.9.0: dependencies: lodash "^4.17.15" +cbor-extract@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.2.0.tgz#cee78e630cbeae3918d1e2e58e0cebaf3a3be840" + integrity sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA== + dependencies: + node-gyp-build-optional-packages "5.1.1" + optionalDependencies: + "@cbor-extract/cbor-extract-darwin-arm64" "2.2.0" + "@cbor-extract/cbor-extract-darwin-x64" "2.2.0" + "@cbor-extract/cbor-extract-linux-arm" "2.2.0" + "@cbor-extract/cbor-extract-linux-arm64" "2.2.0" + "@cbor-extract/cbor-extract-linux-x64" "2.2.0" + "@cbor-extract/cbor-extract-win32-x64" "2.2.0" + +cbor-x@1.5.8: + version "1.5.8" + resolved "https://registry.yarnpkg.com/cbor-x/-/cbor-x-1.5.8.tgz#3bd7bc61120692b5031d7f782a39b64f51f1d825" + integrity sha512-gc3bHBsvG6GClCY6c0/iip+ghlqizkVp+TtaL927lwvP4VP9xBdi1HmqPR5uj/Mj/0TOlngMkIYa25wKg+VNrQ== + optionalDependencies: + cbor-extract "^2.2.0" + cbor@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.1.tgz#b16e393d4948d44758cd54ac6151379d443b37ae" @@ -8989,6 +9040,11 @@ detect-indent@^6.0.0: resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-libc@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" @@ -14336,6 +14392,13 @@ node-forge@^1: resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-gyp-build-optional-packages@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz#52b143b9dd77b7669073cbfe39e3f4118bfc603c" + integrity sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw== + dependencies: + detect-libc "^2.0.1" + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz"