UNPKG

@gaonengwww/jose

Version:

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes

315 lines (304 loc) 9.19 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/lib/encrypt.ts var encrypt_exports = {}; __export(encrypt_exports, { default: () => encrypt_default }); module.exports = __toCommonJS(encrypt_exports); // src/lib/buffer_utils.ts var encoder = new TextEncoder(); var decoder = new TextDecoder(); var MAX_INT32 = 2 ** 32; function concat(...buffers) { const size = buffers.reduce((acc, { length }) => acc + length, 0); const buf = new Uint8Array(size); let i = 0; for (const buffer of buffers) { buf.set(buffer, i); i += buffer.length; } return buf; } function writeUInt32BE(buf, value, offset) { if (value < 0 || value >= MAX_INT32) { throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`); } buf.set([value >>> 24, value >>> 16, value >>> 8, value & 255], offset); } function uint64be(value) { const high = Math.floor(value / MAX_INT32); const low = value % MAX_INT32; const buf = new Uint8Array(8); writeUInt32BE(buf, high, 0); writeUInt32BE(buf, low, 4); return buf; } // src/util/errors.ts var JOSEError = class extends Error { /** * A unique error code for the particular error subclass. * * @ignore */ static code = "ERR_JOSE_GENERIC"; /** A unique error code for {@link JOSEError}. */ code = "ERR_JOSE_GENERIC"; /** @ignore */ constructor(message2, options) { super(message2, options); this.name = this.constructor.name; Error.captureStackTrace?.(this, this.constructor); } }; var JOSENotSupported = class extends JOSEError { /** @ignore */ static code = "ERR_JOSE_NOT_SUPPORTED"; /** A unique error code for {@link JOSENotSupported}. */ code = "ERR_JOSE_NOT_SUPPORTED"; }; var JWEInvalid = class extends JOSEError { /** @ignore */ static code = "ERR_JWE_INVALID"; /** A unique error code for {@link JWEInvalid}. */ code = "ERR_JWE_INVALID"; }; // src/lib/iv.ts function bitLength(alg) { switch (alg) { case "A128GCM": case "A128GCMKW": case "A192GCM": case "A192GCMKW": case "A256GCM": case "A256GCMKW": return 96; case "A128CBC-HS256": case "A192CBC-HS384": case "A256CBC-HS512": return 128; default: throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); } } var iv_default = (alg) => crypto.getRandomValues(new Uint8Array(bitLength(alg) >> 3)); // src/lib/check_iv_length.ts var check_iv_length_default = (enc, iv) => { if (iv.length << 3 !== bitLength(enc)) { throw new JWEInvalid("Invalid Initialization Vector length"); } }; // src/lib/check_cek_length.ts var check_cek_length_default = (cek, expected) => { const actual = cek.byteLength << 3; if (actual !== expected) { throw new JWEInvalid( `Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits` ); } }; // src/lib/crypto_key.ts function unusable(name, prop = "algorithm.name") { return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`); } function isAlgorithm(algorithm, name) { return algorithm.name === name; } function getHashLength(hash) { return parseInt(hash.name.slice(4), 10); } function checkUsage(key, usage) { if (usage && !key.usages.includes(usage)) { throw new TypeError( `CryptoKey does not support this operation, its usages must include ${usage}.` ); } } function checkEncCryptoKey(key, alg, usage) { switch (alg) { case "A128GCM": case "A192GCM": case "A256GCM": { if (!isAlgorithm(key.algorithm, "AES-GCM")) throw unusable("AES-GCM"); const expected = parseInt(alg.slice(1, 4), 10); const actual = key.algorithm.length; if (actual !== expected) throw unusable(expected, "algorithm.length"); break; } case "A128KW": case "A192KW": case "A256KW": { if (!isAlgorithm(key.algorithm, "AES-KW")) throw unusable("AES-KW"); const expected = parseInt(alg.slice(1, 4), 10); const actual = key.algorithm.length; if (actual !== expected) throw unusable(expected, "algorithm.length"); break; } case "ECDH": { switch (key.algorithm.name) { case "ECDH": case "X25519": break; default: throw unusable("ECDH or X25519"); } break; } case "PBES2-HS256+A128KW": case "PBES2-HS384+A192KW": case "PBES2-HS512+A256KW": if (!isAlgorithm(key.algorithm, "PBKDF2")) throw unusable("PBKDF2"); break; case "RSA-OAEP": case "RSA-OAEP-256": case "RSA-OAEP-384": case "RSA-OAEP-512": { if (!isAlgorithm(key.algorithm, "RSA-OAEP")) throw unusable("RSA-OAEP"); const expected = parseInt(alg.slice(9), 10) || 1; const actual = getHashLength(key.algorithm.hash); if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash"); break; } default: throw new TypeError("CryptoKey does not support this operation"); } checkUsage(key, usage); } // src/lib/invalid_key_input.ts function message(msg, actual, ...types) { types = types.filter(Boolean); if (types.length > 2) { const last = types.pop(); msg += `one of type ${types.join(", ")}, or ${last}.`; } else if (types.length === 2) { msg += `one of type ${types[0]} or ${types[1]}.`; } else { msg += `of type ${types[0]}.`; } if (actual == null) { msg += ` Received ${actual}`; } else if (typeof actual === "function" && actual.name) { msg += ` Received function ${actual.name}`; } else if (typeof actual === "object" && actual != null) { if (actual.constructor?.name) { msg += ` Received an instance of ${actual.constructor.name}`; } } return msg; } var invalid_key_input_default = (actual, ...types) => { return message("Key must be ", actual, ...types); }; // src/lib/is_key_like.ts function isCryptoKey(key) { return key?.[Symbol.toStringTag] === "CryptoKey"; } // src/lib/encrypt.ts async function cbcEncrypt(enc, plaintext, cek, iv, aad) { if (!(cek instanceof Uint8Array)) { throw new TypeError(invalid_key_input_default(cek, "Uint8Array")); } const keySize = parseInt(enc.slice(1, 4), 10); const encKey = await crypto.subtle.importKey( "raw", cek.subarray(keySize >> 3), "AES-CBC", false, ["encrypt"] ); const macKey = await crypto.subtle.importKey( "raw", cek.subarray(0, keySize >> 3), { hash: `SHA-${keySize << 1}`, name: "HMAC" }, false, ["sign"] ); const ciphertext = new Uint8Array( await crypto.subtle.encrypt( { iv, name: "AES-CBC" }, encKey, plaintext ) ); const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); const tag = new Uint8Array( (await crypto.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3) ); return { ciphertext, tag, iv }; } async function gcmEncrypt(enc, plaintext, cek, iv, aad) { let encKey; if (cek instanceof Uint8Array) { encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, ["encrypt"]); } else { checkEncCryptoKey(cek, enc, "encrypt"); encKey = cek; } const encrypted = new Uint8Array( await crypto.subtle.encrypt( { additionalData: aad, iv, name: "AES-GCM", tagLength: 128 }, encKey, plaintext ) ); const tag = encrypted.slice(-16); const ciphertext = encrypted.slice(0, -16); return { ciphertext, tag, iv }; } var encrypt_default = async (enc, plaintext, cek, iv, aad) => { if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { throw new TypeError( invalid_key_input_default(cek, "CryptoKey", "KeyObject", "Uint8Array", "JSON Web Key") ); } if (iv) { check_iv_length_default(enc, iv); } else { iv = iv_default(enc); } switch (enc) { case "A128CBC-HS256": case "A192CBC-HS384": case "A256CBC-HS512": if (cek instanceof Uint8Array) { check_cek_length_default(cek, parseInt(enc.slice(-3), 10)); } return cbcEncrypt(enc, plaintext, cek, iv, aad); case "A128GCM": case "A192GCM": case "A256GCM": if (cek instanceof Uint8Array) { check_cek_length_default(cek, parseInt(enc.slice(1, 4), 10)); } return gcmEncrypt(enc, plaintext, cek, iv, aad); default: throw new JOSENotSupported("Unsupported JWE Content Encryption Algorithm"); } };