diff --git a/CHANGELOG.md b/CHANGELOG.md index a86f5973b2..237d1a373a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Prefer the use of `BigInt` over integer literal (`n` postfix) to facilitate the use of a polyfill. +- Replaced `secp256k1` and hence `elliptic` dependencies with `@noble/secp256k1`, + reducing package size, number of dependency and removing need for `crypto-browserify` polyfill. ### Fixed diff --git a/examples/web-chat/config/webpack.extend.js b/examples/web-chat/config/webpack.extend.js index c081232a2f..c0b2c160e1 100644 --- a/examples/web-chat/config/webpack.extend.js +++ b/examples/web-chat/config/webpack.extend.js @@ -8,7 +8,7 @@ module.exports = { Object.assign(config.resolve.fallback, { assert: require.resolve("assert"), buffer: require.resolve("buffer"), - crypto: require.resolve("crypto-browserify"), + crypto: false, http: require.resolve("http-browserify"), https: require.resolve("https-browserify"), stream: require.resolve("stream-browserify"), @@ -41,7 +41,7 @@ module.exports = { Object.assign(config.resolve.fallback, { assert: require.resolve("assert"), buffer: require.resolve("buffer"), - crypto: require.resolve("crypto-browserify"), + crypto: false, http: require.resolve("http-browserify"), https: require.resolve("https-browserify"), stream: require.resolve("stream-browserify"), diff --git a/package-lock.json b/package-lock.json index c5c357c63c..069421f61c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@chainsafe/libp2p-noise": "^5.0.0", "@ethersproject/rlp": "^5.5.0", + "@noble/secp256k1": "^1.3.4", "debug": "^4.3.1", "dns-query": "^0.8.0", "hi-base32": "^0.5.1", @@ -25,7 +26,6 @@ "multiaddr": "^10.0.1", "multihashes": "^4.0.3", "protobufjs": "^6.8.8", - "secp256k1": "^4.0.2", "uuid": "^8.3.2" }, "devDependencies": { @@ -45,7 +45,6 @@ "assert": "^2.0.0", "buffer": "^6.0.3", "chai": "^4.3.4", - "crypto-browserify": "^3.12.0", "cspell": "^5.14.0", "eslint": "^8.6.0", "eslint-config-prettier": "^8.3.0", @@ -2642,7 +2641,8 @@ "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, "node_modules/body-parser": { "version": "1.19.0", @@ -2756,7 +2756,8 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "node_modules/browser-process-hrtime": { "version": "1.0.0", @@ -4282,6 +4283,7 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -6000,6 +6002,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -6053,6 +6056,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -8317,12 +8321,14 @@ "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "node_modules/minimatch": { "version": "3.0.4", @@ -8703,11 +8709,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node_modules/node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, "node_modules/node-fetch": { "name": "@achingbrain/node-fetch", "version": "2.6.7", @@ -8725,16 +8726,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -10669,20 +10660,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "hasInstallScript": true, - "dependencies": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -14803,7 +14780,8 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, "body-parser": { "version": "1.19.0", @@ -14901,7 +14879,8 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "browser-process-hrtime": { "version": "1.0.0", @@ -16124,6 +16103,7 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -17396,6 +17376,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -17439,6 +17420,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -19209,12 +19191,14 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -19519,11 +19503,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, "node-fetch": { "version": "npm:@achingbrain/node-fetch@2.6.7", "resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", @@ -19534,11 +19513,6 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, - "node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" - }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -20990,16 +20964,6 @@ "ajv-keywords": "^3.5.2" } }, - "secp256k1": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", - "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", - "requires": { - "elliptic": "^6.5.4", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", diff --git a/package.json b/package.json index cc79504b9b..742bb43072 100644 --- a/package.json +++ b/package.json @@ -61,12 +61,16 @@ "deploy": "node ci/deploy.js", "reset-hard": "git clean -dfx && git reset --hard && npm i && npm run build && for d in examples/*/; do (cd $d; npm i); done" }, + "browser": { + "crypto": false + }, "engines": { "node": ">=16" }, "dependencies": { "@chainsafe/libp2p-noise": "^5.0.0", "@ethersproject/rlp": "^5.5.0", + "@noble/secp256k1": "^1.3.4", "debug": "^4.3.1", "dns-query": "^0.8.0", "hi-base32": "^0.5.1", @@ -81,7 +85,6 @@ "multiaddr": "^10.0.1", "multihashes": "^4.0.3", "protobufjs": "^6.8.8", - "secp256k1": "^4.0.2", "uuid": "^8.3.2" }, "devDependencies": { @@ -101,7 +104,6 @@ "assert": "^2.0.0", "buffer": "^6.0.3", "chai": "^4.3.4", - "crypto-browserify": "^3.12.0", "cspell": "^5.14.0", "eslint": "^8.6.0", "eslint-config-prettier": "^8.3.0", diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index fb6cd705a5..cde7edc769 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -1,33 +1,48 @@ import nodeCrypto from "crypto"; -// IE 11 -declare global { - interface Window { - msCrypto?: Crypto; - } +import { concat } from "uint8arrays/concat"; - interface Crypto { - webkitSubtle?: SubtleCrypto; +declare const self: Record | undefined; +const crypto: { node?: any; web?: any } = { + node: nodeCrypto, + web: typeof self === "object" && "crypto" in self ? self.crypto : undefined, +}; + +export function getSubtle(): SubtleCrypto { + if (crypto.web) { + return crypto.web.subtle; + } else if (crypto.node) { + return crypto.node.webcrypto.subtle; + } else { + throw new Error( + "The environment doesn't have Crypto Subtle API (if in the browser, be sure to use to be in a secure context, ie, https)" + ); } } -const crypto = - (typeof window !== "undefined" && - (window as Window) && - (window.crypto || window.msCrypto)) || - (nodeCrypto.webcrypto as unknown as Crypto); -const subtle: SubtleCrypto = crypto.subtle || crypto.webkitSubtle; - -if (subtle === undefined) { - throw new Error("crypto and/or subtle api unavailable"); +export function randomBytes(bytesLength = 32): Uint8Array { + if (crypto.web) { + return crypto.web.getRandomValues(new Uint8Array(bytesLength)); + } else if (crypto.node) { + const { randomBytes } = crypto.node; + return Uint8Array.from(randomBytes(bytesLength)); + } else { + throw new Error( + "The environment doesn't have randomBytes function (if in the browser, be sure to use to be in a secure context, ie, https)" + ); + } } -export { crypto, subtle }; - -export function randomBytes(size: number): Uint8Array { - return crypto.getRandomValues(new Uint8Array(size)); -} - -export function sha256(msg: ArrayBufferLike): Promise { - return subtle.digest({ name: "SHA-256" }, msg); +export async function sha256(...messages: Uint8Array[]): Promise { + if (crypto.web) { + const buffer = await crypto.web.subtle.digest("SHA-256", concat(messages)); + return new Uint8Array(buffer); + } else if (crypto.node) { + const { createHash } = crypto.node; + const hash = createHash("sha256"); + messages.forEach((m) => hash.update(m)); + return Uint8Array.from(hash.digest()); + } else { + throw new Error("The environment doesn't have sha256 function"); + } } diff --git a/src/lib/discovery/enrtree.ts b/src/lib/discovery/enrtree.ts index 6abddae350..4bff987d0d 100644 --- a/src/lib/discovery/enrtree.ts +++ b/src/lib/discovery/enrtree.ts @@ -1,7 +1,7 @@ import assert from "assert"; +import * as secp from "@noble/secp256k1"; import * as base32 from "hi-base32"; -import { ecdsaVerify } from "secp256k1"; import { fromString } from "uint8arrays/from-string"; import { ENR } from "../enr"; @@ -48,11 +48,17 @@ export class ENRTree { 64 ); - const isVerified = ecdsaVerify( - signatureBuffer, - keccak256Buf(signedComponentBuffer), - new Uint8Array(decodedPublicKey) - ); + let isVerified; + try { + const _sig = secp.Signature.fromCompact(signatureBuffer.slice(0, 64)); + isVerified = secp.verify( + _sig, + keccak256Buf(signedComponentBuffer), + new Uint8Array(decodedPublicKey) + ); + } catch { + isVerified = false; + } assert(isVerified, "Unable to verify ENRTree root signature"); diff --git a/src/lib/enr/enr.spec.ts b/src/lib/enr/enr.spec.ts index 46ad13f8bd..7827f19b61 100644 --- a/src/lib/enr/enr.spec.ts +++ b/src/lib/enr/enr.spec.ts @@ -37,7 +37,7 @@ describe("ENR", function () { lightPush: false, }; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const enr2 = ENR.decodeTxt(txt); if (!enr.signature) throw "enr.signature is undefined"; @@ -116,7 +116,7 @@ describe("ENR", function () { enr.setLocationMultiaddr(new Multiaddr("/ip4/18.223.219.100/udp/9000")); enr.set("id", new Uint8Array([0])); - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); ENR.decodeTxt(txt); assert.fail("Expect error here"); @@ -190,11 +190,11 @@ describe("ENR", function () { }); }); - describe("Static tests", () => { + describe("Static tests", function () { let privateKey: Uint8Array; let record: ENR; - beforeEach(() => { + beforeEach(async function () { const seq = BigInt(1); privateKey = hexToBytes( "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" @@ -202,8 +202,7 @@ describe("ENR", function () { record = ENR.createV4(v4.publicKey(privateKey)); record.setLocationMultiaddr(new Multiaddr("/ip4/127.0.0.1/udp/30303")); record.seq = seq; - // To set signature - record.encode(privateKey); + await record.encodeTxt(privateKey); }); it("should properly compute the node id", () => { @@ -212,21 +211,24 @@ describe("ENR", function () { ); }); - it("should encode/decode to RLP encoding", () => { - const decoded = ENR.decode(record.encode(privateKey)); + it("should encode/decode to RLP encoding", async function () { + const decoded = ENR.decode(await record.encode(privateKey)); expect(decoded).to.deep.equal(record); }); - it("should encode/decode to text encoding", () => { + it("should encode/decode to text encoding", async function () { // spec enr https://eips.ethereum.org/EIPS/eip-778 const testTxt = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8"; const decoded = ENR.decodeTxt(testTxt); - expect(decoded.udp).to.be.equal(30303); - expect(decoded.ip).to.be.equal("127.0.0.1"); - expect(decoded).to.deep.equal(record); - const recordTxt = record.encodeTxt(privateKey); - expect(recordTxt).to.equal(testTxt); + // Note: Signatures are different due to the extra entropy added + // by @noble/secp256k1: + // https://github.com/paulmillr/noble-secp256k1#signmsghash-privatekey + expect(decoded.udp).to.deep.equal(record.udp); + expect(decoded.ip).to.deep.equal(record.ip); + expect(decoded.id).to.deep.equal(record.id); + expect(decoded.seq).to.equal(record.seq); + expect(decoded.get("secp256k1")).to.deep.equal(record.get("secp256k1")); }); }); @@ -397,10 +399,10 @@ describe("ENR", function () { }; }); - it("should set field with all protocols disabled", () => { + it("should set field with all protocols disabled", async () => { enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(false); @@ -409,14 +411,14 @@ describe("ENR", function () { expect(decoded.lightPush).to.equal(false); }); - it("should set field with all protocols enabled", () => { + it("should set field with all protocols enabled", async () => { waku2Protocols.relay = true; waku2Protocols.store = true; waku2Protocols.filter = true; waku2Protocols.lightPush = true; enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(true); @@ -425,11 +427,11 @@ describe("ENR", function () { expect(decoded.lightPush).to.equal(true); }); - it("should set field with only RELAY enabled", () => { + it("should set field with only RELAY enabled", async () => { waku2Protocols.relay = true; enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(true); @@ -438,11 +440,11 @@ describe("ENR", function () { expect(decoded.lightPush).to.equal(false); }); - it("should set field with only STORE enabled", () => { + it("should set field with only STORE enabled", async () => { waku2Protocols.store = true; enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(false); @@ -451,11 +453,11 @@ describe("ENR", function () { expect(decoded.lightPush).to.equal(false); }); - it("should set field with only FILTER enabled", () => { + it("should set field with only FILTER enabled", async () => { waku2Protocols.filter = true; enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(false); @@ -464,11 +466,11 @@ describe("ENR", function () { expect(decoded.lightPush).to.equal(false); }); - it("should set field with only LIGHTPUSH enabled", () => { + it("should set field with only LIGHTPUSH enabled", async () => { waku2Protocols.lightPush = true; enr.waku2 = waku2Protocols; - const txt = enr.encodeTxt(keypair.privateKey); + const txt = await enr.encodeTxt(keypair.privateKey); const decoded = ENR.decodeTxt(txt).waku2!; expect(decoded.relay).to.equal(false); diff --git a/src/lib/enr/enr.ts b/src/lib/enr/enr.ts index f1fa53699e..fe7b862480 100644 --- a/src/lib/enr/enr.ts +++ b/src/lib/enr/enr.ts @@ -22,6 +22,7 @@ import { import { decodeMultiaddrs, encodeMultiaddrs } from "./multiaddrs_codec"; import { ENRKey, ENRValue, NodeId, SequenceNumber } from "./types"; import * as v4 from "./v4"; +import { compressPublicKey } from "./v4"; import { decodeWaku2, encodeWaku2, Waku2 } from "./waku2_codec"; const dbg = debug("waku:enr"); @@ -45,6 +46,10 @@ export class ENR extends Map { publicKey: Uint8Array, kvs: Record = {} ): ENR { + // EIP-778 specifies that the key must be in compressed format, 33 bytes + if (publicKey.length !== 33) { + publicKey = compressPublicKey(publicKey); + } return new ENR({ ...kvs, id: utf8ToBytes("v4"), @@ -453,10 +458,10 @@ export class ENR extends Map { return v4.verify(this.publicKey, data, signature); } - sign(data: Uint8Array, privateKey: Uint8Array): Uint8Array { + async sign(data: Uint8Array, privateKey: Uint8Array): Promise { switch (this.id) { case "v4": - this.signature = v4.sign(privateKey, data); + this.signature = await v4.sign(privateKey, data); break; default: throw new Error(ERR_INVALID_ID); @@ -464,7 +469,9 @@ export class ENR extends Map { return this.signature; } - encodeToValues(privateKey?: Uint8Array): (ENRKey | ENRValue | number[])[] { + async encodeToValues( + privateKey?: Uint8Array + ): Promise<(ENRKey | ENRValue | number[])[]> { // sort keys and flatten into [k, v, k, v, ...] const content: Array = Array.from(this.keys()) .sort((a, b) => a.localeCompare(b)) @@ -473,7 +480,9 @@ export class ENR extends Map { .flat(); content.unshift(new Uint8Array([Number(this.seq)])); if (privateKey) { - content.unshift(this.sign(hexToBytes(RLP.encode(content)), privateKey)); + content.unshift( + await this.sign(hexToBytes(RLP.encode(content)), privateKey) + ); } else { if (!this.signature) { throw new Error(ERR_NO_SIGNATURE); @@ -483,15 +492,19 @@ export class ENR extends Map { return content; } - encode(privateKey?: Uint8Array): Uint8Array { - const encoded = hexToBytes(RLP.encode(this.encodeToValues(privateKey))); + async encode(privateKey?: Uint8Array): Promise { + const encoded = hexToBytes( + RLP.encode(await this.encodeToValues(privateKey)) + ); if (encoded.length >= MAX_RECORD_SIZE) { throw new Error("ENR must be less than 300 bytes"); } return encoded; } - encodeTxt(privateKey?: Uint8Array): string { - return ENR.RECORD_PREFIX + toString(this.encode(privateKey), "base64url"); + async encodeTxt(privateKey?: Uint8Array): Promise { + return ( + ENR.RECORD_PREFIX + toString(await this.encode(privateKey), "base64url") + ); } } diff --git a/src/lib/enr/keypair/secp256k1.ts b/src/lib/enr/keypair/secp256k1.ts index a8aa47816c..1b2cdded6a 100644 --- a/src/lib/enr/keypair/secp256k1.ts +++ b/src/lib/enr/keypair/secp256k1.ts @@ -1,8 +1,8 @@ -import crypto from "crypto"; - -import * as secp256k1 from "secp256k1"; +import * as secp from "@noble/secp256k1"; import { concat } from "uint8arrays/concat"; +import { randomBytes } from "../../crypto"; + import { AbstractKeypair, IKeypair, IKeypairClass, KeypairType } from "./types"; export function secp256k1PublicKeyToCompressed( @@ -11,18 +11,22 @@ export function secp256k1PublicKeyToCompressed( if (publicKey.length === 64) { publicKey = concat([[4], publicKey], 65); } - return secp256k1.publicKeyConvert(publicKey, true); + const point = secp.Point.fromHex(publicKey); + return point.toRawBytes(true); } export function secp256k1PublicKeyToFull(publicKey: Uint8Array): Uint8Array { if (publicKey.length === 64) { publicKey = concat([[4], publicKey], 65); } - return secp256k1.publicKeyConvert(publicKey, false); + const point = secp.Point.fromHex(publicKey); + + return point.toRawBytes(false); } export function secp256k1PublicKeyToRaw(publicKey: Uint8Array): Uint8Array { - return secp256k1.publicKeyConvert(publicKey, false).slice(1); + const point = secp.Point.fromHex(publicKey); + return point.toRawBytes(false).slice(1); } export const Secp256k1Keypair: IKeypairClass = class Secp256k1Keypair @@ -41,41 +45,44 @@ export const Secp256k1Keypair: IKeypairClass = class Secp256k1Keypair } static async generate(): Promise { - const privateKey = await randomBytes(32); - const publicKey = secp256k1.publicKeyCreate(privateKey); + const privateKey = randomBytes(32); + const publicKey = secp.getPublicKey(privateKey); return new Secp256k1Keypair(privateKey, publicKey); } privateKeyVerify(key = this._privateKey): boolean { if (key) { - return secp256k1.privateKeyVerify(key); + return secp.utils.isValidPrivateKey(key); } return true; } publicKeyVerify(key = this._publicKey): boolean { if (key) { - return secp256k1.publicKeyVerify(key); + try { + secp.Point.fromHex(key); + return true; + } catch { + return false; + } } return true; } - sign(msg: Uint8Array): Uint8Array { - const { signature, recid } = secp256k1.ecdsaSign(msg, this.privateKey); + async sign(msg: Uint8Array): Promise { + const [signature, recid] = await secp.sign(msg, this.privateKey, { + recovered: true, + der: false, + }); return concat([signature, [recid]], signature.length + 1); } verify(msg: Uint8Array, sig: Uint8Array): boolean { - return secp256k1.ecdsaVerify(sig, msg, this.publicKey); + try { + const _sig = secp.Signature.fromCompact(sig.slice(0, 64)); + return secp.verify(_sig, msg, this.publicKey); + } catch { + return false; + } } }; - -function randomBytes(length: number): Uint8Array { - if (typeof window !== "undefined" && window && window.crypto) { - const array = new Uint8Array(length); - window.crypto.getRandomValues(array); - return array; - } else { - return crypto.randomBytes(length); - } -} diff --git a/src/lib/enr/keypair/types.ts b/src/lib/enr/keypair/types.ts index 657d751749..1673d13bf4 100644 --- a/src/lib/enr/keypair/types.ts +++ b/src/lib/enr/keypair/types.ts @@ -10,7 +10,7 @@ export interface IKeypair { publicKey: Uint8Array; privateKeyVerify(): boolean; publicKeyVerify(): boolean; - sign(msg: Uint8Array): Uint8Array; + sign(msg: Uint8Array): Promise; verify(msg: Uint8Array, sig: Uint8Array): boolean; hasPrivateKey(): boolean; } @@ -29,7 +29,7 @@ export abstract class AbstractKeypair { throw new Error("Invalid private key"); } if ((this._publicKey = publicKey) && !this.publicKeyVerify()) { - throw new Error("Invalid private key"); + throw new Error("Invalid public key"); } } diff --git a/src/lib/enr/v4.ts b/src/lib/enr/v4.ts index 0738a30732..ad6b7920cc 100644 --- a/src/lib/enr/v4.ts +++ b/src/lib/enr/v4.ts @@ -1,7 +1,8 @@ -import crypto from "crypto"; - +import * as secp from "@noble/secp256k1"; import { keccak256 } from "js-sha3"; -import * as secp256k1 from "secp256k1"; + +import { randomBytes } from "../crypto"; +import { bytesToHex } from "../utils"; import { createNodeId } from "./create"; import { NodeId } from "./types"; @@ -10,17 +11,26 @@ export function hash(input: Uint8Array): Uint8Array { return new Uint8Array(keccak256.arrayBuffer(input)); } -export async function createPrivateKey(): Promise { +export function createPrivateKey(): Uint8Array { return randomBytes(32); } export function publicKey(privKey: Uint8Array): Uint8Array { - return secp256k1.publicKeyCreate(privKey); + return secp.getPublicKey(privKey, true); } -export function sign(privKey: Uint8Array, msg: Uint8Array): Uint8Array { - const { signature } = secp256k1.ecdsaSign(hash(msg), privKey); - return signature; +export function compressPublicKey(publicKey: Uint8Array): Uint8Array { + const point = secp.Point.fromHex(bytesToHex(publicKey)); + return point.toRawBytes(true); +} + +export async function sign( + privKey: Uint8Array, + msg: Uint8Array +): Promise { + return secp.sign(hash(msg), privKey, { + der: false, + }); } export function verify( @@ -28,12 +38,17 @@ export function verify( msg: Uint8Array, sig: Uint8Array ): boolean { - // Remove the recovery id if present (byte #65) - return secp256k1.ecdsaVerify(sig.slice(0, 64), hash(msg), pubKey); + try { + const _sig = secp.Signature.fromCompact(sig.slice(0, 64)); + return secp.verify(_sig, hash(msg), pubKey); + } catch { + return false; + } } export function nodeId(pubKey: Uint8Array): NodeId { - const uncompressedPubkey = secp256k1.publicKeyConvert(pubKey, false); + const publicKey = secp.Point.fromHex(pubKey); + const uncompressedPubkey = publicKey.toRawBytes(false); return createNodeId(hash(uncompressedPubkey.slice(1))); } @@ -45,20 +60,20 @@ export class ENRKeyPair { public readonly publicKey: Uint8Array ) {} - public static async create(privateKey?: Uint8Array): Promise { + public static create(privateKey?: Uint8Array): ENRKeyPair { if (privateKey) { - if (!secp256k1.privateKeyVerify(privateKey)) { + if (!secp.utils.isValidPrivateKey(privateKey)) { throw new Error("Invalid private key"); } } - const _privateKey = privateKey || (await createPrivateKey()); + const _privateKey = privateKey || createPrivateKey(); const _publicKey = publicKey(_privateKey); const _nodeId = nodeId(_publicKey); return new ENRKeyPair(_nodeId, _privateKey, _publicKey); } - public sign(msg: Uint8Array): Uint8Array { + public async sign(msg: Uint8Array): Promise { return sign(this.privateKey, msg); } @@ -66,13 +81,3 @@ export class ENRKeyPair { return verify(this.publicKey, msg, sig); } } - -function randomBytes(length: number): Uint8Array { - if (typeof window !== "undefined" && window && window.crypto) { - const array = new Uint8Array(length); - window.crypto.getRandomValues(array); - return array; - } else { - return crypto.randomBytes(length); - } -} diff --git a/src/lib/waku_message/ecies.ts b/src/lib/waku_message/ecies.ts index 5cede9cf7a..e05ac121df 100644 --- a/src/lib/waku_message/ecies.ts +++ b/src/lib/waku_message/ecies.ts @@ -1,7 +1,7 @@ import * as secp from "@noble/secp256k1"; import { concat } from "uint8arrays/concat"; -import { randomBytes, sha256, subtle } from "../crypto"; +import { getSubtle, randomBytes, sha256 } from "../crypto"; import { hexToBytes } from "../utils"; /** * HKDF as implemented in go-ethereum. @@ -37,10 +37,10 @@ function aesCtrEncrypt( key: ArrayBufferLike, data: ArrayBufferLike ): Promise { - return subtle + return getSubtle() .importKey("raw", key, "AES-CTR", false, ["encrypt"]) .then((cryptoKey) => - subtle.encrypt( + getSubtle().encrypt( { name: "AES-CTR", counter: counter, length: 128 }, cryptoKey, data @@ -54,10 +54,10 @@ function aesCtrDecrypt( key: ArrayBufferLike, data: ArrayBufferLike ): Promise { - return subtle + return getSubtle() .importKey("raw", key, "AES-CTR", false, ["decrypt"]) .then((cryptoKey) => - subtle.decrypt( + getSubtle().decrypt( { name: "AES-CTR", counter: counter, length: 128 }, cryptoKey, data @@ -71,9 +71,9 @@ function hmacSha256Sign( msg: ArrayBufferLike ): PromiseLike { const algorithm = { name: "HMAC", hash: { name: "SHA-256" } }; - return subtle + return getSubtle() .importKey("raw", key, algorithm, false, ["sign"]) - .then((cryptoKey) => subtle.sign(algorithm, cryptoKey, msg)) + .then((cryptoKey) => getSubtle().sign(algorithm, cryptoKey, msg)) .then((bytes) => new Uint8Array(bytes)); } @@ -83,9 +83,9 @@ function hmacSha256Verify( sig: ArrayBufferLike ): Promise { const algorithm = { name: "HMAC", hash: { name: "SHA-256" } }; - const _key = subtle.importKey("raw", key, algorithm, false, ["verify"]); + const _key = getSubtle().importKey("raw", key, algorithm, false, ["verify"]); return _key.then((cryptoKey) => - subtle.verify(algorithm, cryptoKey, sig, msg) + getSubtle().verify(algorithm, cryptoKey, sig, msg) ); } diff --git a/src/lib/waku_message/index.spec.ts b/src/lib/waku_message/index.spec.ts index d3e699c9b6..84084b78af 100644 --- a/src/lib/waku_message/index.spec.ts +++ b/src/lib/waku_message/index.spec.ts @@ -35,7 +35,7 @@ describe("Waku Message: Browser & Node", function () { await fc.assert( fc.asyncProperty( fc.uint8Array({ minLength: 1 }), - fc.uint8Array({ minLength: 32, maxLength: 32 }), + fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (payload, key) => { const publicKey = getPublicKey(key); @@ -56,8 +56,8 @@ describe("Waku Message: Browser & Node", function () { await fc.assert( fc.asyncProperty( fc.uint8Array({ minLength: 1 }), - fc.uint8Array({ minLength: 32, maxLength: 32 }), - fc.uint8Array({ minLength: 32, maxLength: 32 }), + fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), + fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (payload, sigPrivKey, encPrivKey) => { const sigPubKey = getPublicKey(sigPrivKey); const encPubKey = getPublicKey(encPrivKey); diff --git a/src/lib/waku_message/index.ts b/src/lib/waku_message/index.ts index 2c2d45ade9..2bf49bb3e9 100644 --- a/src/lib/waku_message/index.ts +++ b/src/lib/waku_message/index.ts @@ -89,12 +89,12 @@ export class WakuMessage { } if (encPublicKey) { - const enc = version_1.clearEncode(_payload, sigPrivKey); + const enc = await version_1.clearEncode(_payload, sigPrivKey); _payload = await version_1.encryptAsymmetric(enc.payload, encPublicKey); sig = enc.sig; version = 1; } else if (symKey) { - const enc = version_1.clearEncode(_payload, sigPrivKey); + const enc = await version_1.clearEncode(_payload, sigPrivKey); _payload = await version_1.encryptSymmetric(enc.payload, symKey); sig = enc.sig; version = 1; diff --git a/src/lib/waku_message/symmetric.ts b/src/lib/waku_message/symmetric.ts new file mode 100644 index 0000000000..9d2890a2a4 --- /dev/null +++ b/src/lib/waku_message/symmetric.ts @@ -0,0 +1,37 @@ +import { getSubtle, randomBytes } from "../crypto"; + +export const KeySize = 32; +export const IvSize = 12; +export const TagSize = 16; + +const Algorithm = { name: "AES-GCM", length: 128 }; + +export async function encrypt( + iv: Buffer | Uint8Array, + key: Buffer, + clearText: Buffer +): Promise { + return getSubtle() + .importKey("raw", key, Algorithm, false, ["encrypt"]) + .then((cryptoKey) => + getSubtle().encrypt({ iv, ...Algorithm }, cryptoKey, clearText) + ) + .then(Buffer.from); +} + +export async function decrypt( + iv: Buffer, + key: Buffer, + cipherText: Buffer +): Promise { + return getSubtle() + .importKey("raw", key, Algorithm, false, ["decrypt"]) + .then((cryptoKey) => + getSubtle().decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) + ) + .then(Buffer.from); +} + +export function generateIv(): Uint8Array { + return randomBytes(IvSize); +} diff --git a/src/lib/waku_message/symmetric/browser.ts b/src/lib/waku_message/symmetric/browser.ts deleted file mode 100644 index 25e9b5f791..0000000000 --- a/src/lib/waku_message/symmetric/browser.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { IvSize } from "./index"; - -declare global { - interface Window { - msCrypto?: Crypto; - } - interface Crypto { - webkitSubtle?: SubtleCrypto; - } -} - -const crypto = window.crypto || window.msCrypto; -const subtle: SubtleCrypto = crypto.subtle || crypto.webkitSubtle; - -const Algorithm = { name: "AES-GCM", length: 128 }; - -if (subtle === undefined) { - throw new Error("Failed to load Subtle CryptoAPI"); -} - -export async function encrypt( - iv: Buffer | Uint8Array, - key: Buffer, - clearText: Buffer -): Promise { - return subtle - .importKey("raw", key, Algorithm, false, ["encrypt"]) - .then((cryptoKey) => - subtle.encrypt({ iv, ...Algorithm }, cryptoKey, clearText) - ) - .then(Buffer.from); -} - -export async function decrypt( - iv: Buffer, - key: Buffer, - cipherText: Buffer -): Promise { - return subtle - .importKey("raw", key, Algorithm, false, ["decrypt"]) - .then((cryptoKey) => - subtle.decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) - ) - .then(Buffer.from); -} - -export function generateIv(): Uint8Array { - const iv = new Uint8Array(IvSize); - crypto.getRandomValues(iv); - return iv; -} diff --git a/src/lib/waku_message/symmetric/index.ts b/src/lib/waku_message/symmetric/index.ts deleted file mode 100644 index 925c08aaec..0000000000 --- a/src/lib/waku_message/symmetric/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const SymmetricKeySize = 32; -export const IvSize = 12; -export const TagSize = 16; - -export interface Symmetric { - /** - * Proceed with symmetric encryption of `clearText` value. - */ - encrypt: ( - iv: Buffer | Uint8Array, - key: Buffer, - clearText: Buffer - ) => Promise; - /** - * Proceed with symmetric decryption of `cipherText` value. - */ - decrypt: (iv: Buffer, key: Buffer, cipherText: Buffer) => Promise; - /** - * Generate an Initialization Vector (iv) for for Symmetric encryption purposes. - */ - generateIv: () => Uint8Array; -} - -export let symmetric: Symmetric = {} as unknown as Symmetric; - -import("./browser") - .then((mod) => { - symmetric = mod; - }) - .catch((eBrowser) => { - import("./node") - .then((mod) => { - symmetric = mod; - }) - .catch((eNode) => { - throw `Could not load any symmetric crypto modules: ${eBrowser}, ${eNode}`; - }); - }); diff --git a/src/lib/waku_message/symmetric/node.ts b/src/lib/waku_message/symmetric/node.ts deleted file mode 100644 index 42707db9a8..0000000000 --- a/src/lib/waku_message/symmetric/node.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createCipheriv, createDecipheriv, randomBytes } from "crypto"; - -import { IvSize, TagSize } from "./index"; - -const Algorithm = "aes-256-gcm"; - -export async function encrypt( - iv: Buffer | Uint8Array, - key: Buffer, - clearText: Buffer -): Promise { - const cipher = createCipheriv(Algorithm, key, iv); - const a = cipher.update(clearText); - const b = cipher.final(); - const tag = cipher.getAuthTag(); - return Buffer.concat([a, b, tag]); -} - -export async function decrypt( - iv: Buffer, - key: Buffer, - data: Buffer -): Promise { - const tagStart = data.length - TagSize; - const cipherText = data.slice(0, tagStart); - const tag = data.slice(tagStart); - const decipher = createDecipheriv(Algorithm, key, iv); - decipher.setAuthTag(tag); - const a = decipher.update(cipherText); - const b = decipher.final(); - return Buffer.concat([a, b]); -} - -export function generateIv(): Buffer { - return randomBytes(IvSize); -} diff --git a/src/lib/waku_message/version_1.spec.ts b/src/lib/waku_message/version_1.spec.ts index 041f9ad615..e9553a37a8 100644 --- a/src/lib/waku_message/version_1.spec.ts +++ b/src/lib/waku_message/version_1.spec.ts @@ -14,11 +14,11 @@ import { describe("Waku Message Version 1", function () { it("Sign & Recover", function () { fc.assert( - fc.property( + fc.asyncProperty( fc.uint8Array(), fc.uint8Array({ minLength: 32, maxLength: 32 }), - (message, privKey) => { - const enc = clearEncode(message, privKey); + async (message, privKey) => { + const enc = await clearEncode(message, privKey); const res = clearDecode(enc.payload); const pubKey = getPublicKey(privKey); @@ -44,7 +44,7 @@ describe("Waku Message Version 1", function () { await fc.assert( fc.asyncProperty( fc.uint8Array({ minLength: 1 }), - fc.uint8Array({ minLength: 32, maxLength: 32 }), + fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (message, privKey) => { const publicKey = getPublicKey(privKey); diff --git a/src/lib/waku_message/version_1.ts b/src/lib/waku_message/version_1.ts index 4972410f16..e24fb8fe68 100644 --- a/src/lib/waku_message/version_1.ts +++ b/src/lib/waku_message/version_1.ts @@ -1,13 +1,13 @@ import { Buffer } from "buffer"; -import * as crypto from "crypto"; +import * as secp from "@noble/secp256k1"; import { keccak256 } from "js-sha3"; -import * as secp256k1 from "secp256k1"; +import { randomBytes } from "../crypto"; import { hexToBytes } from "../utils"; import * as ecies from "./ecies"; -import { IvSize, symmetric, SymmetricKeySize } from "./symmetric"; +import * as symmetric from "./symmetric"; const FlagsLength = 1; const FlagMask = 3; // 0011 @@ -26,10 +26,10 @@ export const PrivateKeySize = 32; * @returns The encoded payload, ready for encryption using {@link encryptAsymmetric} * or {@link encryptSymmetric}. */ -export function clearEncode( +export async function clearEncode( messagePayload: Uint8Array, sigPrivKey?: Uint8Array -): { payload: Uint8Array; sig?: Signature } { +): Promise<{ payload: Uint8Array; sig?: Signature }> { let envelope = Buffer.from([0]); // No flags envelope = addPayloadSizeField(envelope, messagePayload); envelope = Buffer.concat([envelope, Buffer.from(messagePayload)]); @@ -58,10 +58,17 @@ export function clearEncode( if (sigPrivKey) { envelope[0] |= IsSignedMask; const hash = keccak256(envelope); - const s = secp256k1.ecdsaSign(hexToBytes(hash), sigPrivKey); - envelope = Buffer.concat([envelope, s.signature, Buffer.from([s.recid])]); + const [signature, recid] = await secp.sign(hash, sigPrivKey, { + recovered: true, + der: false, + }); + envelope = Buffer.concat([ + envelope, + hexToBytes(signature), + Buffer.from([recid]), + ]); sig = { - signature: Buffer.from(s.signature), + signature: Buffer.from(signature), publicKey: getPublicKey(sigPrivKey), }; } @@ -71,7 +78,7 @@ export function clearEncode( export type Signature = { signature: Uint8Array; - publicKey: Uint8Array; + publicKey: Uint8Array | undefined; }; /** @@ -170,7 +177,7 @@ export async function decryptSymmetric( key: Uint8Array | Buffer | string ): Promise { const data = Buffer.from(payload); - const ivStart = data.length - IvSize; + const ivStart = data.length - symmetric.IvSize; const cipher = data.slice(0, ivStart); const iv = data.slice(ivStart); @@ -190,7 +197,7 @@ export function generatePrivateKey(): Uint8Array { * Generate a new symmetric key to be used for symmetric encryption. */ export function generateSymmetricKey(): Uint8Array { - return randomBytes(SymmetricKeySize); + return randomBytes(symmetric.KeySize); } /** @@ -198,7 +205,7 @@ export function generateSymmetricKey(): Uint8Array { * encryption. */ export function getPublicKey(privateKey: Uint8Array | Buffer): Uint8Array { - return secp256k1.publicKeyCreate(privateKey, false); + return secp.getPublicKey(privateKey, false); } /** @@ -249,22 +256,19 @@ function getHash(message: Buffer, isSigned: boolean): string { return keccak256(message); } -function ecRecoverPubKey(messageHash: string, signature: Buffer): Uint8Array { +function ecRecoverPubKey( + messageHash: string, + signature: Buffer +): Uint8Array | undefined { const recovery = signature.slice(64).readIntBE(0, 1); - return secp256k1.ecdsaRecover( - signature.slice(0, 64), - recovery, + const _signature = secp.Signature.fromCompact(signature.slice(0, 64)); + + return secp.recoverPublicKey( hexToBytes(messageHash), + _signature, + recovery, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: compressed: false false ); } - -function randomBytes(length: number): Uint8Array { - if (typeof window !== "undefined" && window && window.crypto) { - const array = new Uint8Array(length); - window.crypto.getRandomValues(array); - return array; - } else { - return crypto.randomBytes(length); - } -} diff --git a/webpack.config.bundle.js b/webpack.config.bundle.js index db4d4b130f..9ad2082392 100644 --- a/webpack.config.bundle.js +++ b/webpack.config.bundle.js @@ -24,7 +24,7 @@ module.exports = { extensions: ['.ts', '.js'], fallback: { buffer: require.resolve('buffer/'), - crypto: require.resolve('crypto-browserify'), + crypto: false, stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), }, diff --git a/webpack.config.js b/webpack.config.js index 9f0f4f3a48..9dc80c76ff 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -24,7 +24,7 @@ module.exports = { extensions: ['.ts', '.js'], fallback: { buffer: require.resolve('buffer/'), - crypto: require.resolve('crypto-browserify'), + crypto: false, stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), }, diff --git a/webpack.config.min.bundle.js b/webpack.config.min.bundle.js index a8aca38a4e..f260cc0805 100644 --- a/webpack.config.min.bundle.js +++ b/webpack.config.min.bundle.js @@ -24,7 +24,7 @@ module.exports = { extensions: ['.ts', '.js'], fallback: { buffer: require.resolve('buffer/'), - crypto: require.resolve('crypto-browserify'), + crypto: false, stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), }, diff --git a/webpack.config.min.js b/webpack.config.min.js index 38813a5841..8175c87033 100644 --- a/webpack.config.min.js +++ b/webpack.config.min.js @@ -24,7 +24,7 @@ module.exports = { extensions: ['.ts', '.js'], fallback: { buffer: require.resolve('buffer/'), - crypto: require.resolve('crypto-browserify'), + crypto: false, stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), },