Skip to content

Commit

Permalink
feat(sdk-lib-mpc): add utility to verify dkls signatures
Browse files Browse the repository at this point in the history
Ticket: HSM-341
  • Loading branch information
islamaminBitGo committed Apr 17, 2024
1 parent a8528d3 commit 86b1c84
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 19 deletions.
3 changes: 2 additions & 1 deletion modules/sdk-lib-mpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"cbor": "^9.0.1",
"libsodium-wrappers-sumo": "^0.7.9",
"openpgp": "5.10.1",
"paillier-bigint": "3.3.0"
"paillier-bigint": "3.3.0",
"secp256k1": "5.0.0"
},
"devDependencies": {
"@types/lodash": "^4.14.151",
Expand Down
35 changes: 35 additions & 0 deletions modules/sdk-lib-mpc/src/tss/ecdsa-dkls/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Secp256k1Curve } from '../../curves';
import { bigIntFromBufferBE, bigIntToBufferBE } from '../../util';
import { DeserializedDklsSignature } from './types';
import { decode } from 'cbor';
import * as secp256k1 from 'secp256k1';
import { Hash, createHash } from 'crypto';

const delimeter = ':';

/**
* Combines partial signatures from parties participating in DSG.
Expand All @@ -27,3 +31,34 @@ export function combinePartialSignatures(round4MessagePayloads: Uint8Array[], rH
S: new Uint8Array(bigIntToBufferBE(normalizedSig.s)),
};
}

/**
* Verify a DKLs Signature and serialize it to recid:r:s:publickey format.
* @param message - message that was signed.
* @param dklsSignature - R and S values of the ECDSA signature.
* @param commonKeychain - public key appended to chaincode in hex.
* @param hash - optional hash function to apply on message before verifying. Default is sha256.
* @param shouldHash - flag to determine whether message should be hashed before verifying.
* @returns {string} - serialized signature in `recid:r:s:publickey` format
*/
export function verifyAndConvertDklsSignature(
message: Buffer,
dklsSignature: DeserializedDklsSignature,
commonKeychain: string,
hash?: Hash,
shouldHash = true
): string {
const messageToVerify = shouldHash ? (hash || createHash('sha256')).update(message).digest() : message;
const pub0 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 0, messageToVerify, true);
const pub1 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 1, messageToVerify, true);
const truePub = commonKeychain.slice(0, 66);
let recId = 0;
if (truePub === Buffer.from(pub1).toString('hex')) {
recId = 1;
} else if (truePub !== Buffer.from(pub0).toString('hex')) {
throw Error('Invalid Signature');
}
return `${recId}${delimeter}${Buffer.from(dklsSignature.R).toString('hex')}${delimeter}${Buffer.from(
dklsSignature.S
).toString('hex')}${delimeter}${commonKeychain.slice(0, 66)}`;
}
29 changes: 11 additions & 18 deletions modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import should from 'should';
import { Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
import { decode } from 'cbor';
import { Secp256k1Bip32HdTree, bigIntFromBufferBE, bigIntToBufferBE } from '../../../../src';
import * as secp256k1 from 'secp256k1';
import { verifyAndConvertDklsSignature } from '../../../../src/tss/ecdsa-dkls/util';

describe('DKLS Dsg 2x3', function () {
const vectors = [
Expand Down Expand Up @@ -112,24 +112,17 @@ describe('DKLS Dsg 2x3', function () {
const chaincode = bigIntFromBufferBE(Buffer.from(decode(keyShare.toBytes()).root_chain_code));
const hdTree = new Secp256k1Bip32HdTree();
const derivedKey = hdTree.publicDerive({ pk: pk, chaincode: chaincode }, vector.derivationPath);
const pub1 = secp256k1.ecdsaRecover(
Buffer.concat([party1.signature.R, party1.signature.S]),
0,
crypto.createHash('sha256').update(Buffer.from(vector.msgToSign, 'hex')).digest(),
true
);
const pub2 = secp256k1.ecdsaRecover(
Buffer.concat([party1.signature.R, party1.signature.S]),
1,
crypto.createHash('sha256').update(Buffer.from(vector.msgToSign, 'hex')).digest(),
true
);
const derivedPub =
vector.derivationPath === 'm' ? keyShare.publicKey : new Uint8Array(bigIntToBufferBE(derivedKey.pk));
(
(pub1.every((p) => derivedPub.includes(p)) && derivedPub.every((p) => pub1.includes(p))) ||
(pub2.every((p) => derivedPub.includes(p)) && derivedPub.every((p) => pub2.includes(p)))
).should.equal(true);
vector.derivationPath === 'm'
? Buffer.from(keyShare.publicKey).toString('hex')
: bigIntToBufferBE(derivedKey.pk).toString('hex');
const convertedSignature = verifyAndConvertDklsSignature(
Buffer.from(vector.msgToSign, 'hex'),
party1.signature,
derivedPub
);
should.exist(convertedSignature);
convertedSignature.split(':').length.should.equal(4);
});
});

Expand Down

0 comments on commit 86b1c84

Please sign in to comment.