From da4827c592238264ad69a2460a518ce2169d57ad Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Wed, 10 Jun 2020 19:24:50 +0200 Subject: [PATCH 1/6] Remove keccak native dependency. --- package.json | 2 +- src/account.ts | 19 ++++++++++++------- src/hash.ts | 22 ++++++++++++++++++---- src/signature.ts | 7 ++++--- test/externals.spec.ts | 1 - 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index fde7f31e..7f491804 100644 --- a/package.json +++ b/package.json @@ -92,8 +92,8 @@ "@types/bn.js": "^4.11.3", "bn.js": "^5.1.2", "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", "ethjs-util": "0.1.6", - "keccak": "^3.0.0", "rlp": "^2.2.4", "secp256k1": "^4.0.1" }, diff --git a/src/account.ts b/src/account.ts index 0d1fb63e..d4bba115 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,6 +1,11 @@ const ethjsUtil = require('ethjs-util') +const { + privateKeyVerify, + publicKeyCreate, + publicKeyVerify, + publicKeyConvert, +} = require('ethereum-cryptography/shims/hdkey-secp256k1v3') import * as assert from 'assert' -import * as secp256k1 from 'secp256k1' import * as BN from 'bn.js' import { zeros, bufferToHex, toBuffer } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' @@ -119,7 +124,7 @@ export const generateAddress2 = function(from: Buffer, salt: Buffer, initCode: B * Checks if the private key satisfies the rules of the curve secp256k1. */ export const isValidPrivate = function(privateKey: Buffer): boolean { - return secp256k1.privateKeyVerify(privateKey) + return privateKeyVerify(privateKey) } /** @@ -132,14 +137,14 @@ export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = fal assertIsBuffer(publicKey) if (publicKey.length === 64) { // Convert to SEC1 for secp256k1 - return secp256k1.publicKeyVerify(Buffer.concat([Buffer.from([4]), publicKey])) + return publicKeyVerify(Buffer.concat([Buffer.from([4]), publicKey])) } if (!sanitize) { return false } - return secp256k1.publicKeyVerify(publicKey) + return publicKeyVerify(publicKey) } /** @@ -151,7 +156,7 @@ export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = fal export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): Buffer { assertIsBuffer(pubKey) if (sanitize && pubKey.length !== 64) { - pubKey = toBuffer(secp256k1.publicKeyConvert(pubKey, false).slice(1)) + pubKey = toBuffer(publicKeyConvert(pubKey, false).slice(1)) } assert(pubKey.length === 64) // Only take the lower 160bits of the hash @@ -174,7 +179,7 @@ export const privateToAddress = function(privateKey: Buffer): Buffer { export const privateToPublic = function(privateKey: Buffer): Buffer { assertIsBuffer(privateKey) // skip the type flag and use the X, Y points - return toBuffer(secp256k1.publicKeyCreate(privateKey, false).slice(1)) + return toBuffer(publicKeyCreate(privateKey, false).slice(1)) } /** @@ -183,7 +188,7 @@ export const privateToPublic = function(privateKey: Buffer): Buffer { export const importPublic = function(publicKey: Buffer): Buffer { assertIsBuffer(publicKey) if (publicKey.length !== 64) { - publicKey = toBuffer(secp256k1.publicKeyConvert(publicKey, false).slice(1)) + publicKey = toBuffer(publicKeyConvert(publicKey, false).slice(1)) } return publicKey } diff --git a/src/hash.ts b/src/hash.ts index e9e40ca7..44bcc8f1 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -1,4 +1,4 @@ -const createKeccakHash = require('keccak') +const { keccak224, keccak384, keccak256: k256, keccak512 } = require('ethereum-cryptography/keccak') const createHash = require('create-hash') const ethjsUtil = require('ethjs-util') import * as rlp from 'rlp' @@ -12,9 +12,23 @@ import { assertIsString, assertIsBuffer, assertIsArray, assertIsHexString } from */ export const keccak = function(a: Buffer, bits: number = 256): Buffer { assertIsBuffer(a) - return createKeccakHash(`keccak${bits}`) - .update(a) - .digest() + switch (bits) { + case 224: { + return keccak224(a) + } + case 256: { + return k256(a) + } + case 384: { + return keccak384(a) + } + case 512: { + return keccak512(a) + } + default: { + throw new Error(`Invald algorithm: keccak${bits}`) + } + } } /** diff --git a/src/signature.ts b/src/signature.ts index 24a226e7..cc84e3c7 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,3 +1,4 @@ +const { recover, sign, publicKeyConvert } = require('ethereum-cryptography/shims/hdkey-secp256k1v3') import * as secp256k1 from 'secp256k1' import * as BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex } from './bytes' @@ -18,8 +19,8 @@ export const ecsign = function( privateKey: Buffer, chainId?: number, ): ECDSASignature { - const sig = secp256k1.ecdsaSign(msgHash, privateKey) - const recovery: number = sig.recid + const sig = sign(msgHash, privateKey) + const recovery: number = sig.recovery const ret = { r: toBuffer(sig.signature.slice(0, 32)), @@ -47,7 +48,7 @@ export const ecrecover = function( throw new Error('Invalid signature v value') } const senderPubKey = secp256k1.ecdsaRecover(signature, recovery, msgHash) - return toBuffer(secp256k1.publicKeyConvert(senderPubKey, false).slice(1)) + return toBuffer(publicKeyConvert(senderPubKey, false).slice(1)) } /** diff --git a/test/externals.spec.ts b/test/externals.spec.ts index 6d8402ba..5841ede9 100644 --- a/test/externals.spec.ts +++ b/test/externals.spec.ts @@ -2,7 +2,6 @@ import * as assert from 'assert' import * as BN_export from 'bn.js' import * as rlp_export from 'rlp' -import * as secp256k1_export from 'secp256k1' import * as src from '../src' From 6a6af8ea5209d95fd17451e7b4165a203e10f602 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Wed, 10 Jun 2020 20:42:11 +0200 Subject: [PATCH 2/6] Remove secp256k1 native dependency. --- package.json | 3 +-- src/signature.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7f491804..1fc2e858 100644 --- a/package.json +++ b/package.json @@ -94,8 +94,7 @@ "create-hash": "^1.1.2", "ethereum-cryptography": "^0.1.3", "ethjs-util": "0.1.6", - "rlp": "^2.2.4", - "secp256k1": "^4.0.1" + "rlp": "^2.2.4" }, "devDependencies": { "@ethereumjs/config-prettier": "^1.1.0", diff --git a/src/signature.ts b/src/signature.ts index cc84e3c7..030b4953 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,5 +1,5 @@ -const { recover, sign, publicKeyConvert } = require('ethereum-cryptography/shims/hdkey-secp256k1v3') -import * as secp256k1 from 'secp256k1' +const { sign, publicKeyConvert } = require('ethereum-cryptography/shims/hdkey-secp256k1v3') +const { ecdsaRecover } = require('ethereum-cryptography/secp256k1') import * as BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex } from './bytes' import { keccak } from './hash' @@ -47,7 +47,7 @@ export const ecrecover = function( if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } - const senderPubKey = secp256k1.ecdsaRecover(signature, recovery, msgHash) + const senderPubKey = ecdsaRecover(signature, recovery, msgHash) return toBuffer(publicKeyConvert(senderPubKey, false).slice(1)) } From 28d1354ab1cdb76aff9a676806305324d93ce6c4 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Thu, 11 Jun 2020 16:33:33 +0200 Subject: [PATCH 3/6] Adds missing keccak tests. --- test/hash.spec.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/hash.spec.ts b/test/hash.spec.ts index a76d0c90..e024d024 100644 --- a/test/hash.spec.ts +++ b/test/hash.spec.ts @@ -16,12 +16,32 @@ import { } from '../src' describe('keccak', function() { - it('should produce a hash', function() { + it('should produce a keccak224 hash', function() { + const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' + const r = '9e66938bd8f32c8610444bb524630db496bd58b689f9733182df63ba' + const hash = keccak(toBuffer(msg), 224) + assert.equal(hash.toString('hex'), r) + }) + it('should produce a keccak256 hash', function() { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' const r = '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28' const hash = keccak(toBuffer(msg)) assert.equal(hash.toString('hex'), r) }) + it('should produce a keccak384 hash', function() { + const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' + const r = + '923e0f6a1c324a698139c3f3abbe88ac70bf2e7c02b26192c6124732555a32cef18e81ac91d5d97ce969745409c5bbc6' + const hash = keccak(toBuffer(msg), 384) + assert.equal(hash.toString('hex'), r) + }) + it('should produce a keccak512 hash', function() { + const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' + const r = + '36fdacd0339307068e9ed191773a6f11f6f9f99016bd50f87fd529ab7c87e1385f2b7ef1ac257cc78a12dcb3e5804254c6a7b404a6484966b831eadc721c3d24' + const hash = keccak(toBuffer(msg), 512) + assert.equal(hash.toString('hex'), r) + }) it('should error if input is not Buffer', function() { const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' assert.throws(function() { From 1d720123e58881f60b0d162393f6d60b84553d30 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Thu, 11 Jun 2020 16:47:30 +0200 Subject: [PATCH 4/6] Adds missing keccak test. --- test/hash.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/hash.spec.ts b/test/hash.spec.ts index e024d024..bb01089d 100644 --- a/test/hash.spec.ts +++ b/test/hash.spec.ts @@ -48,6 +48,12 @@ describe('keccak', function() { keccak((msg) as Buffer) }) }) + it('should error if provided incorrect bits', function() { + const msg = '0x3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1' + assert.throws(function() { + keccak(toBuffer(msg), 1024) + }) + }) }) describe('keccak256', function() { From eda4d21cdbc3ba046037861b1b76451f251b9afd Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 19 Jun 2020 21:32:15 +0200 Subject: [PATCH 5/6] Use secp256k1 lib instead of shims. --- src/account.ts | 8 ++++---- src/signature.ts | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/account.ts b/src/account.ts index d4bba115..b5b54a0b 100644 --- a/src/account.ts +++ b/src/account.ts @@ -4,7 +4,7 @@ const { publicKeyCreate, publicKeyVerify, publicKeyConvert, -} = require('ethereum-cryptography/shims/hdkey-secp256k1v3') +} = require('ethereum-cryptography/secp256k1') import * as assert from 'assert' import * as BN from 'bn.js' import { zeros, bufferToHex, toBuffer } from './bytes' @@ -156,7 +156,7 @@ export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = fal export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): Buffer { assertIsBuffer(pubKey) if (sanitize && pubKey.length !== 64) { - pubKey = toBuffer(publicKeyConvert(pubKey, false).slice(1)) + pubKey = Buffer.from(publicKeyConvert(pubKey, false).slice(1)) } assert(pubKey.length === 64) // Only take the lower 160bits of the hash @@ -179,7 +179,7 @@ export const privateToAddress = function(privateKey: Buffer): Buffer { export const privateToPublic = function(privateKey: Buffer): Buffer { assertIsBuffer(privateKey) // skip the type flag and use the X, Y points - return toBuffer(publicKeyCreate(privateKey, false).slice(1)) + return Buffer.from(publicKeyCreate(privateKey, false)).slice(1) } /** @@ -188,7 +188,7 @@ export const privateToPublic = function(privateKey: Buffer): Buffer { export const importPublic = function(publicKey: Buffer): Buffer { assertIsBuffer(publicKey) if (publicKey.length !== 64) { - publicKey = toBuffer(publicKeyConvert(publicKey, false).slice(1)) + publicKey = Buffer.from(publicKeyConvert(publicKey, false).slice(1)) } return publicKey } diff --git a/src/signature.ts b/src/signature.ts index 030b4953..2a85d50b 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,5 +1,4 @@ -const { sign, publicKeyConvert } = require('ethereum-cryptography/shims/hdkey-secp256k1v3') -const { ecdsaRecover } = require('ethereum-cryptography/secp256k1') +const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require('ethereum-cryptography/secp256k1') import * as BN from 'bn.js' import { toBuffer, setLengthLeft, bufferToHex } from './bytes' import { keccak } from './hash' @@ -19,12 +18,12 @@ export const ecsign = function( privateKey: Buffer, chainId?: number, ): ECDSASignature { - const sig = sign(msgHash, privateKey) - const recovery: number = sig.recovery + const sig = ecdsaSign(msgHash, privateKey) + const recovery: number = sig.recid const ret = { - r: toBuffer(sig.signature.slice(0, 32)), - s: toBuffer(sig.signature.slice(32, 64)), + r: Buffer.from(sig.signature.slice(0, 32)), + s: Buffer.from(sig.signature.slice(32, 64)), v: chainId ? recovery + (chainId * 2 + 35) : recovery + 27, } @@ -48,7 +47,7 @@ export const ecrecover = function( throw new Error('Invalid signature v value') } const senderPubKey = ecdsaRecover(signature, recovery, msgHash) - return toBuffer(publicKeyConvert(senderPubKey, false).slice(1)) + return Buffer.from(publicKeyConvert(senderPubKey, false).slice(1)) } /** From ef5a7ac1e3bfc0ee43c30a65cface61a392a5832 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Mon, 22 Jun 2020 10:26:59 +0200 Subject: [PATCH 6/6] Remove unused import. --- src/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.ts b/src/account.ts index b5b54a0b..2cabc8c6 100644 --- a/src/account.ts +++ b/src/account.ts @@ -7,7 +7,7 @@ const { } = require('ethereum-cryptography/secp256k1') import * as assert from 'assert' import * as BN from 'bn.js' -import { zeros, bufferToHex, toBuffer } from './bytes' +import { zeros, bufferToHex } from './bytes' import { keccak, keccak256, keccakFromString, rlphash } from './hash' import { assertIsHexString, assertIsBuffer } from './helpers'