Improve error feedback when crypto/subtle is not available

This commit is contained in:
Franck Royer 2022-05-09 17:34:31 +10:00
parent 556e060335
commit 1ca7215478
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
3 changed files with 53 additions and 38 deletions

View File

@ -1,33 +1,48 @@
import nodeCrypto from "crypto"; import nodeCrypto from "crypto";
// IE 11 import { concat } from "uint8arrays/concat";
declare global {
interface Window {
msCrypto?: Crypto;
}
interface Crypto { declare const self: Record<string, any> | undefined;
webkitSubtle?: SubtleCrypto; 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 = export function randomBytes(bytesLength = 32): Uint8Array {
(typeof window !== "undefined" && if (crypto.web) {
(window as Window) && return crypto.web.getRandomValues(new Uint8Array(bytesLength));
(window.crypto || window.msCrypto)) || } else if (crypto.node) {
(nodeCrypto.webcrypto as unknown as Crypto); const { randomBytes } = crypto.node;
const subtle: SubtleCrypto = crypto.subtle || crypto.webkitSubtle; return Uint8Array.from(randomBytes(bytesLength));
} else {
if (subtle === undefined) { throw new Error(
throw new Error("crypto and/or subtle api unavailable"); "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 async function sha256(...messages: Uint8Array[]): Promise<Uint8Array> {
if (crypto.web) {
export function randomBytes(size: number): Uint8Array { const buffer = await crypto.web.subtle.digest("SHA-256", concat(messages));
return crypto.getRandomValues(new Uint8Array(size)); return new Uint8Array(buffer);
} } else if (crypto.node) {
const { createHash } = crypto.node;
export function sha256(msg: ArrayBufferLike): Promise<ArrayBuffer> { const hash = createHash("sha256");
return subtle.digest({ name: "SHA-256" }, msg); messages.forEach((m) => hash.update(m));
return Uint8Array.from(hash.digest());
} else {
throw new Error("The environment doesn't have sha256 function");
}
} }

View File

@ -1,7 +1,7 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/secp256k1";
import { concat } from "uint8arrays/concat"; import { concat } from "uint8arrays/concat";
import { randomBytes, sha256, subtle } from "../crypto"; import { getSubtle, randomBytes, sha256 } from "../crypto";
import { hexToBytes } from "../utils"; import { hexToBytes } from "../utils";
/** /**
* HKDF as implemented in go-ethereum. * HKDF as implemented in go-ethereum.
@ -37,10 +37,10 @@ function aesCtrEncrypt(
key: ArrayBufferLike, key: ArrayBufferLike,
data: ArrayBufferLike data: ArrayBufferLike
): Promise<Uint8Array> { ): Promise<Uint8Array> {
return subtle return getSubtle()
.importKey("raw", key, "AES-CTR", false, ["encrypt"]) .importKey("raw", key, "AES-CTR", false, ["encrypt"])
.then((cryptoKey) => .then((cryptoKey) =>
subtle.encrypt( getSubtle().encrypt(
{ name: "AES-CTR", counter: counter, length: 128 }, { name: "AES-CTR", counter: counter, length: 128 },
cryptoKey, cryptoKey,
data data
@ -54,10 +54,10 @@ function aesCtrDecrypt(
key: ArrayBufferLike, key: ArrayBufferLike,
data: ArrayBufferLike data: ArrayBufferLike
): Promise<Uint8Array> { ): Promise<Uint8Array> {
return subtle return getSubtle()
.importKey("raw", key, "AES-CTR", false, ["decrypt"]) .importKey("raw", key, "AES-CTR", false, ["decrypt"])
.then((cryptoKey) => .then((cryptoKey) =>
subtle.decrypt( getSubtle().decrypt(
{ name: "AES-CTR", counter: counter, length: 128 }, { name: "AES-CTR", counter: counter, length: 128 },
cryptoKey, cryptoKey,
data data
@ -71,9 +71,9 @@ function hmacSha256Sign(
msg: ArrayBufferLike msg: ArrayBufferLike
): PromiseLike<Uint8Array> { ): PromiseLike<Uint8Array> {
const algorithm = { name: "HMAC", hash: { name: "SHA-256" } }; const algorithm = { name: "HMAC", hash: { name: "SHA-256" } };
return subtle return getSubtle()
.importKey("raw", key, algorithm, false, ["sign"]) .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)); .then((bytes) => new Uint8Array(bytes));
} }
@ -83,9 +83,9 @@ function hmacSha256Verify(
sig: ArrayBufferLike sig: ArrayBufferLike
): Promise<boolean> { ): Promise<boolean> {
const algorithm = { name: "HMAC", hash: { name: "SHA-256" } }; 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) => return _key.then((cryptoKey) =>
subtle.verify(algorithm, cryptoKey, sig, msg) getSubtle().verify(algorithm, cryptoKey, sig, msg)
); );
} }

View File

@ -1,4 +1,4 @@
import { randomBytes, subtle } from "../crypto"; import { getSubtle, randomBytes } from "../crypto";
export const KeySize = 32; export const KeySize = 32;
export const IvSize = 12; export const IvSize = 12;
@ -11,10 +11,10 @@ export async function encrypt(
key: Buffer, key: Buffer,
clearText: Buffer clearText: Buffer
): Promise<Buffer> { ): Promise<Buffer> {
return subtle return getSubtle()
.importKey("raw", key, Algorithm, false, ["encrypt"]) .importKey("raw", key, Algorithm, false, ["encrypt"])
.then((cryptoKey) => .then((cryptoKey) =>
subtle.encrypt({ iv, ...Algorithm }, cryptoKey, clearText) getSubtle().encrypt({ iv, ...Algorithm }, cryptoKey, clearText)
) )
.then(Buffer.from); .then(Buffer.from);
} }
@ -24,10 +24,10 @@ export async function decrypt(
key: Buffer, key: Buffer,
cipherText: Buffer cipherText: Buffer
): Promise<Buffer> { ): Promise<Buffer> {
return subtle return getSubtle()
.importKey("raw", key, Algorithm, false, ["decrypt"]) .importKey("raw", key, Algorithm, false, ["decrypt"])
.then((cryptoKey) => .then((cryptoKey) =>
subtle.decrypt({ iv, ...Algorithm }, cryptoKey, cipherText) getSubtle().decrypt({ iv, ...Algorithm }, cryptoKey, cipherText)
) )
.then(Buffer.from); .then(Buffer.from);
} }