- Allow for hasher to secp256k1 sign/verify functions (#657)
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 3.2.0-beta.x
|
||||
|
||||
- Allow for (optional, default blake2) hasher to secp256k1 sign/verify functions
|
||||
|
||||
## 3.1.1 Aug 10, 2020
|
||||
|
||||
- Add `deriveAddress(<address>, '/<soft>/<soft-b>')` via sr25519 soft
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.1",
|
||||
"@polkadot/dev": "^0.55.36",
|
||||
"@polkadot/ts": "^0.3.36",
|
||||
"@polkadot/dev": "^0.55.37",
|
||||
"@polkadot/ts": "^0.3.37",
|
||||
"@types/jest": "^26.0.9"
|
||||
},
|
||||
"version": "3.1.1"
|
||||
|
||||
@@ -24,6 +24,6 @@ export default function keccakAsU8a (value: Buffer | Uint8Array | string): Uint8
|
||||
return isReady()
|
||||
? keccak256(u8aToU8a(value))
|
||||
: new Uint8Array(
|
||||
jssha3.keccak256.update(value).arrayBuffer()
|
||||
jssha3.keccak256.update(u8aToU8a(value)).arrayBuffer()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ import blake2AsU8a from '../blake2/asU8a';
|
||||
const HDKD = compactAddLength(stringToU8a('Secp256k1HDKD'));
|
||||
|
||||
export default function deriveHard (seed: Uint8Array, chainCode: Uint8Array): Uint8Array {
|
||||
// NOTE This is specific to the Substrate HDD derivation, so always use the blake2 hasher
|
||||
return blake2AsU8a(u8aConcat(HDKD, seed, chainCode), 256);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import hasher from './hasher';
|
||||
|
||||
describe('secp256k1Hasher', (): void => {
|
||||
it('fails with unknown hasher', (): void => {
|
||||
expect(
|
||||
() => hasher('unknown' as 'blake2', 'testing')
|
||||
).toThrow(/Unsupported secp256k1 hasher 'unknown', expected one of blake2, keccak/);
|
||||
});
|
||||
|
||||
it('creates a blake2 hash', (): void => {
|
||||
expect(
|
||||
hasher('blake2', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25])
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a keccak hash', (): void => {
|
||||
expect(
|
||||
hasher('keccak', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([78, 3, 101, 122, 234, 69, 169, 79, 199, 212, 123, 168, 38, 200, 214, 103, 192, 209, 230, 227, 58, 100, 160, 54, 236, 68, 245, 143, 161, 45, 108, 69])
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { HashType } from './types';
|
||||
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
import { keccakAsU8a } from '../keccak';
|
||||
|
||||
const HASH_TYPES = ['blake2', 'keccak'];
|
||||
|
||||
export default function secp256k1Hasher (hashType: HashType, data: Uint8Array | string): Uint8Array {
|
||||
if (hashType === 'blake2') {
|
||||
return blake2AsU8a(data, 256);
|
||||
} else if (hashType === 'keccak') {
|
||||
return keccakAsU8a(data);
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported secp256k1 hasher '${hashType as string}', expected one of ${HASH_TYPES.join(', ')}`);
|
||||
}
|
||||
@@ -6,7 +6,36 @@ import { u8aToHex, hexToU8a } from '@polkadot/util';
|
||||
|
||||
import { mnemonicToMiniSecret } from '../../mnemonic';
|
||||
import { secp256k1KeypairFromSeed } from '..';
|
||||
import tests from './testing';
|
||||
|
||||
// mnemonic, secret, public, account_id
|
||||
type Test = [string, string, string, string];
|
||||
|
||||
const tests: Test[] = [
|
||||
[
|
||||
'life fee table ahead modify maximum dumb such tobacco boss dry nurse',
|
||||
'0xf2360e871c830d397fe221382b503f07ddd8763df81a94bb2504390a2fb91f59',
|
||||
'0x036b0aa6beab469dd2b748a0ff5ddbe3d13df1e15c9d28a2aa057212994e127bea',
|
||||
'0xae8e8fcacbaeb607bcdf0bbd7e615f2b4ef484ee54f19d68a7393fb6db2dd9cd'
|
||||
],
|
||||
[
|
||||
'tide survey cradle cover column ugly author wait eye state elder blame',
|
||||
'0x5385355a5118ec732b9dbcf1668ba21db38b07cf79082dafa9a7cc4b52e4abb0',
|
||||
'0x03929e4f93cdad265751ad8f6365185d8e937610d19b510400f5867d542d60a313',
|
||||
'0xf80ea815da66c42f870b687e1530770d5a7936ae81a147b009506d85bd6d621c'
|
||||
],
|
||||
[
|
||||
'laugh fish flee cake approve butter april dynamic myth license ticket lobster',
|
||||
'0x83ec65cf9a8a7442d808aef6f8987599f1ba3be880769bb3a20621b13adbd476',
|
||||
'0x0388299e4cfaa33d180a026bd54a46ad98df129a131320a9d2fd6f80e64bc3db39',
|
||||
'0x35036238dd195f4c2169379354bda6cba5746f67bde03ef59a77a4cea80729bc'
|
||||
],
|
||||
[
|
||||
'animal thing fork recipe exotic pilot inquiry pledge obey slab obtain reveal',
|
||||
'0x0fd50580eb5a58b0eee60c77656dffa50094b539262366f1227d3babfd7343e5',
|
||||
'0x036edc954685ad89f0a23b0fb1eb2b9c3a8600eee9091c758426dfb2bc7889a7c3',
|
||||
'0x2a94b10d1f28810dc4628e7e424b2d08bd3d17fb08f9416d112f17e86c8fa77c'
|
||||
]
|
||||
];
|
||||
|
||||
describe('secp256k1KeypairFromSeed', (): void => {
|
||||
const TEST = hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae');
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Keypair } from '../../types';
|
||||
import { assert, hexToU8a } from '@polkadot/util';
|
||||
|
||||
import elliptic from 'elliptic';
|
||||
import { assert, hexToU8a } from '@polkadot/util';
|
||||
|
||||
const EC = elliptic.ec;
|
||||
const ec = new EC('secp256k1');
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
// mnemonic, secret, public, account_id
|
||||
type Test = [string, string, string, string];
|
||||
|
||||
const tests: Test[] = [
|
||||
[
|
||||
'life fee table ahead modify maximum dumb such tobacco boss dry nurse',
|
||||
'0xf2360e871c830d397fe221382b503f07ddd8763df81a94bb2504390a2fb91f59',
|
||||
'0x036b0aa6beab469dd2b748a0ff5ddbe3d13df1e15c9d28a2aa057212994e127bea',
|
||||
'0xae8e8fcacbaeb607bcdf0bbd7e615f2b4ef484ee54f19d68a7393fb6db2dd9cd'
|
||||
],
|
||||
[
|
||||
'tide survey cradle cover column ugly author wait eye state elder blame',
|
||||
'0x5385355a5118ec732b9dbcf1668ba21db38b07cf79082dafa9a7cc4b52e4abb0',
|
||||
'0x03929e4f93cdad265751ad8f6365185d8e937610d19b510400f5867d542d60a313',
|
||||
'0xf80ea815da66c42f870b687e1530770d5a7936ae81a147b009506d85bd6d621c'
|
||||
],
|
||||
[
|
||||
'laugh fish flee cake approve butter april dynamic myth license ticket lobster',
|
||||
'0x83ec65cf9a8a7442d808aef6f8987599f1ba3be880769bb3a20621b13adbd476',
|
||||
'0x0388299e4cfaa33d180a026bd54a46ad98df129a131320a9d2fd6f80e64bc3db39',
|
||||
'0x35036238dd195f4c2169379354bda6cba5746f67bde03ef59a77a4cea80729bc'
|
||||
],
|
||||
[
|
||||
'animal thing fork recipe exotic pilot inquiry pledge obey slab obtain reveal',
|
||||
'0x0fd50580eb5a58b0eee60c77656dffa50094b539262366f1227d3babfd7343e5',
|
||||
'0x036edc954685ad89f0a23b0fb1eb2b9c3a8600eee9091c758426dfb2bc7889a7c3',
|
||||
'0x2a94b10d1f28810dc4628e7e424b2d08bd3d17fb08f9416d112f17e86c8fa77c'
|
||||
]
|
||||
];
|
||||
|
||||
export default tests;
|
||||
@@ -3,9 +3,12 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Keypair } from '../types';
|
||||
import { assert, u8aToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
import { HashType } from './types';
|
||||
|
||||
import elliptic from 'elliptic';
|
||||
import { assert, u8aConcat } from '@polkadot/util';
|
||||
|
||||
import hasher from './hasher';
|
||||
|
||||
const EC = elliptic.ec;
|
||||
const ec = new EC('secp256k1');
|
||||
@@ -14,12 +17,11 @@ const ec = new EC('secp256k1');
|
||||
* @name secp256k1Sign
|
||||
* @description Returns message signature of `message`, using the supplied pair
|
||||
*/
|
||||
export default function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>): Uint8Array {
|
||||
export default function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2'): Uint8Array {
|
||||
assert(secretKey?.length === 32, 'Expected valid secp256k1 secretKey, 32-bytes');
|
||||
|
||||
const messageHash = blake2AsU8a(u8aToU8a(message), 256);
|
||||
const key = ec.keyFromPrivate(secretKey);
|
||||
const ecsig = key.sign(messageHash);
|
||||
const ecsig = key.sign(hasher(hashType, message));
|
||||
const rParam = new Uint8Array(ecsig.r.toArray());
|
||||
const sParam = new Uint8Array(ecsig.s.toArray());
|
||||
const recoveryParam = Uint8Array.of(ecsig.recoveryParam || 0);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { stringToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
|
||||
import randomAsU8a from '../random/asU8a';
|
||||
import pairFromSeed from './keypair/fromSeed';
|
||||
import hasher from './hasher';
|
||||
import sign from './sign';
|
||||
import verify from './verify';
|
||||
|
||||
@@ -29,8 +30,24 @@ describe('sign and verify', (): void => {
|
||||
it('can sign and verify a message by random key', (): void => {
|
||||
const pair = pairFromSeed(randomAsU8a());
|
||||
const signature = sign(MESSAGE, pair);
|
||||
const address = blake2AsU8a(pair.publicKey, 256);
|
||||
const address = hasher('blake2', pair.publicKey);
|
||||
|
||||
expect(verify(MESSAGE, signature, address)).toBe(true);
|
||||
});
|
||||
|
||||
it('can sign and verify a message by random key (keccak)', (): void => {
|
||||
const pair = pairFromSeed(randomAsU8a());
|
||||
const signature = sign(MESSAGE, pair, 'keccak');
|
||||
const address = hasher('keccak', pair.publicKey);
|
||||
|
||||
expect(verify(MESSAGE, signature, address, 'keccak')).toBe(true);
|
||||
});
|
||||
|
||||
it('can fals verification on hasher mismatches', (): void => {
|
||||
const pair = pairFromSeed(randomAsU8a());
|
||||
const signature = sign(MESSAGE, pair, 'keccak');
|
||||
const address = hasher('keccak', pair.publicKey);
|
||||
|
||||
expect(verify(MESSAGE, signature, address, 'blake2')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export type HashType = 'blake2' | 'keccak';
|
||||
@@ -2,9 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
import { HashType } from './types';
|
||||
|
||||
import elliptic from 'elliptic';
|
||||
import { u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import hasher from './hasher';
|
||||
|
||||
const EC = elliptic.ec;
|
||||
const ec = new EC('secp256k1');
|
||||
@@ -13,7 +16,7 @@ const ec = new EC('secp256k1');
|
||||
* @name secp256k1Verify
|
||||
* @description Verifies the signature of `message`, using the supplied pair
|
||||
*/
|
||||
export default function secp256k1Verify (message: Uint8Array | string, signature: Uint8Array | string, address: Uint8Array | string): boolean {
|
||||
export default function secp256k1Verify (message: Uint8Array | string, signature: Uint8Array | string, address: Uint8Array | string, hashType: HashType = 'blake2'): boolean {
|
||||
const signatureU8a = u8aToU8a(signature);
|
||||
const sig = {
|
||||
r: signatureU8a.slice(0, 32),
|
||||
@@ -22,9 +25,9 @@ export default function secp256k1Verify (message: Uint8Array | string, signature
|
||||
const recovery = signatureU8a[64];
|
||||
const publicKey = new Uint8Array(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
||||
ec.recoverPubKey(blake2AsU8a(message, 256), sig, recovery)
|
||||
ec.recoverPubKey(hasher(hashType, message), sig, recovery)
|
||||
.encodeCompressed(null)
|
||||
);
|
||||
|
||||
return Buffer.compare(blake2AsU8a(publicKey, 256), u8aToU8a(address)) === 0;
|
||||
return Buffer.compare(hasher(hashType, publicKey), u8aToU8a(address)) === 0;
|
||||
}
|
||||
|
||||
@@ -2731,9 +2731,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/dev@npm:^0.55.36":
|
||||
version: 0.55.36
|
||||
resolution: "@polkadot/dev@npm:0.55.36"
|
||||
"@polkadot/dev@npm:^0.55.37":
|
||||
version: 0.55.37
|
||||
resolution: "@polkadot/dev@npm:0.55.37"
|
||||
dependencies:
|
||||
"@babel/cli": ^7.10.5
|
||||
"@babel/core": ^7.11.1
|
||||
@@ -2820,7 +2820,7 @@ __metadata:
|
||||
polkadot-exec-typedoc: scripts/polkadot-exec-typedoc.js
|
||||
polkadot-exec-vuepress: scripts/polkadot-exec-vuepress.js
|
||||
polkadot-exec-webpack: scripts/polkadot-exec-webpack.js
|
||||
checksum: d651ef83ef38cabd7f40e3afe2d696d5bb01d5ce6d7ac6a0f5840e6f3795840412136bf48e8e57035b64acdff67699617523a97ec348b882af930128a056d28f
|
||||
checksum: f31f1d6f5ad62c7ccd0f889644e8a1e223f853f27e47656d633fb0162a604efa459d3469aa16bffa3a806edc2205158da8223afa35ca97f2af39ec6ba6741ce2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2834,12 +2834,12 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/ts@npm:^0.3.36":
|
||||
version: 0.3.36
|
||||
resolution: "@polkadot/ts@npm:0.3.36"
|
||||
"@polkadot/ts@npm:^0.3.37":
|
||||
version: 0.3.37
|
||||
resolution: "@polkadot/ts@npm:0.3.37"
|
||||
dependencies:
|
||||
"@types/chrome": ^0.0.122
|
||||
checksum: b5afdb242a5a0ab0e94e8b9b0a00c2e52dc765ba4f4ffb4694c3246e38a968920670c695773b9ac60201bcc1585a26dbfedfa4e510b3b4bc3c4a7e9bee7021ae
|
||||
checksum: 46be0d8e509ea7032ad51933a4e722b7056d5d867bfcafafe5f3449d8008676dd8f4bf147fe5c58e4551f5f1df2a7176fe72c7e8e388809ac487997866218ef8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15884,8 +15884,8 @@ __metadata:
|
||||
resolution: "root-workspace-0b6124@workspace:."
|
||||
dependencies:
|
||||
"@babel/core": ^7.11.1
|
||||
"@polkadot/dev": ^0.55.36
|
||||
"@polkadot/ts": ^0.3.36
|
||||
"@polkadot/dev": ^0.55.37
|
||||
"@polkadot/ts": ^0.3.37
|
||||
"@types/jest": ^26.0.9
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
Reference in New Issue
Block a user