PHP WebShell
Текущая директория: /opt/BitGoJS/modules/account-lib/test/unit/mpc/tss/eddsa
Просмотр файла: eddsa.ts
/**
* @prettier
*/
import 'should';
import assert from 'assert';
import * as bs58 from 'bs58';
import { randomBytes } from 'crypto';
import * as sol from '@solana/web3.js';
import { Dot, Sol } from '../../../../../src';
import {
bigIntFromBufferLE,
bigIntToBufferLE,
bigIntFromBufferBE,
bigIntToBufferBE,
Ed25519BIP32,
Eddsa,
HDTree,
} from '@bitgo/sdk-core';
describe('TSS EDDSA key generation and signing', function () {
let MPC: Eddsa;
let hdTree: HDTree;
before('initialize modules', async function () {
hdTree = await Ed25519BIP32.initialize();
MPC = await Eddsa.initialize(hdTree);
});
it('should fail to generate keys with invalid config', function () {
assert.throws(() => MPC.keyShare(0, 2, 3), /Invalid KeyShare config/);
assert.throws(() => MPC.keyShare(5, 2, 3), /Invalid KeyShare config/);
});
it('should sign and verify signature for low number public key', function () {
// We use little endian encoding. This means that the following value is a number that is shorter than 32 Bytes when
// leading zeroes are cut off. This is exactly what happened when we passed it to the sodium library for verifying
// the signature against the public key.
const y = '991b12a1b41b966a3382db32fe9b7fa9f80433940d0b17a1759f1e45ada83f00';
const R = 'b14386bb518b675357a4c79d2439166a5fc5a3a0e1c579c7b829eff1e7a7d967';
const signableHex =
'01000307991b12a1b41b966a3382db32fe9b7fa9f80433940d0b17a1759f1e45ada83f0041536a20902b6d253b111fe5abe87757c123c28cc5fe4eb0da11b5857f3f38e65290384154058de76870e94672fc02e5f96f23e99307f08c56073ebea94cbc57d4d6429b650666264cbcdfe6070749d586d32781608958931e9b8d01636c4f320000000000000000000000000000000000000000000000000000000000000000b43af3bab20c3f39ef3c148c85640614a41043eeb306de5996380f10ec105a8e06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a91073eab4660e8e3a957c7820d52df6338a8c7b60103bf903cb5f82118430922b02040200030c020000005819b217000000000604010502000a0cf40100000000000009';
const userToBitgoGShare = {
i: 1,
y,
R,
gamma: 'fcfd96d4ee4f3399b728b3c820a8eed4a6fa496828e84af2756197993b5df30b',
};
const bitgoToUserGShare = {
i: 3,
y,
R,
gamma: '89d5e45641dc93539a32a6651eaae2448db4d44f6d3040a1390beb14d0225c00',
};
const signature = MPC.signCombine([userToBitgoGShare, bitgoToUserGShare]);
const signablePayloadBuffer = Buffer.from(signableHex, 'hex');
const verificationResult = MPC.verify(signablePayloadBuffer, signature);
verificationResult.should.be.true();
});
it('should generate keys and sign message', function () {
const A = MPC.keyShare(1, 2, 3);
const B = MPC.keyShare(2, 2, 3);
const C = MPC.keyShare(3, 2, 3);
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]);
const C_combine = MPC.keyCombine(C.uShare, [A.yShares[3], B.yShares[3]]);
const message = 'MPC on a Friday night';
const message_buffer = Buffer.from(message);
const incorrect_message = 'MPC on a Monday night';
const incorrect_message_buffer = Buffer.from(incorrect_message);
// signing with A and B
let A_sign_share = MPC.signShare(message_buffer, A_combine.pShare, [A_combine.jShares[2]]);
let B_sign_share = MPC.signShare(message_buffer, B_combine.pShare, [B_combine.jShares[1]]);
let A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]);
let B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]);
let signature = MPC.signCombine([A_sign, B_sign]);
let result = MPC.verify(message_buffer, signature);
result.should.equal(true);
let resultTwo = MPC.verify(incorrect_message_buffer, signature);
resultTwo.should.equal(false);
// signing with A and C
A_sign_share = MPC.signShare(message_buffer, A_combine.pShare, [A_combine.jShares[3]]);
let C_sign_share = MPC.signShare(message_buffer, C_combine.pShare, [C_combine.jShares[1]]);
A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [C_sign_share.rShares[1]], [B.yShares[1]]);
let C_sign = MPC.sign(message_buffer, C_sign_share.xShare, [A_sign_share.rShares[3]], [B.yShares[3]]);
signature = MPC.signCombine([A_sign, C_sign]);
result = MPC.verify(message_buffer, signature);
result.should.equal(true);
resultTwo = MPC.verify(incorrect_message_buffer, signature);
resultTwo.should.equal(false);
// signing with B and C
B_sign_share = MPC.signShare(message_buffer, B_combine.pShare, [B_combine.jShares[3]]);
C_sign_share = MPC.signShare(message_buffer, C_combine.pShare, [C_combine.jShares[2]]);
B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [C_sign_share.rShares[2]], [A.yShares[2]]);
C_sign = MPC.sign(message_buffer, C_sign_share.xShare, [B_sign_share.rShares[3]], [A.yShares[3]]);
signature = MPC.signCombine([B_sign, C_sign]);
result = MPC.verify(message_buffer, signature);
result.should.equal(true);
resultTwo = MPC.verify(incorrect_message_buffer, signature);
resultTwo.should.equal(false);
});
it('should verify BIP32 subkey signature', function () {
const path = 'm/0/1/2';
const A = MPC.keyShare(1, 2, 3);
const B = MPC.keyShare(2, 2, 3);
const C = MPC.keyShare(3, 2, 3);
// Combine shares to common base address.
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]);
// Party A derives subkey P share and new Y shares.
const A_subkey = MPC.keyDerive(A.uShare, [B.yShares[1], C.yShares[1]], path);
// Party B calculates new P share using party A's subkey Y shares.
const B_subkey = MPC.keyCombine(B.uShare, [A_subkey.yShares[2], C.yShares[2]]);
// Derive the public subkeychain separately using the common keychain.
const subkey = hdTree.publicDerive(
{
pk: bigIntFromBufferLE(Buffer.from(A_combine.pShare.y, 'hex')),
chaincode: bigIntFromBufferBE(Buffer.from(A_combine.pShare.chaincode, 'hex')),
},
path,
);
const y = bigIntToBufferLE(subkey.pk, 32).toString('hex');
const chaincode = bigIntToBufferBE(subkey.chaincode, 32).toString('hex');
// Verify the keychain in the subkey P shares equals the separately derived public subkeychain.
A_subkey.pShare.y.should.equal(y);
A_subkey.pShare.chaincode.should.equal(chaincode);
B_subkey.pShare.y.should.equal(y);
B_subkey.pShare.chaincode.should.equal(chaincode);
const message = 'MPC on a Friday night';
const message_buffer = Buffer.from(message);
// Signing with A and B using subkey P shares.
const A_sign_share = MPC.signShare(message_buffer, A_subkey.pShare, [A_combine.jShares[2]]);
const B_sign_share = MPC.signShare(message_buffer, B_subkey.pShare, [B_combine.jShares[1]]);
const A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]);
const B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]);
const signature = MPC.signCombine([A_sign, B_sign]);
const result = MPC.verify(message_buffer, signature);
result.should.equal(true);
// Verify the public key in the signature equals the separately derived public subkey.
signature.y.should.equal(y);
});
it('should derive unhardened child keys', function () {
const A = MPC.keyShare(1, 2, 3);
const B = MPC.keyShare(2, 2, 3);
const C = MPC.keyShare(3, 2, 3);
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const commonKeychain = A_combine.pShare.y + A_combine.pShare.chaincode;
for (let index = 0; index < 10; index++) {
const path = `m/0/0/${index}`;
const derive1 = MPC.deriveUnhardened(commonKeychain, path);
const subkey = MPC.keyDerive(A.uShare, [B.yShares[1], C.yShares[1]], path);
const derive2 = MPC.deriveUnhardened(commonKeychain, path);
const derivedPk = derive1.slice(0, 64);
(subkey.pShare.y + subkey.pShare.chaincode).should.equal(derive1);
derive1.should.equal(derive2, 'derivation should be deterministic');
const solAddress = bs58.encode(Buffer.from(derivedPk, 'hex'));
Sol.Utils.isValidPublicKey(solAddress).should.be.true();
const solPk = new sol.PublicKey(solAddress);
solPk.toBuffer().toString('hex').should.equal(derivedPk);
}
const rootPath = 'm/0';
const rootKeychain = MPC.deriveUnhardened(commonKeychain, rootPath);
const rootPublicKey = Buffer.from(rootKeychain, 'hex').slice(0, 32).toString('hex');
const solPk = new sol.PublicKey(bs58.encode(Buffer.from(rootPublicKey, 'hex')));
solPk.toBuffer().toString('hex').should.equal(rootPublicKey);
});
it('should derive unhardened valid dot child keys', function () {
const A = MPC.keyShare(1, 2, 3);
const B = MPC.keyShare(2, 2, 3);
const C = MPC.keyShare(3, 2, 3);
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const commonKeychain = A_combine.pShare.y + A_combine.pShare.chaincode;
for (let index = 0; index < 10; index++) {
const path = `m/0/0/${index}`;
const derive1 = MPC.deriveUnhardened(commonKeychain, path);
const derive2 = MPC.deriveUnhardened(commonKeychain, path);
const derivedPk = Buffer.from(derive1, 'hex').slice(0, 32).toString('hex');
derive1.should.equal(derive2, 'derivation should be deterministic');
const pubKeyPair = new Dot.KeyPair({ pub: derivedPk });
pubKeyPair.getKeys().pub.should.equal(derivedPk);
}
const rootPath = 'm/';
const rootKeychain = MPC.deriveUnhardened(commonKeychain, rootPath);
const rootPublicKey = Buffer.from(rootKeychain, 'hex').slice(0, 32).toString('hex');
const pubKeyPair = new Dot.KeyPair({ pub: rootPublicKey });
pubKeyPair.getKeys().pub.should.equal(rootPublicKey);
});
it('should fail signing without meeting threshold', function () {
const A = MPC.keyShare(1, 2, 3);
const B = MPC.keyShare(2, 2, 3);
const C = MPC.keyShare(3, 2, 3);
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]);
const message = 'MPC on a Friday night';
const message_buffer = Buffer.from(message, 'utf-8');
const A_sign_share = MPC.signShare(message_buffer, A_combine.pShare, [A_combine.jShares[2]]);
const B_sign_share = MPC.signShare(message_buffer, B_combine.pShare, [B_combine.jShares[1]]);
const A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [B_sign_share.rShares[1]]);
const signature = MPC.signCombine([A_sign]);
MPC.verify(message_buffer, signature).should.equal(false);
});
describe('with specific seed', function () {
it('should generate keys and sign message', function () {
const seed = randomBytes(64);
const A = MPC.keyShare(1, 2, 3, seed);
const B = MPC.keyShare(2, 2, 3, seed);
const C = MPC.keyShare(3, 2, 3, seed);
// Keys should be deterministic when using seed
MPC.keyShare(1, 2, 3, seed).should.deepEqual(A);
MPC.keyShare(2, 2, 3, seed).should.deepEqual(B);
MPC.keyShare(3, 2, 3, seed).should.deepEqual(C);
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]);
const C_combine = MPC.keyCombine(C.uShare, [A.yShares[3], B.yShares[3]]);
const message = 'MPC on a Friday night';
const message_buffer = Buffer.from(message);
// signing with A and B
let A_sign_share = MPC.signShare(message_buffer, A_combine.pShare, [A_combine.jShares[2]], seed);
let B_sign_share = MPC.signShare(message_buffer, B_combine.pShare, [B_combine.jShares[1]], seed);
let A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]);
let B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]);
let signature = MPC.signCombine([A_sign, B_sign]);
let result = MPC.verify(message_buffer, signature);
result.should.equal(true);
// signing with A and C
A_sign_share = MPC.signShare(message_buffer, A_combine.pShare, [A_combine.jShares[3]], seed);
let C_sign_share = MPC.signShare(message_buffer, C_combine.pShare, [C_combine.jShares[1]], seed);
A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [C_sign_share.rShares[1]], [B.yShares[1]]);
let C_sign = MPC.sign(message_buffer, C_sign_share.xShare, [A_sign_share.rShares[3]], [B.yShares[3]]);
signature = MPC.signCombine([A_sign, C_sign]);
result = MPC.verify(message_buffer, signature);
result.should.equal(true);
// signing with B and C
B_sign_share = MPC.signShare(message_buffer, B_combine.pShare, [B_combine.jShares[3]], seed);
C_sign_share = MPC.signShare(message_buffer, C_combine.pShare, [C_combine.jShares[2]], seed);
B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [C_sign_share.rShares[2]], [A.yShares[2]]);
C_sign = MPC.sign(message_buffer, C_sign_share.xShare, [B_sign_share.rShares[3]], [A.yShares[3]]);
signature = MPC.signCombine([B_sign, C_sign]);
result = MPC.verify(message_buffer, signature);
result.should.equal(true);
});
it('should verify BIP32 subkey signature', function () {
const seed = randomBytes(64);
const path = 'm/0/1/2';
const A = MPC.keyShare(1, 2, 3, seed);
const B = MPC.keyShare(2, 2, 3, seed);
const C = MPC.keyShare(3, 2, 3, seed);
// Keys should be deterministic when using seed
MPC.keyShare(1, 2, 3, seed).should.deepEqual(A);
MPC.keyShare(2, 2, 3, seed).should.deepEqual(B);
MPC.keyShare(3, 2, 3, seed).should.deepEqual(C);
// Combine shares to common base address.
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]);
// Party A derives subkey P share and new Y shares.
const A_subkey = MPC.keyDerive(A.uShare, [B.yShares[1], C.yShares[1]], path);
// Party B calculates new P share using party A's subkey Y shares.
const B_subkey = MPC.keyCombine(B.uShare, [A_subkey.yShares[2], C.yShares[2]]);
// Derive the public subkeychain separately using the common keychain.
const subkey = hdTree.publicDerive(
{
pk: bigIntFromBufferLE(Buffer.from(A_combine.pShare.y, 'hex')),
chaincode: bigIntFromBufferBE(Buffer.from(A_combine.pShare.chaincode, 'hex')),
},
path,
);
const y = bigIntToBufferLE(subkey.pk, 32).toString('hex');
const chaincode = bigIntToBufferBE(subkey.chaincode, 32).toString('hex');
// Verify the keychain in the subkey P shares equals the separately derived public subkeychain.
A_subkey.pShare.y.should.equal(y);
A_subkey.pShare.chaincode.should.equal(chaincode);
B_subkey.pShare.y.should.equal(y);
B_subkey.pShare.chaincode.should.equal(chaincode);
const message = 'MPC on a Friday night';
const message_buffer = Buffer.from(message);
// Signing with A and B using subkey P shares.
const A_sign_share = MPC.signShare(message_buffer, A_subkey.pShare, [A_combine.jShares[2]]);
const B_sign_share = MPC.signShare(message_buffer, B_subkey.pShare, [B_combine.jShares[1]]);
const A_sign = MPC.sign(message_buffer, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]);
const B_sign = MPC.sign(message_buffer, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]);
const signature = MPC.signCombine([A_sign, B_sign]);
const result = MPC.verify(message_buffer, signature);
result.should.equal(true);
// Verify the public key in the signature equals the separately derived public subkey.
signature.y.should.equal(y);
});
it('should fail if seed is not length 64', function () {
assert.throws(() => MPC.keyShare(1, 2, 3, randomBytes(33)), /Seed must have length 64/);
assert.throws(() => MPC.keyShare(1, 2, 3, randomBytes(66)), /Seed must have length 64/);
const fakePShare = {
i: 1,
t: 3,
n: 2,
y: 'yString',
u: 'uString',
prefix: 'prefix',
chaincode: 'chaincode',
};
assert.throws(
() => MPC.signShare(Buffer.from('abcd', 'hex'), fakePShare, [], randomBytes(33)),
/Seed must have length 64/,
);
assert.throws(
() => MPC.signShare(Buffer.from('abcd', 'hex'), fakePShare, [], randomBytes(66)),
/Seed must have length 64/,
);
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!