From 1ca7215478edc67a48cb07058a120c5b44d6d5ce Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 9 May 2022 17:34:31 +1000 Subject: [PATCH] Improve error feedback when crypto/subtle is not available --- src/lib/crypto.ts | 63 +++++++++++++++++++------------ src/lib/waku_message/ecies.ts | 18 ++++----- src/lib/waku_message/symmetric.ts | 10 ++--- 3 files changed, 53 insertions(+), 38 deletions(-) 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/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/symmetric.ts b/src/lib/waku_message/symmetric.ts index b9cac71c56..9d2890a2a4 100644 --- a/src/lib/waku_message/symmetric.ts +++ b/src/lib/waku_message/symmetric.ts @@ -1,4 +1,4 @@ -import { randomBytes, subtle } from "../crypto"; +import { getSubtle, randomBytes } from "../crypto"; export const KeySize = 32; export const IvSize = 12; @@ -11,10 +11,10 @@ export async function encrypt( key: Buffer, clearText: Buffer ): Promise { - return subtle + return getSubtle() .importKey("raw", key, Algorithm, false, ["encrypt"]) .then((cryptoKey) => - subtle.encrypt({ iv, ...Algorithm }, cryptoKey, clearText) + getSubtle().encrypt({ iv, ...Algorithm }, cryptoKey, clearText) ) .then(Buffer.from); } @@ -24,10 +24,10 @@ export async function decrypt( key: Buffer, cipherText: Buffer ): Promise { - return subtle + return getSubtle() .importKey("raw", key, Algorithm, false, ["decrypt"]) .then((cryptoKey) => - subtle.decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) + getSubtle().decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) ) .then(Buffer.from); }