diff --git a/package-lock.json b/package-lock.json index 20860b3e90..76c7c993bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19499,6 +19499,7 @@ "base64url": "^3.0.1", "bl": "^1.1.2", "debug": "^4.3.3", + "ethereum-cryptography": "^1.0.3", "hi-base32": "^0.5.0", "inherits": "^2.0.1", "ip": "^1.1.3", @@ -19509,7 +19510,6 @@ "multiaddr": "^10.0.1", "rlp": "^3.0.0", "scanf": "^1.1.2", - "secp256k1": "^4.0.2", "snappyjs": "^0.6.1" }, "devDependencies": { @@ -19521,7 +19521,6 @@ "@types/keccak": "^3.0.1", "@types/ms": "^0.7.30", "@types/node": "^16.11.7", - "@types/secp256k1": "^4.0.1", "@types/tape": "^4.13.2", "chalk": "^2.4.2", "eslint": "^6.8.0", @@ -20428,13 +20427,13 @@ "@types/lru-cache": "^5.1.0", "@types/ms": "^0.7.30", "@types/node": "^16.11.7", - "@types/secp256k1": "^4.0.1", "@types/tape": "^4.13.2", "base64url": "^3.0.1", "bl": "^1.1.2", "chalk": "^2.4.2", "debug": "^4.3.3", "eslint": "^6.8.0", + "ethereum-cryptography": "^1.0.3", "hi-base32": "^0.5.0", "inherits": "^2.0.1", "ip": "^1.1.3", @@ -20447,7 +20446,6 @@ "prettier": "^2.0.5", "rlp": "^3.0.0", "scanf": "^1.1.2", - "secp256k1": "^4.0.2", "snappyjs": "^0.6.1", "tape": "^5.3.1", "testdouble": "^3.8.2", diff --git a/packages/devp2p/package.json b/packages/devp2p/package.json index 0f67c2db79..3aae73ed2d 100644 --- a/packages/devp2p/package.json +++ b/packages/devp2p/package.json @@ -42,24 +42,22 @@ }, "dependencies": { "@ethereumjs/common": "^2.6.4", + "@scure/base": "1.1.1", "@types/bl": "^2.1.0", "@types/k-bucket": "^5.0.0", "@types/lru-cache": "^5.1.0", - "base64url": "^3.0.1", "bl": "^1.1.2", "debug": "^4.3.3", "@ethereumjs/util": "^8.0.0", - "hi-base32": "^0.5.0", + "ethereum-cryptography": "^1.1.0", "inherits": "^2.0.1", "ip": "^1.1.3", "k-bucket": "^5.0.0", - "keccak": "^3.0.1", "lru-cache": "^5.1.1", "ms": "^0.7.1", "multiaddr": "^10.0.1", "rlp": "^3.0.0", "scanf": "^1.1.2", - "secp256k1": "^4.0.2", "snappyjs": "^0.6.1" }, "devDependencies": { @@ -68,10 +66,8 @@ "@types/chalk": "^2.2.0", "@types/debug": "^4.1.4", "@types/ip": "^1.1.0", - "@types/keccak": "^3.0.1", "@types/ms": "^0.7.30", "@types/node": "^16.11.7", - "@types/secp256k1": "^4.0.1", "@types/tape": "^4.13.2", "chalk": "^2.4.2", "eslint": "^6.8.0", diff --git a/packages/devp2p/src/dns/enr.ts b/packages/devp2p/src/dns/enr.ts index ca6d80e0bd..9ce64c1525 100644 --- a/packages/devp2p/src/dns/enr.ts +++ b/packages/devp2p/src/dns/enr.ts @@ -1,12 +1,11 @@ -import * as base32 from 'hi-base32' +import { base32, base64url } from '@scure/base' import { sscanf } from 'scanf' -import { ecdsaVerify } from 'secp256k1' import { Multiaddr } from 'multiaddr' -import base64url from 'base64url' import { arrToBufArr, bufArrToArr } from '@ethereumjs/util' import RLP from 'rlp' import { PeerInfo } from '../dpt' import { toNewUint8Array, keccak256 } from '../util' +import { ecdsaVerify } from 'ethereum-cryptography/secp256k1-compat' const Convert = require('multiaddr/src/convert') @@ -52,7 +51,7 @@ export class ENR { throw new Error(`String encoded ENR must start with '${this.RECORD_PREFIX}'`) // ENRs are RLP encoded and written to DNS TXT entries as base64 url-safe strings - const base64BufferEnr = base64url.toBuffer(enr.slice(this.RECORD_PREFIX.length)) + const base64BufferEnr = Buffer.from(base64url.decode(enr.slice(this.RECORD_PREFIX.length))) const decoded = arrToBufArr(RLP.decode(Uint8Array.from(base64BufferEnr))) as Buffer[] const [signature, seq, ...kvs] = decoded @@ -109,14 +108,17 @@ export class ENR { if (!rootVals.seq) throw new Error("Could not parse 'seq' value from ENR root entry") if (!rootVals.signature) throw new Error("Could not parse 'sig' value from ENR root entry") - const decodedPublicKey = base32.decode.asBytes(publicKey) + const decodedPublicKey = [...base32.decode(publicKey + '===').values()] // The signature is a 65-byte secp256k1 over the keccak256 hash // of the record content, excluding the `sig=` part, encoded as URL-safe base64 string // (Trailing recovery bit must be trimmed to pass `ecdsaVerify` method) const signedComponent = root.split(' sig')[0] const signedComponentBuffer = Buffer.from(signedComponent) - const signatureBuffer = base64url.toBuffer(rootVals.signature).slice(0, 64) + const signatureBuffer = Buffer.from( + [...base64url.decode(rootVals.signature + '=').values()].slice(0, 64) + ) + const keyBuffer = Buffer.from(decodedPublicKey) const isVerified = ecdsaVerify(signatureBuffer, keccak256(signedComponentBuffer), keyBuffer) diff --git a/packages/devp2p/src/dpt/dpt.ts b/packages/devp2p/src/dpt/dpt.ts index 274204d9dd..a87eeb4af1 100644 --- a/packages/devp2p/src/dpt/dpt.ts +++ b/packages/devp2p/src/dpt/dpt.ts @@ -1,6 +1,6 @@ import ms from 'ms' import { EventEmitter } from 'events' -import { publicKeyCreate } from 'secp256k1' +import { getPublicKey } from 'ethereum-cryptography/secp256k1' import { randomBytes } from 'crypto' // import { debug as createDebugLogger } from 'debug' import { devp2pDebug } from '../util' @@ -106,7 +106,7 @@ export class DPT extends EventEmitter { super() this.privateKey = Buffer.from(privateKey) - this._id = pk2id(Buffer.from(publicKeyCreate(this.privateKey, false))) + this._id = pk2id(Buffer.from(getPublicKey(this.privateKey, false))) this._shouldFindNeighbours = options.shouldFindNeighbours === false ? false : true this._shouldGetDnsPeers = options.shouldGetDnsPeers ?? false // By default, tries to connect to 12 new peers every 3s diff --git a/packages/devp2p/src/dpt/message.ts b/packages/devp2p/src/dpt/message.ts index fefd42bdec..8c3d376dcf 100644 --- a/packages/devp2p/src/dpt/message.ts +++ b/packages/devp2p/src/dpt/message.ts @@ -1,8 +1,8 @@ import { debug as createDebugLogger } from 'debug' import ip from 'ip' -import secp256k1 from 'secp256k1' import { bufArrToArr } from '@ethereumjs/util' import RLP from 'rlp' +import { ecdsaRecover, ecdsaSign } from 'ethereum-cryptography/secp256k1-compat' import { keccak256, int2buffer, buffer2int, assertEq, unstrictDecode } from '../util' import { PeerInfo } from './dpt' @@ -176,7 +176,7 @@ export function encode(typename: string, data: T, privateKey: Buffer) { ]) const sighash = keccak256(typedata) - const sig = secp256k1.ecdsaSign(sighash, privateKey) + const sig = ecdsaSign(sighash, privateKey) const hashdata = Buffer.concat([Buffer.from(sig.signature), Buffer.from([sig.recid]), typedata]) const hash = keccak256(hashdata) return Buffer.concat([hash, hashdata]) @@ -195,7 +195,7 @@ export function decode(buffer: Buffer) { const sighash = keccak256(typedata) const signature = buffer.slice(32, 96) const recoverId = buffer[96] - const publicKey = Buffer.from(secp256k1.ecdsaRecover(signature, recoverId, sighash, false)) + const publicKey = Buffer.from(ecdsaRecover(signature, recoverId, sighash, false)) return { typename, data, publicKey } } diff --git a/packages/devp2p/src/rlpx/ecies.ts b/packages/devp2p/src/rlpx/ecies.ts index 06488088ef..e1053b4a82 100644 --- a/packages/devp2p/src/rlpx/ecies.ts +++ b/packages/devp2p/src/rlpx/ecies.ts @@ -1,8 +1,8 @@ import crypto, { Decipher } from 'crypto' import { debug as createDebugLogger } from 'debug' -import { publicKeyCreate, ecdh, ecdsaRecover, ecdsaSign } from 'secp256k1' import { bufArrToArr } from '@ethereumjs/util' import RLP from 'rlp' +import { getPublicKey } from 'ethereum-cryptography/secp256k1' import { unstrictDecode } from '../util' import { MAC } from './mac' @@ -17,6 +17,7 @@ import { buffer2int, zfill, } from '../util' +import { ecdsaSign, ecdsaRecover, ecdh } from 'ethereum-cryptography/secp256k1-compat' const debug = createDebugLogger('devp2p:rlpx:peer') @@ -26,10 +27,9 @@ function ecdhX(publicKey: Buffer, privateKey: Buffer) { const pubKey = new Uint8Array(33) pubKey[0] = (y[31] & 1) === 0 ? 0x02 : 0x03 pubKey.set(x, 1) - return pubKey + return pubKey.slice(1) } - // @ts-ignore - return Buffer.from(ecdh(publicKey, privateKey, { hashfn }, Buffer.alloc(33)).slice(1)) + return Buffer.from(ecdh(publicKey, privateKey, { hashfn: hashfn }, Buffer.alloc(32))) } // a straigth rip from python interop w/go ecies implementation @@ -80,7 +80,7 @@ export class ECIES { this._nonce = crypto.randomBytes(32) this._ephemeralPrivateKey = genPrivateKey() - this._ephemeralPublicKey = Buffer.from(publicKeyCreate(this._ephemeralPrivateKey, false)) + this._ephemeralPublicKey = Buffer.from(getPublicKey(this._ephemeralPrivateKey, false)) } _encryptMessage(data: Buffer, sharedMacData: Buffer | null = null): Buffer | undefined { @@ -106,7 +106,7 @@ export class ECIES { .update(Buffer.concat([dataIV, sharedMacData])) .digest() - const publicKey = publicKeyCreate(privateKey, false) + const publicKey = getPublicKey(privateKey, false) return Buffer.concat([publicKey, dataIV, tag]) } diff --git a/packages/devp2p/src/rlpx/mac.ts b/packages/devp2p/src/rlpx/mac.ts index b9a6800a5d..e14c3b3de8 100644 --- a/packages/devp2p/src/rlpx/mac.ts +++ b/packages/devp2p/src/rlpx/mac.ts @@ -1,12 +1,14 @@ import { createCipheriv } from 'crypto' -import createKeccakHash from 'keccak' +import { keccak256 } from 'ethereum-cryptography/keccak' import { xor } from '../util' +export type Hash = ReturnType + export class MAC { - _hash: any + _hash: Hash _secret: Buffer constructor(secret: Buffer) { - this._hash = createKeccakHash('keccak256') + this._hash = keccak256.create() this._secret = secret } @@ -29,6 +31,6 @@ export class MAC { } digest() { - return this._hash._clone().digest().slice(0, 16) + return Buffer.from(this._hash.clone().digest().slice(0, 16)) } } diff --git a/packages/devp2p/src/rlpx/rlpx.ts b/packages/devp2p/src/rlpx/rlpx.ts index fe2fb73790..a16969bacb 100644 --- a/packages/devp2p/src/rlpx/rlpx.ts +++ b/packages/devp2p/src/rlpx/rlpx.ts @@ -1,7 +1,7 @@ import * as net from 'net' import * as os from 'os' import ms from 'ms' -import { publicKeyCreate } from 'secp256k1' +import { getPublicKey } from 'ethereum-cryptography/secp256k1' import { EventEmitter } from 'events' import { debug as createDebugLogger, Debugger } from 'debug' import { devp2pDebug } from '../util' @@ -54,7 +54,7 @@ export class RLPx extends EventEmitter { super() this._privateKey = Buffer.from(privateKey) - this._id = pk2id(Buffer.from(publicKeyCreate(this._privateKey, false))) + this._id = pk2id(Buffer.from(getPublicKey(this._privateKey, false))) // options this._timeout = options.timeout ?? ms('10s') diff --git a/packages/devp2p/src/util.ts b/packages/devp2p/src/util.ts index 1c27da4e79..7bff5468f5 100644 --- a/packages/devp2p/src/util.ts +++ b/packages/devp2p/src/util.ts @@ -1,8 +1,8 @@ -import { randomBytes } from 'crypto' -import { privateKeyVerify, publicKeyConvert } from 'secp256k1' -import createKeccakHash from 'keccak' import { arrToBufArr } from '@ethereumjs/util' import RLP from 'rlp' +import { utils } from 'ethereum-cryptography/secp256k1' +import { publicKeyConvert } from 'ethereum-cryptography/secp256k1-compat' +import { keccak256 as _keccak256 } from 'ethereum-cryptography/keccak' import { ETH } from './protocol/eth' import { LES } from './protocol/les' import { debug as createDebugLogger } from 'debug' @@ -11,12 +11,12 @@ export const devp2pDebug = createDebugLogger('devp2p') export function keccak256(...buffers: Buffer[]) { const buffer = Buffer.concat(buffers) - return createKeccakHash('keccak256').update(buffer).digest() + return Buffer.from(_keccak256(buffer)) } export function genPrivateKey(): Buffer { - const privateKey = randomBytes(32) - return privateKeyVerify(privateKey) ? privateKey : genPrivateKey() + const privateKey = utils.randomPrivateKey() + return utils.isValidPrivateKey(privateKey) ? Buffer.from(privateKey) : genPrivateKey() } export function pk2id(pk: Buffer): Buffer { diff --git a/packages/devp2p/test/dpt-message.ts b/packages/devp2p/test/dpt-message.ts index df5b245271..40534b7265 100644 --- a/packages/devp2p/test/dpt-message.ts +++ b/packages/devp2p/test/dpt-message.ts @@ -1,12 +1,12 @@ +import { publicKeyCreate } from 'ethereum-cryptography/secp256k1-compat' import test from 'tape' -import * as secp256k1 from 'secp256k1' import * as message from '../src/dpt/message' const privateKey = Buffer.from( 'b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291', 'hex' ) -const publicKey = Buffer.from(secp256k1.publicKeyCreate(privateKey, false)) +const publicKey = Buffer.from(publicKeyCreate(privateKey, false)) test('ping packet with version 4, additional list elements', (t) => { const buffer = Buffer.from( diff --git a/packages/devp2p/test/rlpx-ecies.ts b/packages/devp2p/test/rlpx-ecies.ts index f99bb012da..854654e862 100644 --- a/packages/devp2p/test/rlpx-ecies.ts +++ b/packages/devp2p/test/rlpx-ecies.ts @@ -1,10 +1,10 @@ import { randomBytes } from 'crypto' -import * as secp256k1 from 'secp256k1' import test, { Test } from 'tape' import * as util from '../src/util' import { ECIES } from '../src/rlpx/ecies' import testdata from './testdata.json' +import { publicKeyCreate } from 'ethereum-cryptography/secp256k1-compat' declare module 'tape' { export interface Test { @@ -16,8 +16,8 @@ function randomBefore(fn: Function) { return (t: Test) => { const privateKey1 = util.genPrivateKey() const privateKey2 = util.genPrivateKey() - const publicKey1 = Buffer.from(secp256k1.publicKeyCreate(privateKey1, false)) - const publicKey2 = Buffer.from(secp256k1.publicKeyCreate(privateKey2, false)) + const publicKey1 = Buffer.from(publicKeyCreate(privateKey1, false)) + const publicKey2 = Buffer.from(publicKeyCreate(privateKey2, false)) t.context = { a: new ECIES(privateKey1, util.pk2id(publicKey1), util.pk2id(publicKey2)), b: new ECIES(privateKey2, util.pk2id(publicKey2), util.pk2id(publicKey1)),