From 335843e96d4781ab1bb0bcad3dffe81abda413c5 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 7 Mar 2022 07:48:02 +1100 Subject: [PATCH 1/4] Use browser (subtle) implementation for all env --- src/lib/waku_message/symmetric/index.ts | 63 ++++++++++++------------- src/lib/waku_message/version_1.ts | 6 +-- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/lib/waku_message/symmetric/index.ts b/src/lib/waku_message/symmetric/index.ts index 925c08aaec..d29e2cb9b9 100644 --- a/src/lib/waku_message/symmetric/index.ts +++ b/src/lib/waku_message/symmetric/index.ts @@ -1,38 +1,37 @@ -export const SymmetricKeySize = 32; +import { randomBytes, subtle } from "../../crypto"; + +export const KeySize = 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; +const Algorithm = { name: "AES-GCM", length: 128 }; + +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 let symmetric: Symmetric = {} as unknown as Symmetric; +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); +} -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}`; - }); - }); +export function generateIv(): Uint8Array { + return randomBytes(IvSize); +} diff --git a/src/lib/waku_message/version_1.ts b/src/lib/waku_message/version_1.ts index 4972410f16..535c232484 100644 --- a/src/lib/waku_message/version_1.ts +++ b/src/lib/waku_message/version_1.ts @@ -7,7 +7,7 @@ import * as secp256k1 from "secp256k1"; 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 @@ -170,7 +170,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 +190,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); } /** From 58516ae7ea787f29ae7118804b11de2a8d8452c7 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 7 Mar 2022 07:50:33 +1100 Subject: [PATCH 2/4] Remove node implementation --- .../{symmetric/index.ts => symmetric.ts} | 2 +- src/lib/waku_message/symmetric/browser.ts | 51 ------------------- src/lib/waku_message/symmetric/node.ts | 36 ------------- 3 files changed, 1 insertion(+), 88 deletions(-) rename src/lib/waku_message/{symmetric/index.ts => symmetric.ts} (94%) delete mode 100644 src/lib/waku_message/symmetric/browser.ts delete mode 100644 src/lib/waku_message/symmetric/node.ts diff --git a/src/lib/waku_message/symmetric/index.ts b/src/lib/waku_message/symmetric.ts similarity index 94% rename from src/lib/waku_message/symmetric/index.ts rename to src/lib/waku_message/symmetric.ts index d29e2cb9b9..b9cac71c56 100644 --- a/src/lib/waku_message/symmetric/index.ts +++ b/src/lib/waku_message/symmetric.ts @@ -1,4 +1,4 @@ -import { randomBytes, subtle } from "../../crypto"; +import { randomBytes, subtle } from "../crypto"; export const KeySize = 32; export const IvSize = 12; 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/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); -} From 09189d23cb8dce37a9d28e475dccdf4eb7eb3da7 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 7 Mar 2022 07:51:36 +1100 Subject: [PATCH 3/4] Remove node crypto import --- src/lib/enr/keypair/secp256k1.ts | 16 +++------------- src/lib/enr/v4.ts | 20 +++++--------------- src/lib/waku_message/version_1.ts | 12 +----------- 3 files changed, 9 insertions(+), 39 deletions(-) diff --git a/src/lib/enr/keypair/secp256k1.ts b/src/lib/enr/keypair/secp256k1.ts index a8aa47816c..251b0d7cab 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 { concat } from "uint8arrays/concat"; +import { randomBytes } from "../../crypto"; + import { AbstractKeypair, IKeypair, IKeypairClass, KeypairType } from "./types"; export function secp256k1PublicKeyToCompressed( @@ -41,7 +41,7 @@ export const Secp256k1Keypair: IKeypairClass = class Secp256k1Keypair } static async generate(): Promise { - const privateKey = await randomBytes(32); + const privateKey = randomBytes(32); const publicKey = secp256k1.publicKeyCreate(privateKey); return new Secp256k1Keypair(privateKey, publicKey); } @@ -69,13 +69,3 @@ export const Secp256k1Keypair: IKeypairClass = class Secp256k1Keypair return secp256k1.ecdsaVerify(sig, msg, this.publicKey); } }; - -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/v4.ts b/src/lib/enr/v4.ts index 0738a30732..0e70dc2640 100644 --- a/src/lib/enr/v4.ts +++ b/src/lib/enr/v4.ts @@ -1,8 +1,8 @@ -import crypto from "crypto"; - import { keccak256 } from "js-sha3"; import * as secp256k1 from "secp256k1"; +import { randomBytes } from "../crypto"; + import { createNodeId } from "./create"; import { NodeId } from "./types"; @@ -10,7 +10,7 @@ export function hash(input: Uint8Array): Uint8Array { return new Uint8Array(keccak256.arrayBuffer(input)); } -export async function createPrivateKey(): Promise { +export function createPrivateKey(): Uint8Array { return randomBytes(32); } @@ -45,13 +45,13 @@ 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)) { throw new Error("Invalid private key"); } } - const _privateKey = privateKey || (await createPrivateKey()); + const _privateKey = privateKey || createPrivateKey(); const _publicKey = publicKey(_privateKey); const _nodeId = nodeId(_publicKey); @@ -66,13 +66,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/version_1.ts b/src/lib/waku_message/version_1.ts index 535c232484..db4883ea4d 100644 --- a/src/lib/waku_message/version_1.ts +++ b/src/lib/waku_message/version_1.ts @@ -1,9 +1,9 @@ import { Buffer } from "buffer"; -import * as crypto from "crypto"; import { keccak256 } from "js-sha3"; import * as secp256k1 from "secp256k1"; +import { randomBytes } from "../crypto"; import { hexToBytes } from "../utils"; import * as ecies from "./ecies"; @@ -258,13 +258,3 @@ function ecRecoverPubKey(messageHash: string, signature: Buffer): Uint8Array { 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); - } -} From 27952e4800f0a74c07a96f8dcc186b257d035924 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 7 Mar 2022 11:12:43 +1100 Subject: [PATCH 4/4] wip: Use browser resolve --- package.json | 3 +++ src/lib/crypto.ts | 24 +----------------------- src/lib/crypto_subtle.ts | 15 +++++++++++++++ src/lib/crypto_subtle_browser.ts | 22 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/lib/crypto_subtle.ts create mode 100644 src/lib/crypto_subtle_browser.ts diff --git a/package.json b/package.json index ffee210f13..108335eb7b 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,9 @@ "**/*.spec.js" ] }, + "browser": { + "./lib/crypto_subtle.js": "./lib/crypto_subtle_browser.js" + }, "size-limit": [ { "path": "build/esm/index.js", diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index fb6cd705a5..61860d126b 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -1,26 +1,4 @@ -import nodeCrypto from "crypto"; - -// IE 11 -declare global { - interface Window { - msCrypto?: Crypto; - } - - interface Crypto { - webkitSubtle?: SubtleCrypto; - } -} - -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"); -} +import { crypto, subtle } from "./crypto_subtle"; export { crypto, subtle }; diff --git a/src/lib/crypto_subtle.ts b/src/lib/crypto_subtle.ts new file mode 100644 index 0000000000..a56492488f --- /dev/null +++ b/src/lib/crypto_subtle.ts @@ -0,0 +1,15 @@ +import nodeCrypto from "crypto"; + +// Types do not seem up-to-date +const crypto: Crypto = nodeCrypto.webcrypto as unknown as Crypto; +if (crypto === undefined) { + throw new Error("node crypto api unavailable"); +} + +const subtle: SubtleCrypto = crypto.subtle || crypto.webkitSubtle; + +if (subtle === undefined) { + throw new Error("node subtle api unavailable"); +} + +export { crypto, subtle }; diff --git a/src/lib/crypto_subtle_browser.ts b/src/lib/crypto_subtle_browser.ts new file mode 100644 index 0000000000..b8261700a8 --- /dev/null +++ b/src/lib/crypto_subtle_browser.ts @@ -0,0 +1,22 @@ +declare global { + interface Window { + msCrypto?: Crypto; + } + + interface Crypto { + webkitSubtle?: SubtleCrypto; + } +} + +const crypto = window.crypto || window.msCrypto; +if (crypto === undefined) { + throw new Error("browser crypto api unavailable"); +} + +const subtle: SubtleCrypto = crypto.subtle || crypto.webkitSubtle; + +if (subtle === undefined) { + throw new Error("browser subtle api unavailable"); +} + +export { crypto, subtle };