Skip to content

Commit 5e73818

Browse files
feat(sdk-lib-mpc): add utility to verify dkls signatures
Ticket: HSM-341
1 parent 1220446 commit 5e73818

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

modules/sdk-lib-mpc/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"cbor": "^9.0.1",
4646
"libsodium-wrappers-sumo": "^0.7.9",
4747
"openpgp": "5.10.1",
48-
"paillier-bigint": "3.3.0"
48+
"paillier-bigint": "3.3.0",
49+
"secp256k1": "5.0.0"
4950
},
5051
"devDependencies": {
5152
"@types/lodash": "^4.14.151",

modules/sdk-lib-mpc/src/tss/ecdsa-dkls/util.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { Secp256k1Curve } from '../../curves';
33
import { bigIntFromBufferBE, bigIntToBufferBE } from '../../util';
44
import { DeserializedDklsSignature } from './types';
55
import { decode } from 'cbor';
6+
import * as secp256k1 from 'secp256k1';
7+
import { Hash, createHash } from 'crypto';
8+
9+
const delimeter = ':';
610

711
/**
812
* Combines partial signatures from parties participating in DSG.
@@ -27,3 +31,34 @@ export function combinePartialSignatures(round4MessagePayloads: Uint8Array[], rH
2731
S: new Uint8Array(bigIntToBufferBE(normalizedSig.s)),
2832
};
2933
}
34+
35+
/**
36+
* Verify a DKLs Signature and serialize it to recid:r:s:publickey format.
37+
* @param message - message that was signed.
38+
* @param dklsSignature - R and S values of the ECDSA signature.
39+
* @param commonKeychain - public key appended to chaincode in hex.
40+
* @param hash - optional hash function to apply on message before verifying. Default is sha256.
41+
* @param shouldHash - flag to determine whether message should be hashed before verifying.
42+
* @returns {string} - serialized signature in `recid:r:s:publickey` format
43+
*/
44+
export function verifyAndConvertDklsSignature(
45+
message: Buffer,
46+
dklsSignature: DeserializedDklsSignature,
47+
commonKeychain: string,
48+
hash?: Hash,
49+
shouldHash = true
50+
): string {
51+
const messageToVerify = shouldHash ? (hash || createHash('sha256')).update(message).digest() : message;
52+
const pub0 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 0, messageToVerify, true);
53+
const pub1 = secp256k1.ecdsaRecover(Buffer.concat([dklsSignature.R, dklsSignature.S]), 1, messageToVerify, true);
54+
const truePub = commonKeychain.slice(0, 66);
55+
let recId = 0;
56+
if (truePub === Buffer.from(pub1).toString('hex')) {
57+
recId = 1;
58+
} else if (truePub !== Buffer.from(pub0).toString('hex')) {
59+
throw Error('Invalid Signature');
60+
}
61+
return `${recId}${delimeter}${Buffer.from(dklsSignature.R).toString('hex')}${delimeter}${Buffer.from(
62+
dklsSignature.S
63+
).toString('hex')}${delimeter}${truePub}`;
64+
}

modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDsg.ts

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import should from 'should';
55
import { Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
66
import { decode } from 'cbor';
77
import { Secp256k1Bip32HdTree, bigIntFromBufferBE, bigIntToBufferBE } from '../../../../src';
8-
import * as secp256k1 from 'secp256k1';
8+
import { verifyAndConvertDklsSignature } from '../../../../src/tss/ecdsa-dkls/util';
99

1010
describe('DKLS Dsg 2x3', function () {
1111
const vectors = [
@@ -112,24 +112,17 @@ describe('DKLS Dsg 2x3', function () {
112112
const chaincode = bigIntFromBufferBE(Buffer.from(decode(keyShare.toBytes()).root_chain_code));
113113
const hdTree = new Secp256k1Bip32HdTree();
114114
const derivedKey = hdTree.publicDerive({ pk: pk, chaincode: chaincode }, vector.derivationPath);
115-
const pub1 = secp256k1.ecdsaRecover(
116-
Buffer.concat([party1.signature.R, party1.signature.S]),
117-
0,
118-
crypto.createHash('sha256').update(Buffer.from(vector.msgToSign, 'hex')).digest(),
119-
true
120-
);
121-
const pub2 = secp256k1.ecdsaRecover(
122-
Buffer.concat([party1.signature.R, party1.signature.S]),
123-
1,
124-
crypto.createHash('sha256').update(Buffer.from(vector.msgToSign, 'hex')).digest(),
125-
true
126-
);
127115
const derivedPub =
128-
vector.derivationPath === 'm' ? keyShare.publicKey : new Uint8Array(bigIntToBufferBE(derivedKey.pk));
129-
(
130-
(pub1.every((p) => derivedPub.includes(p)) && derivedPub.every((p) => pub1.includes(p))) ||
131-
(pub2.every((p) => derivedPub.includes(p)) && derivedPub.every((p) => pub2.includes(p)))
132-
).should.equal(true);
116+
vector.derivationPath === 'm'
117+
? Buffer.from(keyShare.publicKey).toString('hex')
118+
: bigIntToBufferBE(derivedKey.pk).toString('hex');
119+
const convertedSignature = verifyAndConvertDklsSignature(
120+
Buffer.from(vector.msgToSign, 'hex'),
121+
party1.signature,
122+
derivedPub
123+
);
124+
should.exist(convertedSignature);
125+
convertedSignature.split(':').length.should.equal(4);
133126
});
134127
});
135128

0 commit comments

Comments
 (0)