diff --git a/package.json b/package.json index fde7f31e..1fc2e858 100644 --- a/package.json +++ b/package.json @@ -92,10 +92,9 @@ "@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" + "rlp": "^2.2.4" }, "devDependencies": { "@ethereumjs/config-prettier": "^1.1.0", diff --git a/src/account.ts b/src/account.ts index 00f7bbc9..125ef2d9 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,8 +1,13 @@ import * as ethjsUtil from 'ethjs-util' +const { + privateKeyVerify, + publicKeyCreate, + publicKeyVerify, + publicKeyConvert, +} = require('ethereum-cryptography/secp256k1') import * as assert from 'assert' -import * as secp256k1 from 'secp256k1' 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' @@ -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 = Buffer.from(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 Buffer.from(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 = Buffer.from(publicKeyConvert(publicKey, false).slice(1)) } return publicKey } diff --git a/src/hash.ts b/src/hash.ts index 57e06d81..51a30ddc 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') import * as ethjsUtil from '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..2a85d50b 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,4 +1,4 @@ -import * as secp256k1 from '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' @@ -18,12 +18,12 @@ export const ecsign = function( privateKey: Buffer, chainId?: number, ): ECDSASignature { - const sig = secp256k1.ecdsaSign(msgHash, privateKey) + 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, } @@ -46,8 +46,8 @@ export const ecrecover = function( if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } - const senderPubKey = secp256k1.ecdsaRecover(signature, recovery, msgHash) - return toBuffer(secp256k1.publicKeyConvert(senderPubKey, false).slice(1)) + const senderPubKey = ecdsaRecover(signature, recovery, msgHash) + return Buffer.from(publicKeyConvert(senderPubKey, false).slice(1)) } /** diff --git a/test/externals.spec.ts b/test/externals.spec.ts index 1d837c92..4292d4fa 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' diff --git a/test/hash.spec.ts b/test/hash.spec.ts index a76d0c90..bb01089d 100644 --- a/test/hash.spec.ts +++ b/test/hash.spec.ts @@ -16,18 +16,44 @@ 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() { 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() {