import { DigestAlgorithm as WasmDigestAlgorithm, digestAlgorithms as wasmDigestAlgorithms, instantiateWasm,} from "../_wasm_crypto/mod.ts";
import { fnv } from "./_fnv/index.ts";
const webCrypto = ((crypto) => ({ getRandomValues: crypto.getRandomValues?.bind(crypto), randomUUID: crypto.randomUUID?.bind(crypto), subtle: { decrypt: crypto.subtle?.decrypt?.bind(crypto.subtle), deriveBits: crypto.subtle?.deriveBits?.bind(crypto.subtle), deriveKey: crypto.subtle?.deriveKey?.bind(crypto.subtle), digest: crypto.subtle?.digest?.bind(crypto.subtle), encrypt: crypto.subtle?.encrypt?.bind(crypto.subtle), exportKey: crypto.subtle?.exportKey?.bind(crypto.subtle), generateKey: crypto.subtle?.generateKey?.bind(crypto.subtle), importKey: crypto.subtle?.importKey?.bind(crypto.subtle), sign: crypto.subtle?.sign?.bind(crypto.subtle), unwrapKey: crypto.subtle?.unwrapKey?.bind(crypto.subtle), verify: crypto.subtle?.verify?.bind(crypto.subtle), wrapKey: crypto.subtle?.wrapKey?.bind(crypto.subtle), },}))(globalThis.crypto);
const bufferSourceBytes = (data: BufferSource | unknown) => { let bytes: Uint8Array | undefined; if (data instanceof Uint8Array) { bytes = data; } else if (ArrayBuffer.isView(data)) { bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); } else if (data instanceof ArrayBuffer) { bytes = new Uint8Array(data); } return bytes;};
const stdCrypto = ((x) => x)({ ...webCrypto, subtle: { ...webCrypto.subtle,
async digest( algorithm: DigestAlgorithm, data: BufferSource | AsyncIterable<BufferSource> | Iterable<BufferSource>, ): Promise<ArrayBuffer> { const { name, length } = normalizeAlgorithm(algorithm); const bytes = bufferSourceBytes(data);
if (FNVAlgorithms.includes(name)) { return fnv(name, bytes); }
if ( (webCryptoDigestAlgorithms as readonly string[]).includes(name) && bytes ) { return webCrypto.subtle.digest(algorithm, bytes); } else if (wasmDigestAlgorithms.includes(name as WasmDigestAlgorithm)) { if (bytes) { return stdCrypto.subtle.digestSync(algorithm, bytes); } else if ((data as Iterable<BufferSource>)[Symbol.iterator]) { return stdCrypto.subtle.digestSync( algorithm, data as Iterable<BufferSource>, ); } else if ( (data as AsyncIterable<BufferSource>)[Symbol.asyncIterator] ) { const wasmCrypto = instantiateWasm(); const context = new wasmCrypto.DigestContext(name); for await (const chunk of data as AsyncIterable<BufferSource>) { const chunkBytes = bufferSourceBytes(chunk); if (!chunkBytes) { throw new TypeError("data contained chunk of the wrong type"); } context.update(chunkBytes); } return context.digestAndDrop(length).buffer; } else { throw new TypeError( "data must be a BufferSource or [Async]Iterable<BufferSource>", ); } } else if (webCrypto.subtle?.digest) { return webCrypto.subtle.digest( algorithm, (data as unknown) as Uint8Array, ); } else { throw new TypeError(`unsupported digest algorithm: ${algorithm}`); } },
digestSync( algorithm: DigestAlgorithm, data: BufferSource | Iterable<BufferSource>, ): ArrayBuffer { algorithm = normalizeAlgorithm(algorithm);
const bytes = bufferSourceBytes(data);
if (FNVAlgorithms.includes(algorithm.name)) { return fnv(algorithm.name, bytes); }
const wasmCrypto = instantiateWasm(); if (bytes) { return wasmCrypto.digest(algorithm.name, bytes, algorithm.length) .buffer; } else if ((data as Iterable<BufferSource>)[Symbol.iterator]) { const context = new wasmCrypto.DigestContext(algorithm.name); for (const chunk of data as Iterable<BufferSource>) { const chunkBytes = bufferSourceBytes(chunk); if (!chunkBytes) { throw new TypeError("data contained chunk of the wrong type"); } context.update(chunkBytes); } return context.digestAndDrop(algorithm.length).buffer; } else { throw new TypeError( "data must be a BufferSource or Iterable<BufferSource>", ); } }, },});
const FNVAlgorithms = ["FNV32", "FNV32A", "FNV64", "FNV64A"];
const webCryptoDigestAlgorithms = [ "SHA-384", "SHA-256", "SHA-512", "SHA-1",] as const;
type FNVAlgorithms = "FNV32" | "FNV32A" | "FNV64" | "FNV64A";type DigestAlgorithmName = WasmDigestAlgorithm | FNVAlgorithms;
type DigestAlgorithmObject = { name: DigestAlgorithmName; length?: number;};
type DigestAlgorithm = DigestAlgorithmName | DigestAlgorithmObject;
const normalizeAlgorithm = (algorithm: DigestAlgorithm) => ((typeof algorithm === "string") ? { name: algorithm.toUpperCase() } : { ...algorithm, name: algorithm.name.toUpperCase(), }) as DigestAlgorithmObject;
export { stdCrypto as crypto };