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";
// IE 11
declare global {
interface Window {
msCrypto?: Crypto;
}
import { concat } from "uint8arrays/concat";
interface Crypto {
webkitSubtle?: SubtleCrypto;
declare const self: Record<string, any> | 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<ArrayBuffer> {
return subtle.digest({ name: "SHA-256" }, msg);
export async function sha256(...messages: Uint8Array[]): Promise<Uint8Array> {
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");
}
}

View File

@ -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<Uint8Array> {
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<Uint8Array> {
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<Uint8Array> {
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<boolean> {
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)
);
}

View File

@ -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<Buffer> {
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<Buffer> {
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);
}