diff --git a/CHANGELOG.md b/CHANGELOG.md index 34e9d9fd61..9e0d6fbc43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [4.13.2](https://github.com/panva/jose/compare/v4.13.1...v4.13.2) (2023-04-12) + + +### Refactor + +* src/util/decode_protected_header.ts ([5716725](https://github.com/panva/jose/commit/5716725d7eb6fa8a416638db9d448840f839f620)) + ## [4.13.1](https://github.com/panva/jose/compare/v4.13.0...v4.13.1) (2023-03-02) diff --git a/dist/browser/index.bundle.js b/dist/browser/index.bundle.js new file mode 100644 index 0000000000..35e931f177 --- /dev/null +++ b/dist/browser/index.bundle.js @@ -0,0 +1,3363 @@ +var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// dist/browser/runtime/webcrypto.js +var webcrypto_default = crypto; +var isCryptoKey = (key) => key instanceof CryptoKey; + +// dist/browser/runtime/digest.js +var digest = async (algorithm, data) => { + const subtleDigest = `SHA-${algorithm.slice(-3)}`; + return new Uint8Array(await webcrypto_default.subtle.digest(subtleDigest, data)); +}; +var digest_default = digest; + +// dist/browser/lib/buffer_utils.js +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; + buffers.forEach((buffer) => { + buf.set(buffer, i); + i += buffer.length; + }); + return buf; +} +function p2s(alg, p2sInput) { + return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput); +} +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; +} +function uint32be(value) { + const buf = new Uint8Array(4); + writeUInt32BE(buf, value); + return buf; +} +function lengthAndInput(input) { + return concat(uint32be(input.length), input); +} +async function concatKdf(secret, bits, value) { + const iterations = Math.ceil((bits >> 3) / 32); + const res = new Uint8Array(iterations * 32); + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length); + buf.set(uint32be(iter + 1)); + buf.set(secret, 4); + buf.set(value, 4 + secret.length); + res.set(await digest_default("sha256", buf), iter * 32); + } + return res.slice(0, bits >> 3); +} + +// dist/browser/runtime/base64url.js +var encodeBase64 = (input) => { + let unencoded = input; + if (typeof unencoded === "string") { + unencoded = encoder.encode(unencoded); + } + const CHUNK_SIZE = 32768; + const arr = []; + for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) { + arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))); + } + return btoa(arr.join("")); +}; +var encode = (input) => { + return encodeBase64(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); +}; +var decodeBase64 = (encoded) => { + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +}; +var decode = (input) => { + let encoded = input; + if (encoded instanceof Uint8Array) { + encoded = decoder.decode(encoded); + } + encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""); + try { + return decodeBase64(encoded); + } catch (_a) { + throw new TypeError("The input to be decoded is not correctly encoded."); + } +}; + +// dist/browser/util/errors.js +var errors_exports = {}; +__export(errors_exports, { + JOSEAlgNotAllowed: () => JOSEAlgNotAllowed, + JOSEError: () => JOSEError, + JOSENotSupported: () => JOSENotSupported, + JWEDecryptionFailed: () => JWEDecryptionFailed, + JWEInvalid: () => JWEInvalid, + JWKInvalid: () => JWKInvalid, + JWKSInvalid: () => JWKSInvalid, + JWKSMultipleMatchingKeys: () => JWKSMultipleMatchingKeys, + JWKSNoMatchingKey: () => JWKSNoMatchingKey, + JWKSTimeout: () => JWKSTimeout, + JWSInvalid: () => JWSInvalid, + JWSSignatureVerificationFailed: () => JWSSignatureVerificationFailed, + JWTClaimValidationFailed: () => JWTClaimValidationFailed, + JWTExpired: () => JWTExpired, + JWTInvalid: () => JWTInvalid +}); +var JOSEError = class extends Error { + static get code() { + return "ERR_JOSE_GENERIC"; + } + constructor(message2) { + var _a; + super(message2); + this.code = "ERR_JOSE_GENERIC"; + this.name = this.constructor.name; + (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); + } +}; +var JWTClaimValidationFailed = class extends JOSEError { + static get code() { + return "ERR_JWT_CLAIM_VALIDATION_FAILED"; + } + constructor(message2, claim = "unspecified", reason = "unspecified") { + super(message2); + this.code = "ERR_JWT_CLAIM_VALIDATION_FAILED"; + this.claim = claim; + this.reason = reason; + } +}; +var JWTExpired = class extends JOSEError { + static get code() { + return "ERR_JWT_EXPIRED"; + } + constructor(message2, claim = "unspecified", reason = "unspecified") { + super(message2); + this.code = "ERR_JWT_EXPIRED"; + this.claim = claim; + this.reason = reason; + } +}; +var JOSEAlgNotAllowed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JOSE_ALG_NOT_ALLOWED"; + } + static get code() { + return "ERR_JOSE_ALG_NOT_ALLOWED"; + } +}; +var JOSENotSupported = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JOSE_NOT_SUPPORTED"; + } + static get code() { + return "ERR_JOSE_NOT_SUPPORTED"; + } +}; +var JWEDecryptionFailed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWE_DECRYPTION_FAILED"; + this.message = "decryption operation failed"; + } + static get code() { + return "ERR_JWE_DECRYPTION_FAILED"; + } +}; +var JWEInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWE_INVALID"; + } + static get code() { + return "ERR_JWE_INVALID"; + } +}; +var JWSInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWS_INVALID"; + } + static get code() { + return "ERR_JWS_INVALID"; + } +}; +var JWTInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWT_INVALID"; + } + static get code() { + return "ERR_JWT_INVALID"; + } +}; +var JWKInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWK_INVALID"; + } + static get code() { + return "ERR_JWK_INVALID"; + } +}; +var JWKSInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_INVALID"; + } + static get code() { + return "ERR_JWKS_INVALID"; + } +}; +var JWKSNoMatchingKey = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_NO_MATCHING_KEY"; + this.message = "no applicable key found in the JSON Web Key Set"; + } + static get code() { + return "ERR_JWKS_NO_MATCHING_KEY"; + } +}; +var JWKSMultipleMatchingKeys = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS"; + this.message = "multiple matching keys found in the JSON Web Key Set"; + } + static get code() { + return "ERR_JWKS_MULTIPLE_MATCHING_KEYS"; + } +}; +var JWKSTimeout = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_TIMEOUT"; + this.message = "request timed out"; + } + static get code() { + return "ERR_JWKS_TIMEOUT"; + } +}; +var JWSSignatureVerificationFailed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"; + this.message = "signature verification failed"; + } + static get code() { + return "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"; + } +}; + +// dist/browser/runtime/random.js +var random_default = webcrypto_default.getRandomValues.bind(webcrypto_default); + +// dist/browser/lib/iv.js +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) => random_default(new Uint8Array(bitLength(alg) >> 3)); + +// dist/browser/lib/check_iv_length.js +var checkIvLength = (enc, iv) => { + if (iv.length << 3 !== bitLength(enc)) { + throw new JWEInvalid("Invalid Initialization Vector length"); + } +}; +var check_iv_length_default = checkIvLength; + +// dist/browser/runtime/check_cek_length.js +var checkCekLength = (cek, expected) => { + const actual = cek.byteLength << 3; + if (actual !== expected) { + throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } +}; +var check_cek_length_default = checkCekLength; + +// dist/browser/runtime/timing_safe_equal.js +var timingSafeEqual = (a, b) => { + if (!(a instanceof Uint8Array)) { + throw new TypeError("First argument must be a buffer"); + } + if (!(b instanceof Uint8Array)) { + throw new TypeError("Second argument must be a buffer"); + } + if (a.length !== b.length) { + throw new TypeError("Input buffers must have the same length"); + } + const len = a.length; + let out = 0; + let i = -1; + while (++i < len) { + out |= a[i] ^ b[i]; + } + return out === 0; +}; +var timing_safe_equal_default = timingSafeEqual; + +// dist/browser/runtime/env.js +function isCloudflareWorkers() { + return typeof WebSocketPair !== "undefined" || typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime !== "undefined" && EdgeRuntime === "vercel"; +} + +// dist/browser/lib/crypto_key.js +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 getNamedCurve(alg) { + switch (alg) { + case "ES256": + return "P-256"; + case "ES384": + return "P-384"; + case "ES512": + return "P-521"; + default: + throw new Error("unreachable"); + } +} +function checkUsage(key, usages) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = "CryptoKey does not support this operation, its usages must include "; + if (usages.length > 2) { + const last = usages.pop(); + msg += `one of ${usages.join(", ")}, or ${last}.`; + } else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.`; + } else { + msg += `${usages[0]}.`; + } + throw new TypeError(msg); + } +} +function checkSigCryptoKey(key, alg, ...usages) { + switch (alg) { + case "HS256": + case "HS384": + case "HS512": { + if (!isAlgorithm(key.algorithm, "HMAC")) + throw unusable("HMAC"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "RS256": + case "RS384": + case "RS512": { + if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) + throw unusable("RSASSA-PKCS1-v1_5"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "PS256": + case "PS384": + case "PS512": { + if (!isAlgorithm(key.algorithm, "RSA-PSS")) + throw unusable("RSA-PSS"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "EdDSA": { + if (key.algorithm.name !== "Ed25519" && key.algorithm.name !== "Ed448") { + if (isCloudflareWorkers()) { + if (isAlgorithm(key.algorithm, "NODE-ED25519")) + break; + throw unusable("Ed25519, Ed448, or NODE-ED25519"); + } + throw unusable("Ed25519 or Ed448"); + } + break; + } + case "ES256": + case "ES384": + case "ES512": { + if (!isAlgorithm(key.algorithm, "ECDSA")) + throw unusable("ECDSA"); + const expected = getNamedCurve(alg); + const actual = key.algorithm.namedCurve; + if (actual !== expected) + throw unusable(expected, "algorithm.namedCurve"); + break; + } + default: + throw new TypeError("CryptoKey does not support this operation"); + } + checkUsage(key, usages); +} +function checkEncCryptoKey(key, alg, ...usages) { + 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": + case "X448": + break; + default: + throw unusable("ECDH, X25519, or X448"); + } + 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, usages); +} + +// dist/browser/lib/invalid_key_input.js +function message(msg, actual, ...types2) { + if (types2.length > 2) { + const last = types2.pop(); + msg += `one of type ${types2.join(", ")}, or ${last}.`; + } else if (types2.length === 2) { + msg += `one of type ${types2[0]} or ${types2[1]}.`; + } else { + msg += `of type ${types2[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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}`; + } + } + return msg; +} +var invalid_key_input_default = (actual, ...types2) => { + return message("Key must be ", actual, ...types2); +}; +function withAlg(alg, actual, ...types2) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types2); +} + +// dist/browser/runtime/is_key_like.js +var is_key_like_default = (key) => { + return isCryptoKey(key); +}; +var types = ["CryptoKey"]; + +// dist/browser/runtime/decrypt.js +async function cbcDecrypt(enc, cek, ciphertext, iv, tag, 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 webcrypto_default.subtle.importKey("raw", cek.subarray(keySize >> 3), "AES-CBC", false, ["decrypt"]); + const macKey = await webcrypto_default.subtle.importKey("raw", cek.subarray(0, keySize >> 3), { + hash: `SHA-${keySize << 1}`, + name: "HMAC" + }, false, ["sign"]); + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const expectedTag = new Uint8Array((await webcrypto_default.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)); + let macCheckPassed; + try { + macCheckPassed = timing_safe_equal_default(tag, expectedTag); + } catch (_a) { + } + if (!macCheckPassed) { + throw new JWEDecryptionFailed(); + } + let plaintext; + try { + plaintext = new Uint8Array(await webcrypto_default.subtle.decrypt({ iv, name: "AES-CBC" }, encKey, ciphertext)); + } catch (_b) { + } + if (!plaintext) { + throw new JWEDecryptionFailed(); + } + return plaintext; +} +async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) { + let encKey; + if (cek instanceof Uint8Array) { + encKey = await webcrypto_default.subtle.importKey("raw", cek, "AES-GCM", false, ["decrypt"]); + } else { + checkEncCryptoKey(cek, enc, "decrypt"); + encKey = cek; + } + try { + return new Uint8Array(await webcrypto_default.subtle.decrypt({ + additionalData: aad, + iv, + name: "AES-GCM", + tagLength: 128 + }, encKey, concat(ciphertext, tag))); + } catch (_a) { + throw new JWEDecryptionFailed(); + } +} +var decrypt = async (enc, cek, ciphertext, iv, tag, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalid_key_input_default(cek, ...types, "Uint8Array")); + } + check_iv_length_default(enc, iv); + 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 cbcDecrypt(enc, cek, ciphertext, iv, tag, aad); + case "A128GCM": + case "A192GCM": + case "A256GCM": + if (cek instanceof Uint8Array) + check_cek_length_default(cek, parseInt(enc.slice(1, 4), 10)); + return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad); + default: + throw new JOSENotSupported("Unsupported JWE Content Encryption Algorithm"); + } +}; +var decrypt_default = decrypt; + +// dist/browser/runtime/zlib.js +var inflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.'); +}; +var deflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.'); +}; + +// dist/browser/lib/is_disjoint.js +var isDisjoint = (...headers) => { + const sources = headers.filter(Boolean); + if (sources.length === 0 || sources.length === 1) { + return true; + } + let acc; + for (const header of sources) { + const parameters = Object.keys(header); + if (!acc || acc.size === 0) { + acc = new Set(parameters); + continue; + } + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false; + } + acc.add(parameter); + } + } + return true; +}; +var is_disjoint_default = isDisjoint; + +// dist/browser/lib/is_object.js +function isObjectLike(value) { + return typeof value === "object" && value !== null; +} +function isObject(input) { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") { + return false; + } + if (Object.getPrototypeOf(input) === null) { + return true; + } + let proto = input; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(input) === proto; +} + +// dist/browser/runtime/bogus.js +var bogusWebCrypto = [ + { hash: "SHA-256", name: "HMAC" }, + true, + ["sign"] +]; +var bogus_default = bogusWebCrypto; + +// dist/browser/runtime/aeskw.js +function checkKeySize(key, alg) { + if (key.algorithm.length !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`); + } +} +function getCryptoKey(key, alg, usage) { + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + return webcrypto_default.subtle.importKey("raw", key, "AES-KW", true, [usage]); + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); +} +var wrap = async (alg, key, cek) => { + const cryptoKey = await getCryptoKey(key, alg, "wrapKey"); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await webcrypto_default.subtle.importKey("raw", cek, ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW")); +}; +var unwrap = async (alg, key, encryptedKey) => { + const cryptoKey = await getCryptoKey(key, alg, "unwrapKey"); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await webcrypto_default.subtle.unwrapKey("raw", encryptedKey, cryptoKey, "AES-KW", ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.exportKey("raw", cryptoKeyCek)); +}; + +// dist/browser/runtime/ecdhes.js +async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { + if (!isCryptoKey(publicKey)) { + throw new TypeError(invalid_key_input_default(publicKey, ...types)); + } + checkEncCryptoKey(publicKey, "ECDH"); + if (!isCryptoKey(privateKey)) { + throw new TypeError(invalid_key_input_default(privateKey, ...types)); + } + checkEncCryptoKey(privateKey, "ECDH", "deriveBits"); + const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); + let length; + if (publicKey.algorithm.name === "X25519") { + length = 256; + } else if (publicKey.algorithm.name === "X448") { + length = 448; + } else { + length = Math.ceil(parseInt(publicKey.algorithm.namedCurve.substr(-3), 10) / 8) << 3; + } + const sharedSecret = new Uint8Array(await webcrypto_default.subtle.deriveBits({ + name: publicKey.algorithm.name, + public: publicKey + }, privateKey, length)); + return concatKdf(sharedSecret, keyLength, value); +} +async function generateEpk(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return webcrypto_default.subtle.generateKey(key.algorithm, true, ["deriveBits"]); +} +function ecdhAllowed(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return ["P-256", "P-384", "P-521"].includes(key.algorithm.namedCurve) || key.algorithm.name === "X25519" || key.algorithm.name === "X448"; +} + +// dist/browser/lib/check_p2s.js +function checkP2s(p2s2) { + if (!(p2s2 instanceof Uint8Array) || p2s2.length < 8) { + throw new JWEInvalid("PBES2 Salt Input must be 8 or more octets"); + } +} + +// dist/browser/runtime/pbes2kw.js +function getCryptoKey2(key, alg) { + if (key instanceof Uint8Array) { + return webcrypto_default.subtle.importKey("raw", key, "PBKDF2", false, ["deriveBits"]); + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, "deriveBits", "deriveKey"); + return key; + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); +} +async function deriveKey2(p2s2, alg, p2c, key) { + checkP2s(p2s2); + const salt = p2s(alg, p2s2); + const keylen = parseInt(alg.slice(13, 16), 10); + const subtleAlg = { + hash: `SHA-${alg.slice(8, 11)}`, + iterations: p2c, + name: "PBKDF2", + salt + }; + const wrapAlg = { + length: keylen, + name: "AES-KW" + }; + const cryptoKey = await getCryptoKey2(key, alg); + if (cryptoKey.usages.includes("deriveBits")) { + return new Uint8Array(await webcrypto_default.subtle.deriveBits(subtleAlg, cryptoKey, keylen)); + } + if (cryptoKey.usages.includes("deriveKey")) { + return webcrypto_default.subtle.deriveKey(subtleAlg, cryptoKey, wrapAlg, false, ["wrapKey", "unwrapKey"]); + } + throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"'); +} +var encrypt = async (alg, key, cek, p2c = 2048, p2s2 = random_default(new Uint8Array(16))) => { + const derived = await deriveKey2(p2s2, alg, p2c, key); + const encryptedKey = await wrap(alg.slice(-6), derived, cek); + return { encryptedKey, p2c, p2s: encode(p2s2) }; +}; +var decrypt2 = async (alg, key, encryptedKey, p2c, p2s2) => { + const derived = await deriveKey2(p2s2, alg, p2c, key); + return unwrap(alg.slice(-6), derived, encryptedKey); +}; + +// dist/browser/runtime/subtle_rsaes.js +function subtleRsaEs(alg) { + switch (alg) { + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + return "RSA-OAEP"; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} + +// dist/browser/runtime/check_key_length.js +var check_key_length_default = (alg, key) => { + if (alg.startsWith("RS") || alg.startsWith("PS")) { + const { modulusLength } = key.algorithm; + if (typeof modulusLength !== "number" || modulusLength < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); + } + } +}; + +// dist/browser/runtime/rsaes.js +var encrypt2 = async (alg, key, cek) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + checkEncCryptoKey(key, alg, "encrypt", "wrapKey"); + check_key_length_default(alg, key); + if (key.usages.includes("encrypt")) { + return new Uint8Array(await webcrypto_default.subtle.encrypt(subtleRsaEs(alg), key, cek)); + } + if (key.usages.includes("wrapKey")) { + const cryptoKeyCek = await webcrypto_default.subtle.importKey("raw", cek, ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.wrapKey("raw", cryptoKeyCek, key, subtleRsaEs(alg))); + } + throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation'); +}; +var decrypt3 = async (alg, key, encryptedKey) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + checkEncCryptoKey(key, alg, "decrypt", "unwrapKey"); + check_key_length_default(alg, key); + if (key.usages.includes("decrypt")) { + return new Uint8Array(await webcrypto_default.subtle.decrypt(subtleRsaEs(alg), key, encryptedKey)); + } + if (key.usages.includes("unwrapKey")) { + const cryptoKeyCek = await webcrypto_default.subtle.unwrapKey("raw", encryptedKey, key, subtleRsaEs(alg), ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.exportKey("raw", cryptoKeyCek)); + } + throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation'); +}; + +// dist/browser/lib/cek.js +function bitLength2(alg) { + switch (alg) { + case "A128GCM": + return 128; + case "A192GCM": + return 192; + case "A256GCM": + case "A128CBC-HS256": + return 256; + case "A192CBC-HS384": + return 384; + case "A256CBC-HS512": + return 512; + default: + throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } +} +var cek_default = (alg) => random_default(new Uint8Array(bitLength2(alg) >> 3)); + +// dist/browser/lib/format_pem.js +var format_pem_default = (b64, descriptor) => { + const newlined = (b64.match(/.{1,64}/g) || []).join("\n"); + return `-----BEGIN ${descriptor}----- +${newlined} +-----END ${descriptor}-----`; +}; + +// dist/browser/runtime/asn1.js +var genericExport = async (keyType, keyFormat, key) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + if (!key.extractable) { + throw new TypeError("CryptoKey is not extractable"); + } + if (key.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`); + } + return format_pem_default(encodeBase64(new Uint8Array(await webcrypto_default.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`); +}; +var toSPKI = (key) => { + return genericExport("public", "spki", key); +}; +var toPKCS8 = (key) => { + return genericExport("private", "pkcs8", key); +}; +var findOid = (keyData, oid, from = 0) => { + if (from === 0) { + oid.unshift(oid.length); + oid.unshift(6); + } + let i = keyData.indexOf(oid[0], from); + if (i === -1) + return false; + const sub = keyData.subarray(i, i + oid.length); + if (sub.length !== oid.length) + return false; + return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1); +}; +var getNamedCurve2 = (keyData) => { + switch (true) { + case findOid(keyData, [42, 134, 72, 206, 61, 3, 1, 7]): + return "P-256"; + case findOid(keyData, [43, 129, 4, 0, 34]): + return "P-384"; + case findOid(keyData, [43, 129, 4, 0, 35]): + return "P-521"; + case findOid(keyData, [43, 101, 110]): + return "X25519"; + case findOid(keyData, [43, 101, 111]): + return "X448"; + case findOid(keyData, [43, 101, 112]): + return "Ed25519"; + case findOid(keyData, [43, 101, 113]): + return "Ed448"; + default: + throw new JOSENotSupported("Invalid or unsupported EC Key Curve or OKP Key Sub Type"); + } +}; +var genericImport = async (replace, keyFormat, pem, alg, options) => { + var _a, _b; + let algorithm; + let keyUsages; + const keyData = new Uint8Array(atob(pem.replace(replace, "")).split("").map((c) => c.charCodeAt(0))); + const isPublic = keyFormat === "spki"; + switch (alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { name: "RSA-PSS", hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}` + }; + keyUsages = isPublic ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"]; + break; + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + const namedCurve = getNamedCurve2(keyData); + algorithm = namedCurve.startsWith("P-") ? { name: "ECDH", namedCurve } : { name: namedCurve }; + keyUsages = isPublic ? [] : ["deriveBits"]; + break; + } + case "EdDSA": + algorithm = { name: getNamedCurve2(keyData) }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value'); + } + try { + return await webcrypto_default.subtle.importKey(keyFormat, keyData, algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + algorithm = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.importKey(keyFormat, keyData, algorithm, (_b = options === null || options === void 0 ? void 0 : options.extractable) !== null && _b !== void 0 ? _b : false, keyUsages); + } + throw err; + } +}; +var fromPKCS8 = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "pkcs8", pem, alg, options); +}; +var fromSPKI = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "spki", pem, alg, options); +}; +function getElement(seq) { + let result = []; + let next = 0; + while (next < seq.length) { + let nextPart = parseElement(seq.subarray(next)); + result.push(nextPart); + next += nextPart.byteLength; + } + return result; +} +function parseElement(bytes) { + let position = 0; + let tag = bytes[0] & 31; + position++; + if (tag === 31) { + tag = 0; + while (bytes[position] >= 128) { + tag = tag * 128 + bytes[position] - 128; + position++; + } + tag = tag * 128 + bytes[position] - 128; + position++; + } + let length = 0; + if (bytes[position] < 128) { + length = bytes[position]; + position++; + } else if (length === 128) { + length = 0; + while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) { + if (length > bytes.byteLength) { + throw new TypeError("invalid indefinite form length"); + } + length++; + } + const byteLength2 = position + length + 2; + return { + byteLength: byteLength2, + contents: bytes.subarray(position, position + length), + raw: bytes.subarray(0, byteLength2) + }; + } else { + let numberOfDigits = bytes[position] & 127; + position++; + length = 0; + for (let i = 0; i < numberOfDigits; i++) { + length = length * 256 + bytes[position]; + position++; + } + } + const byteLength = position + length; + return { + byteLength, + contents: bytes.subarray(position, byteLength), + raw: bytes.subarray(0, byteLength) + }; +} +function spkiFromX509(buf) { + const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents); + return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw); +} +function getSPKI(x509) { + const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""); + const raw = decodeBase64(pem); + return format_pem_default(spkiFromX509(raw), "PUBLIC KEY"); +} +var fromX509 = (pem, alg, options) => { + let spki; + try { + spki = getSPKI(pem); + } catch (cause) { + throw new TypeError("failed to parse the X.509 certificate", { cause }); + } + return fromSPKI(spki, alg, options); +}; + +// dist/browser/runtime/jwk_to_key.js +function subtleMapping(jwk) { + let algorithm; + let keyUsages; + switch (jwk.kty) { + case "oct": { + switch (jwk.alg) { + case "HS256": + case "HS384": + case "HS512": + algorithm = { name: "HMAC", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = ["sign", "verify"]; + break; + case "A128CBC-HS256": + case "A192CBC-HS384": + case "A256CBC-HS512": + throw new JOSENotSupported(`${jwk.alg} keys cannot be imported as CryptoKey instances`); + case "A128GCM": + case "A192GCM": + case "A256GCM": + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": + algorithm = { name: "AES-GCM" }; + keyUsages = ["encrypt", "decrypt"]; + break; + case "A128KW": + case "A192KW": + case "A256KW": + algorithm = { name: "AES-KW" }; + keyUsages = ["wrapKey", "unwrapKey"]; + break; + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": + algorithm = { name: "PBKDF2" }; + keyUsages = ["deriveBits"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "RSA": { + switch (jwk.alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { name: "RSA-PSS", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}` + }; + keyUsages = jwk.d ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "EC": { + switch (jwk.alg) { + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": + algorithm = { name: "ECDH", namedCurve: jwk.crv }; + keyUsages = jwk.d ? ["deriveBits"] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "OKP": { + switch (jwk.alg) { + case "EdDSA": + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ["deriveBits"] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); + } + return { algorithm, keyUsages }; +} +var parse = async (jwk) => { + var _a, _b; + if (!jwk.alg) { + throw new TypeError('"alg" argument is required when "jwk.alg" is not present'); + } + const { algorithm, keyUsages } = subtleMapping(jwk); + const rest = [ + algorithm, + (_a = jwk.ext) !== null && _a !== void 0 ? _a : false, + (_b = jwk.key_ops) !== null && _b !== void 0 ? _b : keyUsages + ]; + if (algorithm.name === "PBKDF2") { + return webcrypto_default.subtle.importKey("raw", decode(jwk.k), ...rest); + } + const keyData = { ...jwk }; + delete keyData.alg; + delete keyData.use; + try { + return await webcrypto_default.subtle.importKey("jwk", keyData, ...rest); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + rest[0] = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.importKey("jwk", keyData, ...rest); + } + throw err; + } +}; +var jwk_to_key_default = parse; + +// dist/browser/key/import.js +async function importSPKI(spki, alg, options) { + if (typeof spki !== "string" || spki.indexOf("-----BEGIN PUBLIC KEY-----") !== 0) { + throw new TypeError('"spki" must be SPKI formatted string'); + } + return fromSPKI(spki, alg, options); +} +async function importX509(x509, alg, options) { + if (typeof x509 !== "string" || x509.indexOf("-----BEGIN CERTIFICATE-----") !== 0) { + throw new TypeError('"x509" must be X.509 formatted string'); + } + return fromX509(x509, alg, options); +} +async function importPKCS8(pkcs8, alg, options) { + if (typeof pkcs8 !== "string" || pkcs8.indexOf("-----BEGIN PRIVATE KEY-----") !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string'); + } + return fromPKCS8(pkcs8, alg, options); +} +async function importJWK(jwk, alg, octAsKeyObject) { + var _a; + if (!isObject(jwk)) { + throw new TypeError("JWK must be an object"); + } + alg || (alg = jwk.alg); + switch (jwk.kty) { + case "oct": + if (typeof jwk.k !== "string" || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value'); + } + octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : octAsKeyObject = jwk.ext !== true; + if (octAsKeyObject) { + return jwk_to_key_default({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false }); + } + return decode(jwk.k); + case "RSA": + if (jwk.oth !== void 0) { + throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported'); + } + case "EC": + case "OKP": + return jwk_to_key_default({ ...jwk, alg }); + default: + throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value'); + } +} + +// dist/browser/lib/check_key_type.js +var symmetricTypeCheck = (alg, key) => { + if (key instanceof Uint8Array) + return; + if (!is_key_like_default(key)) { + throw new TypeError(withAlg(alg, key, ...types, "Uint8Array")); + } + if (key.type !== "secret") { + throw new TypeError(`${types.join(" or ")} instances for symmetric algorithms must be of type "secret"`); + } +}; +var asymmetricTypeCheck = (alg, key, usage) => { + if (!is_key_like_default(key)) { + throw new TypeError(withAlg(alg, key, ...types)); + } + if (key.type === "secret") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithms must not be of type "secret"`); + } + if (usage === "sign" && key.type === "public") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm signing must be of type "private"`); + } + if (usage === "decrypt" && key.type === "public") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm decryption must be of type "private"`); + } + if (key.algorithm && usage === "verify" && key.type === "private") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm verifying must be of type "public"`); + } + if (key.algorithm && usage === "encrypt" && key.type === "private") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm encryption must be of type "public"`); + } +}; +var checkKeyType = (alg, key, usage) => { + const symmetric = alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A\d{3}(?:GCM)?KW$/.test(alg); + if (symmetric) { + symmetricTypeCheck(alg, key); + } else { + asymmetricTypeCheck(alg, key, usage); + } +}; +var check_key_type_default = checkKeyType; + +// dist/browser/runtime/encrypt.js +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 webcrypto_default.subtle.importKey("raw", cek.subarray(keySize >> 3), "AES-CBC", false, ["encrypt"]); + const macKey = await webcrypto_default.subtle.importKey("raw", cek.subarray(0, keySize >> 3), { + hash: `SHA-${keySize << 1}`, + name: "HMAC" + }, false, ["sign"]); + const ciphertext = new Uint8Array(await webcrypto_default.subtle.encrypt({ + iv, + name: "AES-CBC" + }, encKey, plaintext)); + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const tag = new Uint8Array((await webcrypto_default.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)); + return { ciphertext, tag }; +} +async function gcmEncrypt(enc, plaintext, cek, iv, aad) { + let encKey; + if (cek instanceof Uint8Array) { + encKey = await webcrypto_default.subtle.importKey("raw", cek, "AES-GCM", false, ["encrypt"]); + } else { + checkEncCryptoKey(cek, enc, "encrypt"); + encKey = cek; + } + const encrypted = new Uint8Array(await webcrypto_default.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 }; +} +var encrypt3 = async (enc, plaintext, cek, iv, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalid_key_input_default(cek, ...types, "Uint8Array")); + } + check_iv_length_default(enc, iv); + 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"); + } +}; +var encrypt_default = encrypt3; + +// dist/browser/lib/aesgcmkw.js +async function wrap2(alg, key, cek, iv) { + const jweAlgorithm = alg.slice(0, 7); + iv || (iv = iv_default(jweAlgorithm)); + const { ciphertext: encryptedKey, tag } = await encrypt_default(jweAlgorithm, cek, key, iv, new Uint8Array(0)); + return { encryptedKey, iv: encode(iv), tag: encode(tag) }; +} +async function unwrap2(alg, key, encryptedKey, iv, tag) { + const jweAlgorithm = alg.slice(0, 7); + return decrypt_default(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)); +} + +// dist/browser/lib/decrypt_key_management.js +async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) { + check_key_type_default(alg, key, "decrypt"); + switch (alg) { + case "dir": { + if (encryptedKey !== void 0) + throw new JWEInvalid("Encountered unexpected JWE Encrypted Key"); + return key; + } + case "ECDH-ES": + if (encryptedKey !== void 0) + throw new JWEInvalid("Encountered unexpected JWE Encrypted Key"); + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + if (!isObject(joseHeader.epk)) + throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`); + if (!ecdhAllowed(key)) + throw new JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime"); + const epk = await importJWK(joseHeader.epk, alg); + let partyUInfo; + let partyVInfo; + if (joseHeader.apu !== void 0) { + if (typeof joseHeader.apu !== "string") + throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`); + partyUInfo = decode(joseHeader.apu); + } + if (joseHeader.apv !== void 0) { + if (typeof joseHeader.apv !== "string") + throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`); + partyVInfo = decode(joseHeader.apv); + } + const sharedSecret = await deriveKey(epk, key, alg === "ECDH-ES" ? joseHeader.enc : alg, alg === "ECDH-ES" ? bitLength2(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo); + if (alg === "ECDH-ES") + return sharedSecret; + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return unwrap(alg.slice(-6), sharedSecret, encryptedKey); + } + case "RSA1_5": + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return decrypt3(alg, key, encryptedKey); + } + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + if (typeof joseHeader.p2c !== "number") + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`); + const p2cLimit = (options === null || options === void 0 ? void 0 : options.maxPBES2Count) || 1e4; + if (joseHeader.p2c > p2cLimit) + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`); + if (typeof joseHeader.p2s !== "string") + throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`); + return decrypt2(alg, key, encryptedKey, joseHeader.p2c, decode(joseHeader.p2s)); + } + case "A128KW": + case "A192KW": + case "A256KW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return unwrap(alg, key, encryptedKey); + } + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + if (typeof joseHeader.iv !== "string") + throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`); + if (typeof joseHeader.tag !== "string") + throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`); + const iv = decode(joseHeader.iv); + const tag = decode(joseHeader.tag); + return unwrap2(alg, key, encryptedKey, iv, tag); + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } +} +var decrypt_key_management_default = decryptKeyManagement; + +// dist/browser/lib/validate_crit.js +function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { + if (joseHeader.crit !== void 0 && protectedHeader.crit === void 0) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); + } + if (!protectedHeader || protectedHeader.crit === void 0) { + return /* @__PURE__ */ new Set(); + } + if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input !== "string" || input.length === 0)) { + throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); + } + let recognized; + if (recognizedOption !== void 0) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); + } else { + recognized = recognizedDefault; + } + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); + } + if (joseHeader[parameter] === void 0) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`); + } else if (recognized.get(parameter) && protectedHeader[parameter] === void 0) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); + } + } + return new Set(protectedHeader.crit); +} +var validate_crit_default = validateCrit; + +// dist/browser/lib/validate_algorithms.js +var validateAlgorithms = (option, algorithms) => { + if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) { + throw new TypeError(`"${option}" option must be an array of strings`); + } + if (!algorithms) { + return void 0; + } + return new Set(algorithms); +}; +var validate_algorithms_default = validateAlgorithms; + +// dist/browser/jwe/flattened/decrypt.js +async function flattenedDecrypt(jwe, key, options) { + var _a; + if (!isObject(jwe)) { + throw new JWEInvalid("Flattened JWE must be an object"); + } + if (jwe.protected === void 0 && jwe.header === void 0 && jwe.unprotected === void 0) { + throw new JWEInvalid("JOSE Header missing"); + } + if (typeof jwe.iv !== "string") { + throw new JWEInvalid("JWE Initialization Vector missing or incorrect type"); + } + if (typeof jwe.ciphertext !== "string") { + throw new JWEInvalid("JWE Ciphertext missing or incorrect type"); + } + if (typeof jwe.tag !== "string") { + throw new JWEInvalid("JWE Authentication Tag missing or incorrect type"); + } + if (jwe.protected !== void 0 && typeof jwe.protected !== "string") { + throw new JWEInvalid("JWE Protected Header incorrect type"); + } + if (jwe.encrypted_key !== void 0 && typeof jwe.encrypted_key !== "string") { + throw new JWEInvalid("JWE Encrypted Key incorrect type"); + } + if (jwe.aad !== void 0 && typeof jwe.aad !== "string") { + throw new JWEInvalid("JWE AAD incorrect type"); + } + if (jwe.header !== void 0 && !isObject(jwe.header)) { + throw new JWEInvalid("JWE Shared Unprotected Header incorrect type"); + } + if (jwe.unprotected !== void 0 && !isObject(jwe.unprotected)) { + throw new JWEInvalid("JWE Per-Recipient Unprotected Header incorrect type"); + } + let parsedProt; + if (jwe.protected) { + try { + const protectedHeader2 = decode(jwe.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader2)); + } catch (_b) { + throw new JWEInvalid("JWE Protected Header is invalid"); + } + } + if (!is_disjoint_default(parsedProt, jwe.header, jwe.unprotected)) { + throw new JWEInvalid("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected + }; + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + if (joseHeader.zip !== void 0) { + if (!parsedProt || !parsedProt.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== "DEF") { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid("missing JWE Algorithm (alg) in JWE Header"); + } + if (typeof enc !== "string" || !enc) { + throw new JWEInvalid("missing JWE Encryption Algorithm (enc) in JWE Header"); + } + const keyManagementAlgorithms = options && validate_algorithms_default("keyManagementAlgorithms", options.keyManagementAlgorithms); + const contentEncryptionAlgorithms = options && validate_algorithms_default("contentEncryptionAlgorithms", options.contentEncryptionAlgorithms); + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed'); + } + let encryptedKey; + if (jwe.encrypted_key !== void 0) { + encryptedKey = decode(jwe.encrypted_key); + } + let resolvedKey = false; + if (typeof key === "function") { + key = await key(parsedProt, jwe); + resolvedKey = true; + } + let cek; + try { + cek = await decrypt_key_management_default(alg, key, encryptedKey, joseHeader, options); + } catch (err) { + if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { + throw err; + } + cek = cek_default(enc); + } + const iv = decode(jwe.iv); + const tag = decode(jwe.tag); + const protectedHeader = encoder.encode((_a = jwe.protected) !== null && _a !== void 0 ? _a : ""); + let additionalData; + if (jwe.aad !== void 0) { + additionalData = concat(protectedHeader, encoder.encode("."), encoder.encode(jwe.aad)); + } else { + additionalData = protectedHeader; + } + let plaintext = await decrypt_default(enc, cek, decode(jwe.ciphertext), iv, tag, additionalData); + if (joseHeader.zip === "DEF") { + plaintext = await ((options === null || options === void 0 ? void 0 : options.inflateRaw) || inflate)(plaintext); + } + const result = { plaintext }; + if (jwe.protected !== void 0) { + result.protectedHeader = parsedProt; + } + if (jwe.aad !== void 0) { + result.additionalAuthenticatedData = decode(jwe.aad); + } + if (jwe.unprotected !== void 0) { + result.sharedUnprotectedHeader = jwe.unprotected; + } + if (jwe.header !== void 0) { + result.unprotectedHeader = jwe.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} + +// dist/browser/jwe/compact/decrypt.js +async function compactDecrypt(jwe, key, options) { + if (jwe instanceof Uint8Array) { + jwe = decoder.decode(jwe); + } + if (typeof jwe !== "string") { + throw new JWEInvalid("Compact JWE must be a string or Uint8Array"); + } + const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split("."); + if (length !== 5) { + throw new JWEInvalid("Invalid Compact JWE"); + } + const decrypted = await flattenedDecrypt({ + ciphertext, + iv: iv || void 0, + protected: protectedHeader || void 0, + tag: tag || void 0, + encrypted_key: encryptedKey || void 0 + }, key, options); + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: decrypted.key }; + } + return result; +} + +// dist/browser/jwe/general/decrypt.js +async function generalDecrypt(jwe, key, options) { + if (!isObject(jwe)) { + throw new JWEInvalid("General JWE must be an object"); + } + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { + throw new JWEInvalid("JWE Recipients missing or incorrect type"); + } + if (!jwe.recipients.length) { + throw new JWEInvalid("JWE Recipients has no members"); + } + for (const recipient of jwe.recipients) { + try { + return await flattenedDecrypt({ + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected + }, key, options); + } catch (_a) { + } + } + throw new JWEDecryptionFailed(); +} + +// dist/browser/runtime/key_to_jwk.js +var keyToJWK = async (key) => { + if (key instanceof Uint8Array) { + return { + kty: "oct", + k: encode(key) + }; + } + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); + } + if (!key.extractable) { + throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK"); + } + const { ext, key_ops, alg, use, ...jwk } = await webcrypto_default.subtle.exportKey("jwk", key); + return jwk; +}; +var key_to_jwk_default = keyToJWK; + +// dist/browser/key/export.js +async function exportSPKI(key) { + return toSPKI(key); +} +async function exportPKCS8(key) { + return toPKCS8(key); +} +async function exportJWK(key) { + return key_to_jwk_default(key); +} + +// dist/browser/lib/encrypt_key_management.js +async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) { + let encryptedKey; + let parameters; + let cek; + check_key_type_default(alg, key, "encrypt"); + switch (alg) { + case "dir": { + cek = key; + break; + } + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + if (!ecdhAllowed(key)) { + throw new JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime"); + } + const { apu, apv } = providedParameters; + let { epk: ephemeralKey } = providedParameters; + ephemeralKey || (ephemeralKey = (await generateEpk(key)).privateKey); + const { x, y, crv, kty } = await exportJWK(ephemeralKey); + const sharedSecret = await deriveKey(key, ephemeralKey, alg === "ECDH-ES" ? enc : alg, alg === "ECDH-ES" ? bitLength2(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv); + parameters = { epk: { x, crv, kty } }; + if (kty === "EC") + parameters.epk.y = y; + if (apu) + parameters.apu = encode(apu); + if (apv) + parameters.apv = encode(apv); + if (alg === "ECDH-ES") { + cek = sharedSecret; + break; + } + cek = providedCek || cek_default(enc); + const kwAlg = alg.slice(-6); + encryptedKey = await wrap(kwAlg, sharedSecret, cek); + break; + } + case "RSA1_5": + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": { + cek = providedCek || cek_default(enc); + encryptedKey = await encrypt2(alg, key, cek); + break; + } + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": { + cek = providedCek || cek_default(enc); + const { p2c, p2s: p2s2 } = providedParameters; + ({ encryptedKey, ...parameters } = await encrypt(alg, key, cek, p2c, p2s2)); + break; + } + case "A128KW": + case "A192KW": + case "A256KW": { + cek = providedCek || cek_default(enc); + encryptedKey = await wrap(alg, key, cek); + break; + } + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": { + cek = providedCek || cek_default(enc); + const { iv } = providedParameters; + ({ encryptedKey, ...parameters } = await wrap2(alg, key, cek, iv)); + break; + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + return { cek, encryptedKey, parameters }; +} +var encrypt_key_management_default = encryptKeyManagement; + +// dist/browser/jwe/flattened/encrypt.js +var unprotected = Symbol(); +var FlattenedEncrypt = class { + constructor(plaintext) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError("plaintext must be an instance of Uint8Array"); + } + this._plaintext = plaintext; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError("setKeyManagementParameters can only be called once"); + } + this._keyManagementParameters = parameters; + return this; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._sharedUnprotectedHeader) { + throw new TypeError("setSharedUnprotectedHeader can only be called once"); + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError("setContentEncryptionKey can only be called once"); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError("setInitializationVector can only be called once"); + } + this._iv = iv; + return this; + } + async encrypt(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new JWEInvalid("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()"); + } + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) { + throw new JWEInvalid("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader + }; + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== void 0) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== "DEF") { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (typeof enc !== "string" || !enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + let encryptedKey; + if (alg === "dir") { + if (this._cek) { + throw new TypeError("setContentEncryptionKey cannot be called when using Direct Encryption"); + } + } else if (alg === "ECDH-ES") { + if (this._cek) { + throw new TypeError("setContentEncryptionKey cannot be called when using Direct Key Agreement"); + } + } + let cek; + { + let parameters; + ({ cek, encryptedKey, parameters } = await encrypt_key_management_default(alg, enc, key, this._cek, this._keyManagementParameters)); + if (parameters) { + if (options && unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters); + } else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters }; + } + } else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters); + } else { + this._protectedHeader = { ...this._protectedHeader, ...parameters }; + } + } + } + } + this._iv || (this._iv = iv_default(enc)); + let additionalData; + let protectedHeader; + let aadMember; + if (this._protectedHeader) { + protectedHeader = encoder.encode(encode(JSON.stringify(this._protectedHeader))); + } else { + protectedHeader = encoder.encode(""); + } + if (this._aad) { + aadMember = encode(this._aad); + additionalData = concat(protectedHeader, encoder.encode("."), encoder.encode(aadMember)); + } else { + additionalData = protectedHeader; + } + let ciphertext; + let tag; + if (joseHeader.zip === "DEF") { + const deflated = await ((options === null || options === void 0 ? void 0 : options.deflateRaw) || deflate)(this._plaintext); + ({ ciphertext, tag } = await encrypt_default(enc, deflated, cek, this._iv, additionalData)); + } else { + ; + ({ ciphertext, tag } = await encrypt_default(enc, this._plaintext, cek, this._iv, additionalData)); + } + const jwe = { + ciphertext: encode(ciphertext), + iv: encode(this._iv), + tag: encode(tag) + }; + if (encryptedKey) { + jwe.encrypted_key = encode(encryptedKey); + } + if (aadMember) { + jwe.aad = aadMember; + } + if (this._protectedHeader) { + jwe.protected = decoder.decode(protectedHeader); + } + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader; + } + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader; + } + return jwe; + } +}; + +// dist/browser/jwe/general/encrypt.js +var IndividualRecipient = class { + constructor(enc, key, options) { + this.parent = enc; + this.key = key; + this.options = options; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addRecipient(...args) { + return this.parent.addRecipient(...args); + } + encrypt(...args) { + return this.parent.encrypt(...args); + } + done() { + return this.parent; + } +}; +var GeneralEncrypt = class { + constructor(plaintext) { + this._recipients = []; + this._plaintext = plaintext; + } + addRecipient(key, options) { + const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit }); + this._recipients.push(recipient); + return recipient; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setSharedUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = sharedUnprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + async encrypt(options) { + var _a, _b, _c; + if (!this._recipients.length) { + throw new JWEInvalid("at least one recipient must be added"); + } + options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw }; + if (this._recipients.length === 1) { + const [recipient] = this._recipients; + const flattened = await new FlattenedEncrypt(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(recipient.unprotectedHeader).encrypt(recipient.key, { ...recipient.options, ...options }); + let jwe2 = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag + }; + if (flattened.aad) + jwe2.aad = flattened.aad; + if (flattened.protected) + jwe2.protected = flattened.protected; + if (flattened.unprotected) + jwe2.unprotected = flattened.unprotected; + if (flattened.encrypted_key) + jwe2.recipients[0].encrypted_key = flattened.encrypted_key; + if (flattened.header) + jwe2.recipients[0].header = flattened.header; + return jwe2; + } + let enc; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) { + throw new JWEInvalid("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader + }; + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (alg === "dir" || alg === "ECDH-ES") { + throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient'); + } + if (typeof joseHeader.enc !== "string" || !joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + if (!enc) { + enc = joseHeader.enc; + } else if (enc !== joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients'); + } + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), recipient.options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== void 0) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + } + } + const cek = cek_default(enc); + let jwe = { + ciphertext: "", + iv: "", + recipients: [], + tag: "" + }; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + const target = {}; + jwe.recipients.push(target); + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader + }; + const p2c = joseHeader.alg.startsWith("PBES2") ? 2048 + i : void 0; + if (i === 0) { + const flattened = await new FlattenedEncrypt(this._plaintext).setAdditionalAuthenticatedData(this._aad).setContentEncryptionKey(cek).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(recipient.unprotectedHeader).setKeyManagementParameters({ p2c }).encrypt(recipient.key, { + ...recipient.options, + ...options, + [unprotected]: true + }); + jwe.ciphertext = flattened.ciphertext; + jwe.iv = flattened.iv; + jwe.tag = flattened.tag; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + target.encrypted_key = flattened.encrypted_key; + if (flattened.header) + target.header = flattened.header; + continue; + } + const { encryptedKey, parameters } = await encrypt_key_management_default(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) || ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c }); + target.encrypted_key = encode(encryptedKey); + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters }; + } + return jwe; + } +}; + +// dist/browser/runtime/subtle_dsa.js +function subtleDsa(alg, algorithm) { + const hash = `SHA-${alg.slice(-3)}`; + switch (alg) { + case "HS256": + case "HS384": + case "HS512": + return { hash, name: "HMAC" }; + case "PS256": + case "PS384": + case "PS512": + return { hash, name: "RSA-PSS", saltLength: alg.slice(-3) >> 3 }; + case "RS256": + case "RS384": + case "RS512": + return { hash, name: "RSASSA-PKCS1-v1_5" }; + case "ES256": + case "ES384": + case "ES512": + return { hash, name: "ECDSA", namedCurve: algorithm.namedCurve }; + case "EdDSA": + if (isCloudflareWorkers() && algorithm.name === "NODE-ED25519") { + return { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + } + return { name: algorithm.name }; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} + +// dist/browser/runtime/get_sign_verify_key.js +function getCryptoKey3(alg, key, usage) { + if (isCryptoKey(key)) { + checkSigCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + if (!alg.startsWith("HS")) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return webcrypto_default.subtle.importKey("raw", key, { hash: `SHA-${alg.slice(-3)}`, name: "HMAC" }, false, [usage]); + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); +} + +// dist/browser/runtime/verify.js +var verify = async (alg, key, signature, data) => { + const cryptoKey = await getCryptoKey3(alg, key, "verify"); + check_key_length_default(alg, cryptoKey); + const algorithm = subtleDsa(alg, cryptoKey.algorithm); + try { + return await webcrypto_default.subtle.verify(algorithm, cryptoKey, signature, data); + } catch (_a) { + return false; + } +}; +var verify_default = verify; + +// dist/browser/jws/flattened/verify.js +async function flattenedVerify(jws, key, options) { + var _a; + if (!isObject(jws)) { + throw new JWSInvalid("Flattened JWS must be an object"); + } + if (jws.protected === void 0 && jws.header === void 0) { + throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); + } + if (jws.protected !== void 0 && typeof jws.protected !== "string") { + throw new JWSInvalid("JWS Protected Header incorrect type"); + } + if (jws.payload === void 0) { + throw new JWSInvalid("JWS Payload missing"); + } + if (typeof jws.signature !== "string") { + throw new JWSInvalid("JWS Signature missing or incorrect type"); + } + if (jws.header !== void 0 && !isObject(jws.header)) { + throw new JWSInvalid("JWS Unprotected Header incorrect type"); + } + let parsedProt = {}; + if (jws.protected) { + try { + const protectedHeader = decode(jws.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } catch (_b) { + throw new JWSInvalid("JWS Protected Header is invalid"); + } + } + if (!is_disjoint_default(parsedProt, jws.header)) { + throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...parsedProt, + ...jws.header + }; + const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + let b64 = true; + if (extensions.has("b64")) { + b64 = parsedProt.b64; + if (typeof b64 !== "boolean") { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + const algorithms = options && validate_algorithms_default("algorithms", options.algorithms); + if (algorithms && !algorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (b64) { + if (typeof jws.payload !== "string") { + throw new JWSInvalid("JWS Payload must be a string"); + } + } else if (typeof jws.payload !== "string" && !(jws.payload instanceof Uint8Array)) { + throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance"); + } + let resolvedKey = false; + if (typeof key === "function") { + key = await key(parsedProt, jws); + resolvedKey = true; + } + check_key_type_default(alg, key, "verify"); + const data = concat(encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ""), encoder.encode("."), typeof jws.payload === "string" ? encoder.encode(jws.payload) : jws.payload); + const signature = decode(jws.signature); + const verified = await verify_default(alg, key, signature, data); + if (!verified) { + throw new JWSSignatureVerificationFailed(); + } + let payload; + if (b64) { + payload = decode(jws.payload); + } else if (typeof jws.payload === "string") { + payload = encoder.encode(jws.payload); + } else { + payload = jws.payload; + } + const result = { payload }; + if (jws.protected !== void 0) { + result.protectedHeader = parsedProt; + } + if (jws.header !== void 0) { + result.unprotectedHeader = jws.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} + +// dist/browser/jws/compact/verify.js +async function compactVerify(jws, key, options) { + if (jws instanceof Uint8Array) { + jws = decoder.decode(jws); + } + if (typeof jws !== "string") { + throw new JWSInvalid("Compact JWS must be a string or Uint8Array"); + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split("."); + if (length !== 3) { + throw new JWSInvalid("Invalid Compact JWS"); + } + const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options); + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: verified.key }; + } + return result; +} + +// dist/browser/jws/general/verify.js +async function generalVerify(jws, key, options) { + if (!isObject(jws)) { + throw new JWSInvalid("General JWS must be an object"); + } + if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { + throw new JWSInvalid("JWS Signatures missing or incorrect type"); + } + for (const signature of jws.signatures) { + try { + return await flattenedVerify({ + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature + }, key, options); + } catch (_a) { + } + } + throw new JWSSignatureVerificationFailed(); +} + +// dist/browser/lib/epoch.js +var epoch_default = (date) => Math.floor(date.getTime() / 1e3); + +// dist/browser/lib/secs.js +var minute = 60; +var hour = minute * 60; +var day = hour * 24; +var week = day * 7; +var year = day * 365.25; +var REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i; +var secs_default = (str) => { + const matched = REGEX.exec(str); + if (!matched) { + throw new TypeError("Invalid time period format"); + } + const value = parseFloat(matched[1]); + const unit = matched[2].toLowerCase(); + switch (unit) { + case "sec": + case "secs": + case "second": + case "seconds": + case "s": + return Math.round(value); + case "minute": + case "minutes": + case "min": + case "mins": + case "m": + return Math.round(value * minute); + case "hour": + case "hours": + case "hr": + case "hrs": + case "h": + return Math.round(value * hour); + case "day": + case "days": + case "d": + return Math.round(value * day); + case "week": + case "weeks": + case "w": + return Math.round(value * week); + default: + return Math.round(value * year); + } +}; + +// dist/browser/lib/jwt_claims_set.js +var normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, ""); +var checkAudiencePresence = (audPayload, audOption) => { + if (typeof audPayload === "string") { + return audOption.includes(audPayload); + } + if (Array.isArray(audPayload)) { + return audOption.some(Set.prototype.has.bind(new Set(audPayload))); + } + return false; +}; +var jwt_claims_set_default = (protectedHeader, encodedPayload, options = {}) => { + const { typ } = options; + if (typ && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) { + throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', "typ", "check_failed"); + } + let payload; + try { + payload = JSON.parse(decoder.decode(encodedPayload)); + } catch (_a) { + } + if (!isObject(payload)) { + throw new JWTInvalid("JWT Claims Set must be a top-level JSON object"); + } + const { issuer } = options; + if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) { + throw new JWTClaimValidationFailed('unexpected "iss" claim value', "iss", "check_failed"); + } + const { subject } = options; + if (subject && payload.sub !== subject) { + throw new JWTClaimValidationFailed('unexpected "sub" claim value', "sub", "check_failed"); + } + const { audience } = options; + if (audience && !checkAudiencePresence(payload.aud, typeof audience === "string" ? [audience] : audience)) { + throw new JWTClaimValidationFailed('unexpected "aud" claim value', "aud", "check_failed"); + } + let tolerance; + switch (typeof options.clockTolerance) { + case "string": + tolerance = secs_default(options.clockTolerance); + break; + case "number": + tolerance = options.clockTolerance; + break; + case "undefined": + tolerance = 0; + break; + default: + throw new TypeError("Invalid clockTolerance option type"); + } + const { currentDate } = options; + const now = epoch_default(currentDate || /* @__PURE__ */ new Date()); + if ((payload.iat !== void 0 || options.maxTokenAge) && typeof payload.iat !== "number") { + throw new JWTClaimValidationFailed('"iat" claim must be a number', "iat", "invalid"); + } + if (payload.nbf !== void 0) { + if (typeof payload.nbf !== "number") { + throw new JWTClaimValidationFailed('"nbf" claim must be a number', "nbf", "invalid"); + } + if (payload.nbf > now + tolerance) { + throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', "nbf", "check_failed"); + } + } + if (payload.exp !== void 0) { + if (typeof payload.exp !== "number") { + throw new JWTClaimValidationFailed('"exp" claim must be a number', "exp", "invalid"); + } + if (payload.exp <= now - tolerance) { + throw new JWTExpired('"exp" claim timestamp check failed', "exp", "check_failed"); + } + } + if (options.maxTokenAge) { + const age = now - payload.iat; + const max = typeof options.maxTokenAge === "number" ? options.maxTokenAge : secs_default(options.maxTokenAge); + if (age - tolerance > max) { + throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', "iat", "check_failed"); + } + if (age < 0 - tolerance) { + throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', "iat", "check_failed"); + } + } + return payload; +}; + +// dist/browser/jwt/verify.js +async function jwtVerify(jwt, key, options) { + var _a; + const verified = await compactVerify(jwt, key, options); + if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes("b64")) && verified.protectedHeader.b64 === false) { + throw new JWTInvalid("JWTs MUST NOT use unencoded payload"); + } + const payload = jwt_claims_set_default(verified.protectedHeader, verified.payload, options); + const result = { payload, protectedHeader: verified.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: verified.key }; + } + return result; +} + +// dist/browser/jwt/decrypt.js +async function jwtDecrypt(jwt, key, options) { + const decrypted = await compactDecrypt(jwt, key, options); + const payload = jwt_claims_set_default(decrypted.protectedHeader, decrypted.plaintext, options); + const { protectedHeader } = decrypted; + if (protectedHeader.iss !== void 0 && protectedHeader.iss !== payload.iss) { + throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', "iss", "mismatch"); + } + if (protectedHeader.sub !== void 0 && protectedHeader.sub !== payload.sub) { + throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', "sub", "mismatch"); + } + if (protectedHeader.aud !== void 0 && JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { + throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', "aud", "mismatch"); + } + const result = { payload, protectedHeader }; + if (typeof key === "function") { + return { ...result, key: decrypted.key }; + } + return result; +} + +// dist/browser/jwe/compact/encrypt.js +var CompactEncrypt = class { + constructor(plaintext) { + this._flattened = new FlattenedEncrypt(plaintext); + } + setContentEncryptionKey(cek) { + this._flattened.setContentEncryptionKey(cek); + return this; + } + setInitializationVector(iv) { + this._flattened.setInitializationVector(iv); + return this; + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + setKeyManagementParameters(parameters) { + this._flattened.setKeyManagementParameters(parameters); + return this; + } + async encrypt(key, options) { + const jwe = await this._flattened.encrypt(key, options); + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join("."); + } +}; + +// dist/browser/runtime/sign.js +var sign = async (alg, key, data) => { + const cryptoKey = await getCryptoKey3(alg, key, "sign"); + check_key_length_default(alg, cryptoKey); + const signature = await webcrypto_default.subtle.sign(subtleDsa(alg, cryptoKey.algorithm), cryptoKey, data); + return new Uint8Array(signature); +}; +var sign_default = sign; + +// dist/browser/jws/flattened/sign.js +var FlattenedSign = class { + constructor(payload) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError("payload must be an instance of Uint8Array"); + } + this._payload = payload; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + async sign(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new JWSInvalid("either setProtectedHeader or setUnprotectedHeader must be called before #sign()"); + } + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader)) { + throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader + }; + const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + let b64 = true; + if (extensions.has("b64")) { + b64 = this._protectedHeader.b64; + if (typeof b64 !== "boolean") { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + check_key_type_default(alg, key, "sign"); + let payload = this._payload; + if (b64) { + payload = encoder.encode(encode(payload)); + } + let protectedHeader; + if (this._protectedHeader) { + protectedHeader = encoder.encode(encode(JSON.stringify(this._protectedHeader))); + } else { + protectedHeader = encoder.encode(""); + } + const data = concat(protectedHeader, encoder.encode("."), payload); + const signature = await sign_default(alg, key, data); + const jws = { + signature: encode(signature), + payload: "" + }; + if (b64) { + jws.payload = decoder.decode(payload); + } + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader; + } + if (this._protectedHeader) { + jws.protected = decoder.decode(protectedHeader); + } + return jws; + } +}; + +// dist/browser/jws/compact/sign.js +var CompactSign = class { + constructor(payload) { + this._flattened = new FlattenedSign(payload); + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + async sign(key, options) { + const jws = await this._flattened.sign(key, options); + if (jws.payload === void 0) { + throw new TypeError("use the flattened module for creating JWS with b64: false"); + } + return `${jws.protected}.${jws.payload}.${jws.signature}`; + } +}; + +// dist/browser/jws/general/sign.js +var IndividualSignature = class { + constructor(sig, key, options) { + this.parent = sig; + this.key = key; + this.options = options; + } + setProtectedHeader(protectedHeader) { + if (this.protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this.protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addSignature(...args) { + return this.parent.addSignature(...args); + } + sign(...args) { + return this.parent.sign(...args); + } + done() { + return this.parent; + } +}; +var GeneralSign = class { + constructor(payload) { + this._signatures = []; + this._payload = payload; + } + addSignature(key, options) { + const signature = new IndividualSignature(this, key, options); + this._signatures.push(signature); + return signature; + } + async sign() { + if (!this._signatures.length) { + throw new JWSInvalid("at least one signature must be added"); + } + const jws = { + signatures: [], + payload: "" + }; + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i]; + const flattened = new FlattenedSign(this._payload); + flattened.setProtectedHeader(signature.protectedHeader); + flattened.setUnprotectedHeader(signature.unprotectedHeader); + const { payload, ...rest } = await flattened.sign(signature.key, signature.options); + if (i === 0) { + jws.payload = payload; + } else if (jws.payload !== payload) { + throw new JWSInvalid("inconsistent use of JWS Unencoded Payload (RFC7797)"); + } + jws.signatures.push(rest); + } + return jws; + } +}; + +// dist/browser/jwt/produce.js +var ProduceJWT = class { + constructor(payload) { + if (!isObject(payload)) { + throw new TypeError("JWT Claims Set MUST be an object"); + } + this._payload = payload; + } + setIssuer(issuer) { + this._payload = { ...this._payload, iss: issuer }; + return this; + } + setSubject(subject) { + this._payload = { ...this._payload, sub: subject }; + return this; + } + setAudience(audience) { + this._payload = { ...this._payload, aud: audience }; + return this; + } + setJti(jwtId) { + this._payload = { ...this._payload, jti: jwtId }; + return this; + } + setNotBefore(input) { + if (typeof input === "number") { + this._payload = { ...this._payload, nbf: input }; + } else { + this._payload = { ...this._payload, nbf: epoch_default(/* @__PURE__ */ new Date()) + secs_default(input) }; + } + return this; + } + setExpirationTime(input) { + if (typeof input === "number") { + this._payload = { ...this._payload, exp: input }; + } else { + this._payload = { ...this._payload, exp: epoch_default(/* @__PURE__ */ new Date()) + secs_default(input) }; + } + return this; + } + setIssuedAt(input) { + if (typeof input === "undefined") { + this._payload = { ...this._payload, iat: epoch_default(/* @__PURE__ */ new Date()) }; + } else { + this._payload = { ...this._payload, iat: input }; + } + return this; + } +}; + +// dist/browser/jwt/sign.js +var SignJWT = class extends ProduceJWT { + setProtectedHeader(protectedHeader) { + this._protectedHeader = protectedHeader; + return this; + } + async sign(key, options) { + var _a; + const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload))); + sig.setProtectedHeader(this._protectedHeader); + if (Array.isArray((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.crit) && this._protectedHeader.crit.includes("b64") && this._protectedHeader.b64 === false) { + throw new JWTInvalid("JWTs MUST NOT use unencoded payload"); + } + return sig.sign(key, options); + } +}; + +// dist/browser/jwt/encrypt.js +var EncryptJWT = class extends ProduceJWT { + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError("setKeyManagementParameters can only be called once"); + } + this._keyManagementParameters = parameters; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError("setContentEncryptionKey can only be called once"); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError("setInitializationVector can only be called once"); + } + this._iv = iv; + return this; + } + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true; + return this; + } + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true; + return this; + } + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true; + return this; + } + async encrypt(key, options) { + const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload))); + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss }; + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub }; + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud }; + } + enc.setProtectedHeader(this._protectedHeader); + if (this._iv) { + enc.setInitializationVector(this._iv); + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek); + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters); + } + return enc.encrypt(key, options); + } +}; + +// dist/browser/jwk/thumbprint.js +var check = (value, description) => { + if (typeof value !== "string" || !value) { + throw new JWKInvalid(`${description} missing or invalid`); + } +}; +async function calculateJwkThumbprint(jwk, digestAlgorithm) { + if (!isObject(jwk)) { + throw new TypeError("JWK must be an object"); + } + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : digestAlgorithm = "sha256"; + if (digestAlgorithm !== "sha256" && digestAlgorithm !== "sha384" && digestAlgorithm !== "sha512") { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"'); + } + let components; + switch (jwk.kty) { + case "EC": + check(jwk.crv, '"crv" (Curve) Parameter'); + check(jwk.x, '"x" (X Coordinate) Parameter'); + check(jwk.y, '"y" (Y Coordinate) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; + break; + case "OKP": + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'); + check(jwk.x, '"x" (Public Key) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; + break; + case "RSA": + check(jwk.e, '"e" (Exponent) Parameter'); + check(jwk.n, '"n" (Modulus) Parameter'); + components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; + break; + case "oct": + check(jwk.k, '"k" (Key Value) Parameter'); + components = { k: jwk.k, kty: jwk.kty }; + break; + default: + throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported'); + } + const data = encoder.encode(JSON.stringify(components)); + return encode(await digest_default(digestAlgorithm, data)); +} +async function calculateJwkThumbprintUri(jwk, digestAlgorithm) { + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : digestAlgorithm = "sha256"; + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm); + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`; +} + +// dist/browser/jwk/embedded.js +async function EmbeddedJWK(protectedHeader, token) { + const joseHeader = { + ...protectedHeader, + ...token === null || token === void 0 ? void 0 : token.header + }; + if (!isObject(joseHeader.jwk)) { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object'); + } + const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg, true); + if (key instanceof Uint8Array || key.type !== "public") { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key'); + } + return key; +} + +// dist/browser/jwks/local.js +function getKtyFromAlg(alg) { + switch (typeof alg === "string" && alg.slice(0, 2)) { + case "RS": + case "PS": + return "RSA"; + case "ES": + return "EC"; + case "Ed": + return "OKP"; + default: + throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set'); + } +} +function isJWKSLike(jwks) { + return jwks && typeof jwks === "object" && Array.isArray(jwks.keys) && jwks.keys.every(isJWKLike); +} +function isJWKLike(key) { + return isObject(key); +} +function clone(obj) { + if (typeof structuredClone === "function") { + return structuredClone(obj); + } + return JSON.parse(JSON.stringify(obj)); +} +var LocalJWKSet = class { + constructor(jwks) { + this._cached = /* @__PURE__ */ new WeakMap(); + if (!isJWKSLike(jwks)) { + throw new JWKSInvalid("JSON Web Key Set malformed"); + } + this._jwks = clone(jwks); + } + async getKey(protectedHeader, token) { + const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header }; + const kty = getKtyFromAlg(alg); + const candidates = this._jwks.keys.filter((jwk2) => { + let candidate = kty === jwk2.kty; + if (candidate && typeof kid === "string") { + candidate = kid === jwk2.kid; + } + if (candidate && typeof jwk2.alg === "string") { + candidate = alg === jwk2.alg; + } + if (candidate && typeof jwk2.use === "string") { + candidate = jwk2.use === "sig"; + } + if (candidate && Array.isArray(jwk2.key_ops)) { + candidate = jwk2.key_ops.includes("verify"); + } + if (candidate && alg === "EdDSA") { + candidate = jwk2.crv === "Ed25519" || jwk2.crv === "Ed448"; + } + if (candidate) { + switch (alg) { + case "ES256": + candidate = jwk2.crv === "P-256"; + break; + case "ES256K": + candidate = jwk2.crv === "secp256k1"; + break; + case "ES384": + candidate = jwk2.crv === "P-384"; + break; + case "ES512": + candidate = jwk2.crv === "P-521"; + break; + } + } + return candidate; + }); + const { 0: jwk, length } = candidates; + if (length === 0) { + throw new JWKSNoMatchingKey(); + } else if (length !== 1) { + const error = new JWKSMultipleMatchingKeys(); + const { _cached } = this; + error[Symbol.asyncIterator] = async function* () { + for (const jwk2 of candidates) { + try { + yield await importWithAlgCache(_cached, jwk2, alg); + } catch (_a) { + continue; + } + } + }; + throw error; + } + return importWithAlgCache(this._cached, jwk, alg); + } +}; +async function importWithAlgCache(cache, jwk, alg) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk); + if (cached[alg] === void 0) { + const key = await importJWK({ ...jwk, ext: true }, alg); + if (key instanceof Uint8Array || key.type !== "public") { + throw new JWKSInvalid("JSON Web Key Set members must be public keys"); + } + cached[alg] = key; + } + return cached[alg]; +} +function createLocalJWKSet(jwks) { + const set = new LocalJWKSet(jwks); + return async function(protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} + +// dist/browser/runtime/fetch_jwks.js +var fetchJwks = async (url, timeout, options) => { + let controller; + let id; + let timedOut = false; + if (typeof AbortController === "function") { + controller = new AbortController(); + id = setTimeout(() => { + timedOut = true; + controller.abort(); + }, timeout); + } + const response = await fetch(url.href, { + signal: controller ? controller.signal : void 0, + redirect: "manual", + headers: options.headers + }).catch((err) => { + if (timedOut) + throw new JWKSTimeout(); + throw err; + }); + if (id !== void 0) + clearTimeout(id); + if (response.status !== 200) { + throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response"); + } + try { + return await response.json(); + } catch (_a) { + throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON"); + } +}; +var fetch_jwks_default = fetchJwks; + +// dist/browser/jwks/remote.js +var RemoteJWKSet = class extends LocalJWKSet { + constructor(url, options) { + super({ keys: [] }); + this._jwks = void 0; + if (!(url instanceof URL)) { + throw new TypeError("url must be an instance of URL"); + } + this._url = new URL(url.href); + this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers }; + this._timeoutDuration = typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === "number" ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5e3; + this._cooldownDuration = typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === "number" ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 3e4; + this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === "number" ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 6e5; + } + coolingDown() { + return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cooldownDuration : false; + } + fresh() { + return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cacheMaxAge : false; + } + async getKey(protectedHeader, token) { + if (!this._jwks || !this.fresh()) { + await this.reload(); + } + try { + return await super.getKey(protectedHeader, token); + } catch (err) { + if (err instanceof JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload(); + return super.getKey(protectedHeader, token); + } + } + throw err; + } + } + async reload() { + if (this._pendingFetch && isCloudflareWorkers()) { + this._pendingFetch = void 0; + } + this._pendingFetch || (this._pendingFetch = fetch_jwks_default(this._url, this._timeoutDuration, this._options).then((json) => { + if (!isJWKSLike(json)) { + throw new JWKSInvalid("JSON Web Key Set malformed"); + } + this._jwks = { keys: json.keys }; + this._jwksTimestamp = Date.now(); + this._pendingFetch = void 0; + }).catch((err) => { + this._pendingFetch = void 0; + throw err; + })); + await this._pendingFetch; + } +}; +function createRemoteJWKSet(url, options) { + const set = new RemoteJWKSet(url, options); + return async function(protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} + +// dist/browser/jwt/unsecured.js +var UnsecuredJWT = class extends ProduceJWT { + encode() { + const header = encode(JSON.stringify({ alg: "none" })); + const payload = encode(JSON.stringify(this._payload)); + return `${header}.${payload}.`; + } + static decode(jwt, options) { + if (typeof jwt !== "string") { + throw new JWTInvalid("Unsecured JWT must be a string"); + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split("."); + if (length !== 3 || signature !== "") { + throw new JWTInvalid("Invalid Unsecured JWT"); + } + let header; + try { + header = JSON.parse(decoder.decode(decode(encodedHeader))); + if (header.alg !== "none") + throw new Error(); + } catch (_a) { + throw new JWTInvalid("Invalid Unsecured JWT"); + } + const payload = jwt_claims_set_default(header, decode(encodedPayload), options); + return { payload, header }; + } +}; + +// dist/browser/util/base64url.js +var base64url_exports2 = {}; +__export(base64url_exports2, { + decode: () => decode2, + encode: () => encode2 +}); +var encode2 = encode; +var decode2 = decode; + +// dist/browser/util/decode_protected_header.js +function decodeProtectedHeader(token) { + let protectedB64u; + if (typeof token === "string") { + const parts = token.split("."); + if (parts.length === 3 || parts.length === 5) { + ; + [protectedB64u] = parts; + } + } else if (typeof token === "object" && token) { + if ("protected" in token) { + protectedB64u = token.protected; + } else { + throw new TypeError("Token does not contain a Protected Header"); + } + } + try { + if (typeof protectedB64u !== "string" || !protectedB64u) { + throw new Error(); + } + const result = JSON.parse(decoder.decode(decode2(protectedB64u))); + if (!isObject(result)) { + throw new Error(); + } + return result; + } catch (_a) { + throw new TypeError("Invalid Token or Protected Header formatting"); + } +} + +// dist/browser/util/decode_jwt.js +function decodeJwt(jwt) { + if (typeof jwt !== "string") + throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string"); + const { 1: payload, length } = jwt.split("."); + if (length === 5) + throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded"); + if (length !== 3) + throw new JWTInvalid("Invalid JWT"); + if (!payload) + throw new JWTInvalid("JWTs must contain a payload"); + let decoded; + try { + decoded = decode2(payload); + } catch (_a) { + throw new JWTInvalid("Failed to parse the base64url encoded payload"); + } + let result; + try { + result = JSON.parse(decoder.decode(decoded)); + } catch (_b) { + throw new JWTInvalid("Failed to parse the decoded payload as JSON"); + } + if (!isObject(result)) + throw new JWTInvalid("Invalid JWT Claims Set"); + return result; +} + +// dist/browser/runtime/generate.js +async function generateSecret(alg, options) { + var _a; + let length; + let algorithm; + let keyUsages; + switch (alg) { + case "HS256": + case "HS384": + case "HS512": + length = parseInt(alg.slice(-3), 10); + algorithm = { name: "HMAC", hash: `SHA-${length}`, length }; + keyUsages = ["sign", "verify"]; + break; + case "A128CBC-HS256": + case "A192CBC-HS384": + case "A256CBC-HS512": + length = parseInt(alg.slice(-3), 10); + return random_default(new Uint8Array(length >> 3)); + case "A128KW": + case "A192KW": + case "A256KW": + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: "AES-KW", length }; + keyUsages = ["wrapKey", "unwrapKey"]; + break; + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": + case "A128GCM": + case "A192GCM": + case "A256GCM": + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: "AES-GCM", length }; + keyUsages = ["encrypt", "decrypt"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + return webcrypto_default.subtle.generateKey(algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); +} +function getModulusLengthOption(options) { + var _a; + const modulusLength = (_a = options === null || options === void 0 ? void 0 : options.modulusLength) !== null && _a !== void 0 ? _a : 2048; + if (typeof modulusLength !== "number" || modulusLength < 2048) { + throw new JOSENotSupported("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used"); + } + return modulusLength; +} +async function generateKeyPair(alg, options) { + var _a, _b, _c, _d; + let algorithm; + let keyUsages; + switch (alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { + name: "RSA-PSS", + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["sign", "verify"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { + name: "RSASSA-PKCS1-v1_5", + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["sign", "verify"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["decrypt", "unwrapKey", "encrypt", "wrapKey"]; + break; + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = ["sign", "verify"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = ["sign", "verify"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = ["sign", "verify"]; + break; + case "EdDSA": + keyUsages = ["sign", "verify"]; + const crv = (_a = options === null || options === void 0 ? void 0 : options.crv) !== null && _a !== void 0 ? _a : "Ed25519"; + switch (crv) { + case "Ed25519": + case "Ed448": + algorithm = { name: crv }; + break; + default: + throw new JOSENotSupported("Invalid or unsupported crv option provided"); + } + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + keyUsages = ["deriveKey", "deriveBits"]; + const crv2 = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : "P-256"; + switch (crv2) { + case "P-256": + case "P-384": + case "P-521": { + algorithm = { name: "ECDH", namedCurve: crv2 }; + break; + } + case "X25519": + case "X448": + algorithm = { name: crv2 }; + break; + default: + throw new JOSENotSupported("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448"); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + try { + return await webcrypto_default.subtle.generateKey(algorithm, (_c = options === null || options === void 0 ? void 0 : options.extractable) !== null && _c !== void 0 ? _c : false, keyUsages); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + algorithm = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.generateKey(algorithm, (_d = options === null || options === void 0 ? void 0 : options.extractable) !== null && _d !== void 0 ? _d : false, keyUsages); + } + throw err; + } +} + +// dist/browser/key/generate_key_pair.js +async function generateKeyPair2(alg, options) { + return generateKeyPair(alg, options); +} + +// dist/browser/key/generate_secret.js +async function generateSecret2(alg, options) { + return generateSecret(alg, options); +} +export { + CompactEncrypt, + CompactSign, + EmbeddedJWK, + EncryptJWT, + FlattenedEncrypt, + FlattenedSign, + GeneralEncrypt, + GeneralSign, + SignJWT, + UnsecuredJWT, + base64url_exports2 as base64url, + calculateJwkThumbprint, + calculateJwkThumbprintUri, + compactDecrypt, + compactVerify, + createLocalJWKSet, + createRemoteJWKSet, + decodeJwt, + decodeProtectedHeader, + errors_exports as errors, + exportJWK, + exportPKCS8, + exportSPKI, + flattenedDecrypt, + flattenedVerify, + generalDecrypt, + generalVerify, + generateKeyPair2 as generateKeyPair, + generateSecret2 as generateSecret, + importJWK, + importPKCS8, + importSPKI, + importX509, + jwtDecrypt, + jwtVerify +}; diff --git a/dist/browser/index.bundle.min.js b/dist/browser/index.bundle.min.js new file mode 100644 index 0000000000..b2989e9e48 --- /dev/null +++ b/dist/browser/index.bundle.min.js @@ -0,0 +1,4 @@ +var qt=Object.defineProperty;var ct=(e,t)=>{for(var r in t)qt(e,r,{get:t[r],enumerable:!0})};var f=crypto,b=e=>e instanceof CryptoKey;var Zt=async(e,t)=>{let r=`SHA-${e.slice(-3)}`;return new Uint8Array(await f.subtle.digest(r,t))},Ke=Zt;var E=new TextEncoder,v=new TextDecoder,xe=2**32;function W(...e){let t=e.reduce((o,{length:a})=>o+a,0),r=new Uint8Array(t),n=0;return e.forEach(o=>{r.set(o,n),n+=o.length}),r}function dt(e,t){return W(E.encode(e),new Uint8Array([0]),t)}function Ne(e,t,r){if(t<0||t>=xe)throw new RangeError(`value must be >= 0 and <= ${xe-1}. Received ${t}`);e.set([t>>>24,t>>>16,t>>>8,t&255],r)}function He(e){let t=Math.floor(e/xe),r=e%xe,n=new Uint8Array(8);return Ne(n,t,0),Ne(n,r,4),n}function Ce(e){let t=new Uint8Array(4);return Ne(t,e),t}function Pe(e){return W(Ce(e.length),e)}async function pt(e,t,r){let n=Math.ceil((t>>3)/32),o=new Uint8Array(n*32);for(let a=0;a>3)}var We=e=>{let t=e;typeof t=="string"&&(t=E.encode(t));let r=32768,n=[];for(let o=0;oWe(e).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"),ke=e=>{let t=atob(e),r=new Uint8Array(t.length);for(let n=0;n{let t=e;t instanceof Uint8Array&&(t=v.decode(t)),t=t.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"");try{return ke(t)}catch(r){throw new TypeError("The input to be decoded is not correctly encoded.")}};var ft={};ct(ft,{JOSEAlgNotAllowed:()=>B,JOSEError:()=>C,JOSENotSupported:()=>l,JWEDecryptionFailed:()=>M,JWEInvalid:()=>u,JWKInvalid:()=>de,JWKSInvalid:()=>L,JWKSMultipleMatchingKeys:()=>pe,JWKSNoMatchingKey:()=>q,JWKSTimeout:()=>ue,JWSInvalid:()=>h,JWSSignatureVerificationFailed:()=>Z,JWTClaimValidationFailed:()=>J,JWTExpired:()=>re,JWTInvalid:()=>H});var C=class extends Error{static get code(){return"ERR_JOSE_GENERIC"}constructor(t){var r;super(t),this.code="ERR_JOSE_GENERIC",this.name=this.constructor.name,(r=Error.captureStackTrace)===null||r===void 0||r.call(Error,this,this.constructor)}},J=class extends C{static get code(){return"ERR_JWT_CLAIM_VALIDATION_FAILED"}constructor(t,r="unspecified",n="unspecified"){super(t),this.code="ERR_JWT_CLAIM_VALIDATION_FAILED",this.claim=r,this.reason=n}},re=class extends C{static get code(){return"ERR_JWT_EXPIRED"}constructor(t,r="unspecified",n="unspecified"){super(t),this.code="ERR_JWT_EXPIRED",this.claim=r,this.reason=n}},B=class extends C{constructor(){super(...arguments),this.code="ERR_JOSE_ALG_NOT_ALLOWED"}static get code(){return"ERR_JOSE_ALG_NOT_ALLOWED"}},l=class extends C{constructor(){super(...arguments),this.code="ERR_JOSE_NOT_SUPPORTED"}static get code(){return"ERR_JOSE_NOT_SUPPORTED"}},M=class extends C{constructor(){super(...arguments),this.code="ERR_JWE_DECRYPTION_FAILED",this.message="decryption operation failed"}static get code(){return"ERR_JWE_DECRYPTION_FAILED"}},u=class extends C{constructor(){super(...arguments),this.code="ERR_JWE_INVALID"}static get code(){return"ERR_JWE_INVALID"}},h=class extends C{constructor(){super(...arguments),this.code="ERR_JWS_INVALID"}static get code(){return"ERR_JWS_INVALID"}},H=class extends C{constructor(){super(...arguments),this.code="ERR_JWT_INVALID"}static get code(){return"ERR_JWT_INVALID"}},de=class extends C{constructor(){super(...arguments),this.code="ERR_JWK_INVALID"}static get code(){return"ERR_JWK_INVALID"}},L=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_INVALID"}static get code(){return"ERR_JWKS_INVALID"}},q=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_NO_MATCHING_KEY",this.message="no applicable key found in the JSON Web Key Set"}static get code(){return"ERR_JWKS_NO_MATCHING_KEY"}},pe=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS",this.message="multiple matching keys found in the JSON Web Key Set"}static get code(){return"ERR_JWKS_MULTIPLE_MATCHING_KEYS"}},ue=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_TIMEOUT",this.message="request timed out"}static get code(){return"ERR_JWKS_TIMEOUT"}},Z=class extends C{constructor(){super(...arguments),this.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED",this.message="signature verification failed"}static get code(){return"ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}};var $=f.getRandomValues.bind(f);function Be(e){switch(e){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 l(`Unsupported JWE Algorithm: ${e}`)}}var Je=e=>$(new Uint8Array(Be(e)>>3));var Qt=(e,t)=>{if(t.length<<3!==Be(e))throw new u("Invalid Initialization Vector length")},Te=Qt;var jt=(e,t)=>{let r=e.byteLength<<3;if(r!==t)throw new u(`Invalid Content Encryption Key length. Expected ${t} bits, got ${r} bits`)},ne=jt;var er=(e,t)=>{if(!(e instanceof Uint8Array))throw new TypeError("First argument must be a buffer");if(!(t instanceof Uint8Array))throw new TypeError("Second argument must be a buffer");if(e.length!==t.length)throw new TypeError("Input buffers must have the same length");let r=e.length,n=0,o=-1;for(;++oe.usages.includes(r))){let r="CryptoKey does not support this operation, its usages must include ";if(t.length>2){let n=t.pop();r+=`one of ${t.join(", ")}, or ${n}.`}else t.length===2?r+=`one of ${t[0]} or ${t[1]}.`:r+=`${t[0]}.`;throw new TypeError(r)}}function ht(e,t,...r){switch(t){case"HS256":case"HS384":case"HS512":{if(!N(e.algorithm,"HMAC"))throw P("HMAC");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!N(e.algorithm,"RSASSA-PKCS1-v1_5"))throw P("RSASSA-PKCS1-v1_5");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!N(e.algorithm,"RSA-PSS"))throw P("RSA-PSS");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"EdDSA":{if(e.algorithm.name!=="Ed25519"&&e.algorithm.name!=="Ed448"){if(D()){if(N(e.algorithm,"NODE-ED25519"))break;throw P("Ed25519, Ed448, or NODE-ED25519")}throw P("Ed25519 or Ed448")}break}case"ES256":case"ES384":case"ES512":{if(!N(e.algorithm,"ECDSA"))throw P("ECDSA");let n=tr(t);if(e.algorithm.namedCurve!==n)throw P(n,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}mt(e,r)}function I(e,t,...r){switch(t){case"A128GCM":case"A192GCM":case"A256GCM":{if(!N(e.algorithm,"AES-GCM"))throw P("AES-GCM");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw P(n,"algorithm.length");break}case"A128KW":case"A192KW":case"A256KW":{if(!N(e.algorithm,"AES-KW"))throw P("AES-KW");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw P(n,"algorithm.length");break}case"ECDH":{switch(e.algorithm.name){case"ECDH":case"X25519":case"X448":break;default:throw P("ECDH, X25519, or X448")}break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":if(!N(e.algorithm,"PBKDF2"))throw P("PBKDF2");break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(!N(e.algorithm,"RSA-OAEP"))throw P("RSA-OAEP");let n=parseInt(t.slice(9),10)||1;if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}default:throw new TypeError("CryptoKey does not support this operation")}mt(e,r)}function yt(e,t,...r){if(r.length>2){let n=r.pop();e+=`one of type ${r.join(", ")}, or ${n}.`}else r.length===2?e+=`one of type ${r[0]} or ${r[1]}.`:e+=`of type ${r[0]}.`;return t==null?e+=` Received ${t}`:typeof t=="function"&&t.name?e+=` Received function ${t.name}`:typeof t=="object"&&t!=null&&t.constructor&&t.constructor.name&&(e+=` Received an instance of ${t.constructor.name}`),e}var A=(e,...t)=>yt("Key must be ",e,...t);function Le(e,t,...r){return yt(`Key for the ${e} algorithm must be `,t,...r)}var $e=e=>b(e),y=["CryptoKey"];async function rr(e,t,r,n,o,a){if(!(t instanceof Uint8Array))throw new TypeError(A(t,"Uint8Array"));let i=parseInt(e.slice(1,4),10),s=await f.subtle.importKey("raw",t.subarray(i>>3),"AES-CBC",!1,["decrypt"]),c=await f.subtle.importKey("raw",t.subarray(0,i>>3),{hash:`SHA-${i<<1}`,name:"HMAC"},!1,["sign"]),d=W(a,n,r,He(a.length<<3)),p=new Uint8Array((await f.subtle.sign("HMAC",c,d)).slice(0,i>>3)),m;try{m=lt(o,p)}catch(T){}if(!m)throw new M;let K;try{K=new Uint8Array(await f.subtle.decrypt({iv:n,name:"AES-CBC"},s,r))}catch(T){}if(!K)throw new M;return K}async function nr(e,t,r,n,o,a){let i;t instanceof Uint8Array?i=await f.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]):(I(t,e,"decrypt"),i=t);try{return new Uint8Array(await f.subtle.decrypt({additionalData:a,iv:n,name:"AES-GCM",tagLength:128},i,W(r,o)))}catch(s){throw new M}}var or=async(e,t,r,n,o,a)=>{if(!b(t)&&!(t instanceof Uint8Array))throw new TypeError(A(t,...y,"Uint8Array"));switch(Te(e,n),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return t instanceof Uint8Array&&ne(t,parseInt(e.slice(-3),10)),rr(e,t,r,n,o,a);case"A128GCM":case"A192GCM":case"A256GCM":return t instanceof Uint8Array&&ne(t,parseInt(e.slice(1,4),10)),nr(e,t,r,n,o,a);default:throw new l("Unsupported JWE Content Encryption Algorithm")}},De=or;var wt=async()=>{throw new l('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.')},Et=async()=>{throw new l('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.')};var ar=(...e)=>{let t=e.filter(Boolean);if(t.length===0||t.length===1)return!0;let r;for(let n of t){let o=Object.keys(n);if(!r||r.size===0){r=new Set(o);continue}for(let a of o){if(r.has(a))return!1;r.add(a)}}return!0},R=ar;function ir(e){return typeof e=="object"&&e!==null}function w(e){if(!ir(e)||Object.prototype.toString.call(e)!=="[object Object]")return!1;if(Object.getPrototypeOf(e)===null)return!0;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var sr=[{hash:"SHA-256",name:"HMAC"},!0,["sign"]],oe=sr;function gt(e,t){if(e.algorithm.length!==parseInt(t.slice(1,4),10))throw new TypeError(`Invalid key size for alg: ${t}`)}function St(e,t,r){if(b(e))return I(e,t,r),e;if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"AES-KW",!0,[r]);throw new TypeError(A(e,...y,"Uint8Array"))}var fe=async(e,t,r)=>{let n=await St(t,e,"wrapKey");gt(n,e);let o=await f.subtle.importKey("raw",r,...oe);return new Uint8Array(await f.subtle.wrapKey("raw",o,n,"AES-KW"))},le=async(e,t,r)=>{let n=await St(t,e,"unwrapKey");gt(n,e);let o=await f.subtle.unwrapKey("raw",r,n,"AES-KW",...oe);return new Uint8Array(await f.subtle.exportKey("raw",o))};async function Re(e,t,r,n,o=new Uint8Array(0),a=new Uint8Array(0)){if(!b(e))throw new TypeError(A(e,...y));if(I(e,"ECDH"),!b(t))throw new TypeError(A(t,...y));I(t,"ECDH","deriveBits");let i=W(Pe(E.encode(r)),Pe(o),Pe(a),Ce(n)),s;e.algorithm.name==="X25519"?s=256:e.algorithm.name==="X448"?s=448:s=Math.ceil(parseInt(e.algorithm.namedCurve.substr(-3),10)/8)<<3;let c=new Uint8Array(await f.subtle.deriveBits({name:e.algorithm.name,public:e},t,s));return pt(c,n,i)}async function At(e){if(!b(e))throw new TypeError(A(e,...y));return f.subtle.generateKey(e.algorithm,!0,["deriveBits"])}function Oe(e){if(!b(e))throw new TypeError(A(e,...y));return["P-256","P-384","P-521"].includes(e.algorithm.namedCurve)||e.algorithm.name==="X25519"||e.algorithm.name==="X448"}function Ge(e){if(!(e instanceof Uint8Array)||e.length<8)throw new u("PBES2 Salt Input must be 8 or more octets")}function cr(e,t){if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"PBKDF2",!1,["deriveBits"]);if(b(e))return I(e,t,"deriveBits","deriveKey"),e;throw new TypeError(A(e,...y,"Uint8Array"))}async function _t(e,t,r,n){Ge(e);let o=dt(t,e),a=parseInt(t.slice(13,16),10),i={hash:`SHA-${t.slice(8,11)}`,iterations:r,name:"PBKDF2",salt:o},s={length:a,name:"AES-KW"},c=await cr(n,t);if(c.usages.includes("deriveBits"))return new Uint8Array(await f.subtle.deriveBits(i,c,a));if(c.usages.includes("deriveKey"))return f.subtle.deriveKey(i,c,s,!1,["wrapKey","unwrapKey"]);throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"')}var vt=async(e,t,r,n=2048,o=$(new Uint8Array(16)))=>{let a=await _t(o,e,n,t);return{encryptedKey:await fe(e.slice(-6),a,r),p2c:n,p2s:g(o)}},Kt=async(e,t,r,n,o)=>{let a=await _t(o,e,n,t);return le(e.slice(-6),a,r)};function ae(e){switch(e){case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":return"RSA-OAEP";default:throw new l(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}var Q=(e,t)=>{if(e.startsWith("RS")||e.startsWith("PS")){let{modulusLength:r}=t.algorithm;if(typeof r!="number"||r<2048)throw new TypeError(`${e} requires key modulusLength to be 2048 bits or larger`)}};var xt=async(e,t,r)=>{if(!b(t))throw new TypeError(A(t,...y));if(I(t,e,"encrypt","wrapKey"),Q(e,t),t.usages.includes("encrypt"))return new Uint8Array(await f.subtle.encrypt(ae(e),t,r));if(t.usages.includes("wrapKey")){let n=await f.subtle.importKey("raw",r,...oe);return new Uint8Array(await f.subtle.wrapKey("raw",n,t,ae(e)))}throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation')},Ht=async(e,t,r)=>{if(!b(t))throw new TypeError(A(t,...y));if(I(t,e,"decrypt","unwrapKey"),Q(e,t),t.usages.includes("decrypt"))return new Uint8Array(await f.subtle.decrypt(ae(e),t,r));if(t.usages.includes("unwrapKey")){let n=await f.subtle.unwrapKey("raw",r,t,ae(e),...oe);return new Uint8Array(await f.subtle.exportKey("raw",n))}throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation')};function me(e){switch(e){case"A128GCM":return 128;case"A192GCM":return 192;case"A256GCM":case"A128CBC-HS256":return 256;case"A192CBC-HS384":return 384;case"A256CBC-HS512":return 512;default:throw new l(`Unsupported JWE Algorithm: ${e}`)}}var O=e=>$(new Uint8Array(me(e)>>3));var Ve=(e,t)=>{let r=(e.match(/.{1,64}/g)||[]).join(` +`);return`-----BEGIN ${t}----- +${r} +-----END ${t}-----`};var Wt=async(e,t,r)=>{if(!b(r))throw new TypeError(A(r,...y));if(!r.extractable)throw new TypeError("CryptoKey is not extractable");if(r.type!==e)throw new TypeError(`key is not a ${e} key`);return Ve(We(new Uint8Array(await f.subtle.exportKey(t,r))),`${e.toUpperCase()} KEY`)},Jt=e=>Wt("public","spki",e),Tt=e=>Wt("private","pkcs8",e),G=(e,t,r=0)=>{r===0&&(t.unshift(t.length),t.unshift(6));let n=e.indexOf(t[0],r);if(n===-1)return!1;let o=e.subarray(n,n+t.length);return o.length!==t.length?!1:o.every((a,i)=>a===t[i])||G(e,t,n+1)},Ct=e=>{switch(!0){case G(e,[42,134,72,206,61,3,1,7]):return"P-256";case G(e,[43,129,4,0,34]):return"P-384";case G(e,[43,129,4,0,35]):return"P-521";case G(e,[43,101,110]):return"X25519";case G(e,[43,101,111]):return"X448";case G(e,[43,101,112]):return"Ed25519";case G(e,[43,101,113]):return"Ed448";default:throw new l("Invalid or unsupported EC Key Curve or OKP Key Sub Type")}},It=async(e,t,r,n,o)=>{var a,i;let s,c,d=new Uint8Array(atob(r.replace(e,"")).split("").map(m=>m.charCodeAt(0))),p=t==="spki";switch(n){case"PS256":case"PS384":case"PS512":s={name:"RSA-PSS",hash:`SHA-${n.slice(-3)}`},c=p?["verify"]:["sign"];break;case"RS256":case"RS384":case"RS512":s={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${n.slice(-3)}`},c=p?["verify"]:["sign"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":s={name:"RSA-OAEP",hash:`SHA-${parseInt(n.slice(-3),10)||1}`},c=p?["encrypt","wrapKey"]:["decrypt","unwrapKey"];break;case"ES256":s={name:"ECDSA",namedCurve:"P-256"},c=p?["verify"]:["sign"];break;case"ES384":s={name:"ECDSA",namedCurve:"P-384"},c=p?["verify"]:["sign"];break;case"ES512":s={name:"ECDSA",namedCurve:"P-521"},c=p?["verify"]:["sign"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{let m=Ct(d);s=m.startsWith("P-")?{name:"ECDH",namedCurve:m}:{name:m},c=p?[]:["deriveBits"];break}case"EdDSA":s={name:Ct(d)},c=p?["verify"]:["sign"];break;default:throw new l('Invalid or unsupported "alg" (Algorithm) value')}try{return await f.subtle.importKey(t,d,s,(a=o==null?void 0:o.extractable)!==null&&a!==void 0?a:!1,c)}catch(m){if(s.name==="Ed25519"&&(m==null?void 0:m.name)==="NotSupportedError"&&D())return s={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.importKey(t,d,s,(i=o==null?void 0:o.extractable)!==null&&i!==void 0?i:!1,c);throw m}},Dt=(e,t,r)=>It(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g,"pkcs8",e,t,r),Fe=(e,t,r)=>It(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g,"spki",e,t,r);function Pt(e){let t=[],r=0;for(;r=128;)r=r*128+e[t]-128,t++;r=r*128+e[t]-128,t++}let n=0;if(e[t]<128)n=e[t],t++;else if(n===128){for(n=0;e[t+n]!==0||e[t+n+1]!==0;){if(n>e.byteLength)throw new TypeError("invalid indefinite form length");n++}let a=t+n+2;return{byteLength:a,contents:e.subarray(t,t+n),raw:e.subarray(0,a)}}else{let a=e[t]&127;t++,n=0;for(let i=0;i{let n;try{n=pr(e)}catch(o){throw new TypeError("failed to parse the X.509 certificate",{cause:o})}return Fe(n,t,r)};function ur(e){let t,r;switch(e.kty){case"oct":{switch(e.alg){case"HS256":case"HS384":case"HS512":t={name:"HMAC",hash:`SHA-${e.alg.slice(-3)}`},r=["sign","verify"];break;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":throw new l(`${e.alg} keys cannot be imported as CryptoKey instances`);case"A128GCM":case"A192GCM":case"A256GCM":case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":t={name:"AES-GCM"},r=["encrypt","decrypt"];break;case"A128KW":case"A192KW":case"A256KW":t={name:"AES-KW"},r=["wrapKey","unwrapKey"];break;case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":t={name:"PBKDF2"},r=["deriveBits"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"RSA":{switch(e.alg){case"PS256":case"PS384":case"PS512":t={name:"RSA-PSS",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":t={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":t={name:"RSA-OAEP",hash:`SHA-${parseInt(e.alg.slice(-3),10)||1}`},r=e.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"EC":{switch(e.alg){case"ES256":t={name:"ECDSA",namedCurve:"P-256"},r=e.d?["sign"]:["verify"];break;case"ES384":t={name:"ECDSA",namedCurve:"P-384"},r=e.d?["sign"]:["verify"];break;case"ES512":t={name:"ECDSA",namedCurve:"P-521"},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:"ECDH",namedCurve:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"OKP":{switch(e.alg){case"EdDSA":t={name:e.crv},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}default:throw new l('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return{algorithm:t,keyUsages:r}}var fr=async e=>{var t,r;if(!e.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');let{algorithm:n,keyUsages:o}=ur(e),a=[n,(t=e.ext)!==null&&t!==void 0?t:!1,(r=e.key_ops)!==null&&r!==void 0?r:o];if(n.name==="PBKDF2")return f.subtle.importKey("raw",S(e.k),...a);let i={...e};delete i.alg,delete i.use;try{return await f.subtle.importKey("jwk",i,...a)}catch(s){if(n.name==="Ed25519"&&(s==null?void 0:s.name)==="NotSupportedError"&&D())return a[0]={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.importKey("jwk",i,...a);throw s}},ze=fr;async function lr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PUBLIC KEY-----")!==0)throw new TypeError('"spki" must be SPKI formatted string');return Fe(e,t,r)}async function mr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN CERTIFICATE-----")!==0)throw new TypeError('"x509" must be X.509 formatted string');return Ot(e,t,r)}async function hr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PRIVATE KEY-----")!==0)throw new TypeError('"pkcs8" must be PKCS#8 formatted string');return Dt(e,t,r)}async function j(e,t,r){var n;if(!w(e))throw new TypeError("JWK must be an object");switch(t||(t=e.alg),e.kty){case"oct":if(typeof e.k!="string"||!e.k)throw new TypeError('missing "k" (Key Value) Parameter value');return r!=null||(r=e.ext!==!0),r?ze({...e,alg:t,ext:(n=e.ext)!==null&&n!==void 0?n:!1}):S(e.k);case"RSA":if(e.oth!==void 0)throw new l('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');case"EC":case"OKP":return ze({...e,alg:t});default:throw new l('Unsupported "kty" (Key Type) Parameter value')}}var yr=(e,t)=>{if(!(t instanceof Uint8Array)){if(!$e(t))throw new TypeError(Le(e,t,...y,"Uint8Array"));if(t.type!=="secret")throw new TypeError(`${y.join(" or ")} instances for symmetric algorithms must be of type "secret"`)}},wr=(e,t,r)=>{if(!$e(t))throw new TypeError(Le(e,t,...y));if(t.type==="secret")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithms must not be of type "secret"`);if(r==="sign"&&t.type==="public")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm signing must be of type "private"`);if(r==="decrypt"&&t.type==="public")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm decryption must be of type "private"`);if(t.algorithm&&r==="verify"&&t.type==="private")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm verifying must be of type "public"`);if(t.algorithm&&r==="encrypt"&&t.type==="private")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm encryption must be of type "public"`)},Er=(e,t,r)=>{e.startsWith("HS")||e==="dir"||e.startsWith("PBES2")||/^A\d{3}(?:GCM)?KW$/.test(e)?yr(e,t):wr(e,t,r)},V=Er;async function gr(e,t,r,n,o){if(!(r instanceof Uint8Array))throw new TypeError(A(r,"Uint8Array"));let a=parseInt(e.slice(1,4),10),i=await f.subtle.importKey("raw",r.subarray(a>>3),"AES-CBC",!1,["encrypt"]),s=await f.subtle.importKey("raw",r.subarray(0,a>>3),{hash:`SHA-${a<<1}`,name:"HMAC"},!1,["sign"]),c=new Uint8Array(await f.subtle.encrypt({iv:n,name:"AES-CBC"},i,t)),d=W(o,n,c,He(o.length<<3)),p=new Uint8Array((await f.subtle.sign("HMAC",s,d)).slice(0,a>>3));return{ciphertext:c,tag:p}}async function Sr(e,t,r,n,o){let a;r instanceof Uint8Array?a=await f.subtle.importKey("raw",r,"AES-GCM",!1,["encrypt"]):(I(r,e,"encrypt"),a=r);let i=new Uint8Array(await f.subtle.encrypt({additionalData:o,iv:n,name:"AES-GCM",tagLength:128},a,t)),s=i.slice(-16);return{ciphertext:i.slice(0,-16),tag:s}}var Ar=async(e,t,r,n,o)=>{if(!b(r)&&!(r instanceof Uint8Array))throw new TypeError(A(r,...y,"Uint8Array"));switch(Te(e,n),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return r instanceof Uint8Array&&ne(r,parseInt(e.slice(-3),10)),gr(e,t,r,n,o);case"A128GCM":case"A192GCM":case"A256GCM":return r instanceof Uint8Array&&ne(r,parseInt(e.slice(1,4),10)),Sr(e,t,r,n,o);default:throw new l("Unsupported JWE Content Encryption Algorithm")}},he=Ar;async function Ut(e,t,r,n){let o=e.slice(0,7);n||(n=Je(o));let{ciphertext:a,tag:i}=await he(o,r,t,n,new Uint8Array(0));return{encryptedKey:a,iv:g(n),tag:g(i)}}async function Mt(e,t,r,n,o){let a=e.slice(0,7);return De(a,t,r,n,o,new Uint8Array(0))}async function br(e,t,r,n,o){switch(V(e,t,"decrypt"),e){case"dir":{if(r!==void 0)throw new u("Encountered unexpected JWE Encrypted Key");return t}case"ECDH-ES":if(r!==void 0)throw new u("Encountered unexpected JWE Encrypted Key");case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!w(n.epk))throw new u('JOSE Header "epk" (Ephemeral Public Key) missing or invalid');if(!Oe(t))throw new l("ECDH with the provided key is not allowed or not supported by your javascript runtime");let a=await j(n.epk,e),i,s;if(n.apu!==void 0){if(typeof n.apu!="string")throw new u('JOSE Header "apu" (Agreement PartyUInfo) invalid');i=S(n.apu)}if(n.apv!==void 0){if(typeof n.apv!="string")throw new u('JOSE Header "apv" (Agreement PartyVInfo) invalid');s=S(n.apv)}let c=await Re(a,t,e==="ECDH-ES"?n.enc:e,e==="ECDH-ES"?me(n.enc):parseInt(e.slice(-5,-2),10),i,s);if(e==="ECDH-ES")return c;if(r===void 0)throw new u("JWE Encrypted Key missing");return le(e.slice(-6),c,r)}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(r===void 0)throw new u("JWE Encrypted Key missing");return Ht(e,t,r)}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{if(r===void 0)throw new u("JWE Encrypted Key missing");if(typeof n.p2c!="number")throw new u('JOSE Header "p2c" (PBES2 Count) missing or invalid');let a=(o==null?void 0:o.maxPBES2Count)||1e4;if(n.p2c>a)throw new u('JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds');if(typeof n.p2s!="string")throw new u('JOSE Header "p2s" (PBES2 Salt) missing or invalid');return Kt(e,t,r,n.p2c,S(n.p2s))}case"A128KW":case"A192KW":case"A256KW":{if(r===void 0)throw new u("JWE Encrypted Key missing");return le(e,t,r)}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{if(r===void 0)throw new u("JWE Encrypted Key missing");if(typeof n.iv!="string")throw new u('JOSE Header "iv" (Initialization Vector) missing or invalid');if(typeof n.tag!="string")throw new u('JOSE Header "tag" (Authentication Tag) missing or invalid');let a=S(n.iv),i=S(n.tag);return Mt(e,t,r,a,i)}default:throw new l('Invalid or unsupported "alg" (JWE Algorithm) header value')}}var Nt=br;function _r(e,t,r,n,o){if(o.crit!==void 0&&n.crit===void 0)throw new e('"crit" (Critical) Header Parameter MUST be integrity protected');if(!n||n.crit===void 0)return new Set;if(!Array.isArray(n.crit)||n.crit.length===0||n.crit.some(i=>typeof i!="string"||i.length===0))throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let a;r!==void 0?a=new Map([...Object.entries(r),...t.entries()]):a=t;for(let i of n.crit){if(!a.has(i))throw new l(`Extension Header Parameter "${i}" is not recognized`);if(o[i]===void 0)throw new e(`Extension Header Parameter "${i}" is missing`);if(a.get(i)&&n[i]===void 0)throw new e(`Extension Header Parameter "${i}" MUST be integrity protected`)}return new Set(n.crit)}var U=_r;var vr=(e,t)=>{if(t!==void 0&&(!Array.isArray(t)||t.some(r=>typeof r!="string")))throw new TypeError(`"${e}" option must be an array of strings`);if(t)return new Set(t)},ye=vr;async function we(e,t,r){var n;if(!w(e))throw new u("Flattened JWE must be an object");if(e.protected===void 0&&e.header===void 0&&e.unprotected===void 0)throw new u("JOSE Header missing");if(typeof e.iv!="string")throw new u("JWE Initialization Vector missing or incorrect type");if(typeof e.ciphertext!="string")throw new u("JWE Ciphertext missing or incorrect type");if(typeof e.tag!="string")throw new u("JWE Authentication Tag missing or incorrect type");if(e.protected!==void 0&&typeof e.protected!="string")throw new u("JWE Protected Header incorrect type");if(e.encrypted_key!==void 0&&typeof e.encrypted_key!="string")throw new u("JWE Encrypted Key incorrect type");if(e.aad!==void 0&&typeof e.aad!="string")throw new u("JWE AAD incorrect type");if(e.header!==void 0&&!w(e.header))throw new u("JWE Shared Unprotected Header incorrect type");if(e.unprotected!==void 0&&!w(e.unprotected))throw new u("JWE Per-Recipient Unprotected Header incorrect type");let o;if(e.protected)try{let Y=S(e.protected);o=JSON.parse(v.decode(Y))}catch(Y){throw new u("JWE Protected Header is invalid")}if(!R(o,e.header,e.unprotected))throw new u("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint");let a={...o,...e.header,...e.unprotected};if(U(u,new Map,r==null?void 0:r.crit,o,a),a.zip!==void 0){if(!o||!o.zip)throw new u('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');if(a.zip!=="DEF")throw new l('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}let{alg:i,enc:s}=a;if(typeof i!="string"||!i)throw new u("missing JWE Algorithm (alg) in JWE Header");if(typeof s!="string"||!s)throw new u("missing JWE Encryption Algorithm (enc) in JWE Header");let c=r&&ye("keyManagementAlgorithms",r.keyManagementAlgorithms),d=r&&ye("contentEncryptionAlgorithms",r.contentEncryptionAlgorithms);if(c&&!c.has(i))throw new B('"alg" (Algorithm) Header Parameter not allowed');if(d&&!d.has(s))throw new B('"enc" (Encryption Algorithm) Header Parameter not allowed');let p;e.encrypted_key!==void 0&&(p=S(e.encrypted_key));let m=!1;typeof t=="function"&&(t=await t(o,e),m=!0);let K;try{K=await Nt(i,t,p,a,r)}catch(Y){if(Y instanceof TypeError||Y instanceof u||Y instanceof l)throw Y;K=O(s)}let T=S(e.iv),x=S(e.tag),_=E.encode((n=e.protected)!==null&&n!==void 0?n:""),k;e.aad!==void 0?k=W(_,E.encode("."),E.encode(e.aad)):k=_;let Me=await De(s,K,S(e.ciphertext),T,x,k);a.zip==="DEF"&&(Me=await((r==null?void 0:r.inflateRaw)||wt)(Me));let te={plaintext:Me};return e.protected!==void 0&&(te.protectedHeader=o),e.aad!==void 0&&(te.additionalAuthenticatedData=S(e.aad)),e.unprotected!==void 0&&(te.sharedUnprotectedHeader=e.unprotected),e.header!==void 0&&(te.unprotectedHeader=e.header),m?{...te,key:t}:te}async function Xe(e,t,r){if(e instanceof Uint8Array&&(e=v.decode(e)),typeof e!="string")throw new u("Compact JWE must be a string or Uint8Array");let{0:n,1:o,2:a,3:i,4:s,length:c}=e.split(".");if(c!==5)throw new u("Invalid Compact JWE");let d=await we({ciphertext:i,iv:a||void 0,protected:n||void 0,tag:s||void 0,encrypted_key:o||void 0},t,r),p={plaintext:d.plaintext,protectedHeader:d.protectedHeader};return typeof t=="function"?{...p,key:d.key}:p}async function Kr(e,t,r){if(!w(e))throw new u("General JWE must be an object");if(!Array.isArray(e.recipients)||!e.recipients.every(w))throw new u("JWE Recipients missing or incorrect type");if(!e.recipients.length)throw new u("JWE Recipients has no members");for(let n of e.recipients)try{return await we({aad:e.aad,ciphertext:e.ciphertext,encrypted_key:n.encrypted_key,header:n.header,iv:e.iv,protected:e.protected,tag:e.tag,unprotected:e.unprotected},t,r)}catch(o){}throw new M}var xr=async e=>{if(e instanceof Uint8Array)return{kty:"oct",k:g(e)};if(!b(e))throw new TypeError(A(e,...y,"Uint8Array"));if(!e.extractable)throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");let{ext:t,key_ops:r,alg:n,use:o,...a}=await f.subtle.exportKey("jwk",e);return a},kt=xr;async function Hr(e){return Jt(e)}async function Cr(e){return Tt(e)}async function Ye(e){return kt(e)}async function Pr(e,t,r,n,o={}){let a,i,s;switch(V(e,r,"encrypt"),e){case"dir":{s=r;break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!Oe(r))throw new l("ECDH with the provided key is not allowed or not supported by your javascript runtime");let{apu:c,apv:d}=o,{epk:p}=o;p||(p=(await At(r)).privateKey);let{x:m,y:K,crv:T,kty:x}=await Ye(p),_=await Re(r,p,e==="ECDH-ES"?t:e,e==="ECDH-ES"?me(t):parseInt(e.slice(-5,-2),10),c,d);if(i={epk:{x:m,crv:T,kty:x}},x==="EC"&&(i.epk.y=K),c&&(i.apu=g(c)),d&&(i.apv=g(d)),e==="ECDH-ES"){s=_;break}s=n||O(t);let k=e.slice(-6);a=await fe(k,_,s);break}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{s=n||O(t),a=await xt(e,r,s);break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{s=n||O(t);let{p2c:c,p2s:d}=o;({encryptedKey:a,...i}=await vt(e,r,s,c,d));break}case"A128KW":case"A192KW":case"A256KW":{s=n||O(t),a=await fe(e,r,s);break}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{s=n||O(t);let{iv:c}=o;({encryptedKey:a,...i}=await Ut(e,r,s,c));break}default:throw new l('Invalid or unsupported "alg" (JWE Algorithm) header value')}return{cek:s,encryptedKey:a,parameters:i}}var Ue=Pr;var qe=Symbol(),F=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("plaintext must be an instance of Uint8Array");this._plaintext=t}setKeyManagementParameters(t){if(this._keyManagementParameters)throw new TypeError("setKeyManagementParameters can only be called once");return this._keyManagementParameters=t,this}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._sharedUnprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._sharedUnprotectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}setContentEncryptionKey(t){if(this._cek)throw new TypeError("setContentEncryptionKey can only be called once");return this._cek=t,this}setInitializationVector(t){if(this._iv)throw new TypeError("setInitializationVector can only be called once");return this._iv=t,this}async encrypt(t,r){if(!this._protectedHeader&&!this._unprotectedHeader&&!this._sharedUnprotectedHeader)throw new u("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()");if(!R(this._protectedHeader,this._unprotectedHeader,this._sharedUnprotectedHeader))throw new u("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader,...this._sharedUnprotectedHeader};if(U(u,new Map,r==null?void 0:r.crit,this._protectedHeader,n),n.zip!==void 0){if(!this._protectedHeader||!this._protectedHeader.zip)throw new u('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');if(n.zip!=="DEF")throw new l('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}let{alg:o,enc:a}=n;if(typeof o!="string"||!o)throw new u('JWE "alg" (Algorithm) Header Parameter missing or invalid');if(typeof a!="string"||!a)throw new u('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');let i;if(o==="dir"){if(this._cek)throw new TypeError("setContentEncryptionKey cannot be called when using Direct Encryption")}else if(o==="ECDH-ES"&&this._cek)throw new TypeError("setContentEncryptionKey cannot be called when using Direct Key Agreement");let s;{let x;({cek:s,encryptedKey:i,parameters:x}=await Ue(o,a,t,this._cek,this._keyManagementParameters)),x&&(r&&qe in r?this._unprotectedHeader?this._unprotectedHeader={...this._unprotectedHeader,...x}:this.setUnprotectedHeader(x):this._protectedHeader?this._protectedHeader={...this._protectedHeader,...x}:this.setProtectedHeader(x))}this._iv||(this._iv=Je(a));let c,d,p;this._protectedHeader?d=E.encode(g(JSON.stringify(this._protectedHeader))):d=E.encode(""),this._aad?(p=g(this._aad),c=W(d,E.encode("."),E.encode(p))):c=d;let m,K;if(n.zip==="DEF"){let x=await((r==null?void 0:r.deflateRaw)||Et)(this._plaintext);({ciphertext:m,tag:K}=await he(a,x,s,this._iv,c))}else({ciphertext:m,tag:K}=await he(a,this._plaintext,s,this._iv,c));let T={ciphertext:g(m),iv:g(this._iv),tag:g(K)};return i&&(T.encrypted_key=g(i)),p&&(T.aad=p),this._protectedHeader&&(T.protected=v.decode(d)),this._sharedUnprotectedHeader&&(T.unprotected=this._sharedUnprotectedHeader),this._unprotectedHeader&&(T.header=this._unprotectedHeader),T}};var Ze=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addRecipient(...t){return this.parent.addRecipient(...t)}encrypt(...t){return this.parent.encrypt(...t)}done(){return this.parent}},Qe=class{constructor(t){this._recipients=[],this._plaintext=t}addRecipient(t,r){let n=new Ze(this,t,{crit:r==null?void 0:r.crit});return this._recipients.push(n),n}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}async encrypt(t){var r,n,o;if(!this._recipients.length)throw new u("at least one recipient must be added");if(t={deflateRaw:t==null?void 0:t.deflateRaw},this._recipients.length===1){let[c]=this._recipients,d=await new F(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(c.unprotectedHeader).encrypt(c.key,{...c.options,...t}),p={ciphertext:d.ciphertext,iv:d.iv,recipients:[{}],tag:d.tag};return d.aad&&(p.aad=d.aad),d.protected&&(p.protected=d.protected),d.unprotected&&(p.unprotected=d.unprotected),d.encrypted_key&&(p.recipients[0].encrypted_key=d.encrypted_key),d.header&&(p.recipients[0].header=d.header),p}let a;for(let c=0;c>3};case"RS256":case"RS384":case"RS512":return{hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return{hash:r,name:"ECDSA",namedCurve:t.namedCurve};case"EdDSA":return D()&&t.name==="NODE-ED25519"?{name:"NODE-ED25519",namedCurve:"NODE-ED25519"}:{name:t.name};default:throw new l(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}function ge(e,t,r){if(b(t))return ht(t,e,r),t;if(t instanceof Uint8Array){if(!e.startsWith("HS"))throw new TypeError(A(t,...y));return f.subtle.importKey("raw",t,{hash:`SHA-${e.slice(-3)}`,name:"HMAC"},!1,[r])}throw new TypeError(A(t,...y,"Uint8Array"))}var Wr=async(e,t,r,n)=>{let o=await ge(e,t,"verify");Q(e,o);let a=Ee(e,o.algorithm);try{return await f.subtle.verify(a,o,r,n)}catch(i){return!1}},Bt=Wr;async function Se(e,t,r){var n;if(!w(e))throw new h("Flattened JWS must be an object");if(e.protected===void 0&&e.header===void 0)throw new h('Flattened JWS must have either of the "protected" or "header" members');if(e.protected!==void 0&&typeof e.protected!="string")throw new h("JWS Protected Header incorrect type");if(e.payload===void 0)throw new h("JWS Payload missing");if(typeof e.signature!="string")throw new h("JWS Signature missing or incorrect type");if(e.header!==void 0&&!w(e.header))throw new h("JWS Unprotected Header incorrect type");let o={};if(e.protected)try{let k=S(e.protected);o=JSON.parse(v.decode(k))}catch(k){throw new h("JWS Protected Header is invalid")}if(!R(o,e.header))throw new h("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let a={...o,...e.header},i=U(h,new Map([["b64",!0]]),r==null?void 0:r.crit,o,a),s=!0;if(i.has("b64")&&(s=o.b64,typeof s!="boolean"))throw new h('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:c}=a;if(typeof c!="string"||!c)throw new h('JWS "alg" (Algorithm) Header Parameter missing or invalid');let d=r&&ye("algorithms",r.algorithms);if(d&&!d.has(c))throw new B('"alg" (Algorithm) Header Parameter not allowed');if(s){if(typeof e.payload!="string")throw new h("JWS Payload must be a string")}else if(typeof e.payload!="string"&&!(e.payload instanceof Uint8Array))throw new h("JWS Payload must be a string or an Uint8Array instance");let p=!1;typeof t=="function"&&(t=await t(o,e),p=!0),V(c,t,"verify");let m=W(E.encode((n=e.protected)!==null&&n!==void 0?n:""),E.encode("."),typeof e.payload=="string"?E.encode(e.payload):e.payload),K=S(e.signature);if(!await Bt(c,t,K,m))throw new Z;let x;s?x=S(e.payload):typeof e.payload=="string"?x=E.encode(e.payload):x=e.payload;let _={payload:x};return e.protected!==void 0&&(_.protectedHeader=o),e.header!==void 0&&(_.unprotectedHeader=e.header),p?{..._,key:t}:_}async function je(e,t,r){if(e instanceof Uint8Array&&(e=v.decode(e)),typeof e!="string")throw new h("Compact JWS must be a string or Uint8Array");let{0:n,1:o,2:a,length:i}=e.split(".");if(i!==3)throw new h("Invalid Compact JWS");let s=await Se({payload:o,protected:n,signature:a},t,r),c={payload:s.payload,protectedHeader:s.protectedHeader};return typeof t=="function"?{...c,key:s.key}:c}async function Jr(e,t,r){if(!w(e))throw new h("General JWS must be an object");if(!Array.isArray(e.signatures)||!e.signatures.every(w))throw new h("JWS Signatures missing or incorrect type");for(let n of e.signatures)try{return await Se({header:n.header,payload:e.payload,protected:n.protected,signature:n.signature},t,r)}catch(o){}throw new Z}var ie=e=>Math.floor(e.getTime()/1e3);var Tr=/^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i,se=e=>{let t=Tr.exec(e);if(!t)throw new TypeError("Invalid time period format");let r=parseFloat(t[1]);switch(t[2].toLowerCase()){case"sec":case"secs":case"second":case"seconds":case"s":return Math.round(r);case"minute":case"minutes":case"min":case"mins":case"m":return Math.round(r*60);case"hour":case"hours":case"hr":case"hrs":case"h":return Math.round(r*3600);case"day":case"days":case"d":return Math.round(r*86400);case"week":case"weeks":case"w":return Math.round(r*604800);default:return Math.round(r*31557600)}};var Lt=e=>e.toLowerCase().replace(/^application\//,""),Ir=(e,t)=>typeof e=="string"?t.includes(e):Array.isArray(e)?t.some(Set.prototype.has.bind(new Set(e))):!1,ce=(e,t,r={})=>{let{typ:n}=r;if(n&&(typeof e.typ!="string"||Lt(e.typ)!==Lt(n)))throw new J('unexpected "typ" JWT header value',"typ","check_failed");let o;try{o=JSON.parse(v.decode(t))}catch(m){}if(!w(o))throw new H("JWT Claims Set must be a top-level JSON object");let{issuer:a}=r;if(a&&!(Array.isArray(a)?a:[a]).includes(o.iss))throw new J('unexpected "iss" claim value',"iss","check_failed");let{subject:i}=r;if(i&&o.sub!==i)throw new J('unexpected "sub" claim value',"sub","check_failed");let{audience:s}=r;if(s&&!Ir(o.aud,typeof s=="string"?[s]:s))throw new J('unexpected "aud" claim value',"aud","check_failed");let c;switch(typeof r.clockTolerance){case"string":c=se(r.clockTolerance);break;case"number":c=r.clockTolerance;break;case"undefined":c=0;break;default:throw new TypeError("Invalid clockTolerance option type")}let{currentDate:d}=r,p=ie(d||new Date);if((o.iat!==void 0||r.maxTokenAge)&&typeof o.iat!="number")throw new J('"iat" claim must be a number',"iat","invalid");if(o.nbf!==void 0){if(typeof o.nbf!="number")throw new J('"nbf" claim must be a number',"nbf","invalid");if(o.nbf>p+c)throw new J('"nbf" claim timestamp check failed',"nbf","check_failed")}if(o.exp!==void 0){if(typeof o.exp!="number")throw new J('"exp" claim must be a number',"exp","invalid");if(o.exp<=p-c)throw new re('"exp" claim timestamp check failed',"exp","check_failed")}if(r.maxTokenAge){let m=p-o.iat,K=typeof r.maxTokenAge=="number"?r.maxTokenAge:se(r.maxTokenAge);if(m-c>K)throw new re('"iat" claim timestamp check failed (too far in the past)',"iat","check_failed");if(m<0-c)throw new J('"iat" claim timestamp check failed (it should be in the past)',"iat","check_failed")}return o};async function Dr(e,t,r){var n;let o=await je(e,t,r);if(!((n=o.protectedHeader.crit)===null||n===void 0)&&n.includes("b64")&&o.protectedHeader.b64===!1)throw new H("JWTs MUST NOT use unencoded payload");let i={payload:ce(o.protectedHeader,o.payload,r),protectedHeader:o.protectedHeader};return typeof t=="function"?{...i,key:o.key}:i}async function Rr(e,t,r){let n=await Xe(e,t,r),o=ce(n.protectedHeader,n.plaintext,r),{protectedHeader:a}=n;if(a.iss!==void 0&&a.iss!==o.iss)throw new J('replicated "iss" claim header parameter mismatch',"iss","mismatch");if(a.sub!==void 0&&a.sub!==o.sub)throw new J('replicated "sub" claim header parameter mismatch',"sub","mismatch");if(a.aud!==void 0&&JSON.stringify(a.aud)!==JSON.stringify(o.aud))throw new J('replicated "aud" claim header parameter mismatch',"aud","mismatch");let i={payload:o,protectedHeader:a};return typeof t=="function"?{...i,key:n.key}:i}var Ae=class{constructor(t){this._flattened=new F(t)}setContentEncryptionKey(t){return this._flattened.setContentEncryptionKey(t),this}setInitializationVector(t){return this._flattened.setInitializationVector(t),this}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}setKeyManagementParameters(t){return this._flattened.setKeyManagementParameters(t),this}async encrypt(t,r){let n=await this._flattened.encrypt(t,r);return[n.protected,n.encrypted_key,n.iv,n.ciphertext,n.tag].join(".")}};var Or=async(e,t,r)=>{let n=await ge(e,t,"sign");Q(e,n);let o=await f.subtle.sign(Ee(e,n.algorithm),n,r);return new Uint8Array(o)},$t=Or;var ee=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("payload must be an instance of Uint8Array");this._payload=t}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}async sign(t,r){if(!this._protectedHeader&&!this._unprotectedHeader)throw new h("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");if(!R(this._protectedHeader,this._unprotectedHeader))throw new h("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader},o=U(h,new Map([["b64",!0]]),r==null?void 0:r.crit,this._protectedHeader,n),a=!0;if(o.has("b64")&&(a=this._protectedHeader.b64,typeof a!="boolean"))throw new h('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:i}=n;if(typeof i!="string"||!i)throw new h('JWS "alg" (Algorithm) Header Parameter missing or invalid');V(i,t,"sign");let s=this._payload;a&&(s=E.encode(g(s)));let c;this._protectedHeader?c=E.encode(g(JSON.stringify(this._protectedHeader))):c=E.encode("");let d=W(c,E.encode("."),s),p=await $t(i,t,d),m={signature:g(p),payload:""};return a&&(m.payload=v.decode(s)),this._unprotectedHeader&&(m.header=this._unprotectedHeader),this._protectedHeader&&(m.protected=v.decode(c)),m}};var be=class{constructor(t){this._flattened=new ee(t)}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}async sign(t,r){let n=await this._flattened.sign(t,r);if(n.payload===void 0)throw new TypeError("use the flattened module for creating JWS with b64: false");return`${n.protected}.${n.payload}.${n.signature}`}};var et=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n}setProtectedHeader(t){if(this.protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this.protectedHeader=t,this}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addSignature(...t){return this.parent.addSignature(...t)}sign(...t){return this.parent.sign(...t)}done(){return this.parent}},tt=class{constructor(t){this._signatures=[],this._payload=t}addSignature(t,r){let n=new et(this,t,r);return this._signatures.push(n),n}async sign(){if(!this._signatures.length)throw new h("at least one signature must be added");let t={signatures:[],payload:""};for(let r=0;r{if(typeof e!="string"||!e)throw new de(`${t} missing or invalid`)};async function Gt(e,t){if(!w(e))throw new TypeError("JWK must be an object");if(t!=null||(t="sha256"),t!=="sha256"&&t!=="sha384"&&t!=="sha512")throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');let r;switch(e.kty){case"EC":X(e.crv,'"crv" (Curve) Parameter'),X(e.x,'"x" (X Coordinate) Parameter'),X(e.y,'"y" (Y Coordinate) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x,y:e.y};break;case"OKP":X(e.crv,'"crv" (Subtype of Key Pair) Parameter'),X(e.x,'"x" (Public Key) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x};break;case"RSA":X(e.e,'"e" (Exponent) Parameter'),X(e.n,'"n" (Modulus) Parameter'),r={e:e.e,kty:e.kty,n:e.n};break;case"oct":X(e.k,'"k" (Key Value) Parameter'),r={k:e.k,kty:e.kty};break;default:throw new l('"kty" (Key Type) Parameter missing or unsupported')}let n=E.encode(JSON.stringify(r));return g(await Ke(t,n))}async function Ur(e,t){t!=null||(t="sha256");let r=await Gt(e,t);return`urn:ietf:params:oauth:jwk-thumbprint:sha-${t.slice(-3)}:${r}`}async function Mr(e,t){let r={...e,...t==null?void 0:t.header};if(!w(r.jwk))throw new h('"jwk" (JSON Web Key) Header Parameter must be a JSON object');let n=await j({...r.jwk,ext:!0},r.alg,!0);if(n instanceof Uint8Array||n.type!=="public")throw new h('"jwk" (JSON Web Key) Header Parameter must be a public key');return n}function Nr(e){switch(typeof e=="string"&&e.slice(0,2)){case"RS":case"PS":return"RSA";case"ES":return"EC";case"Ed":return"OKP";default:throw new l('Unsupported "alg" value for a JSON Web Key Set')}}function ot(e){return e&&typeof e=="object"&&Array.isArray(e.keys)&&e.keys.every(kr)}function kr(e){return w(e)}function Br(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}var _e=class{constructor(t){if(this._cached=new WeakMap,!ot(t))throw new L("JSON Web Key Set malformed");this._jwks=Br(t)}async getKey(t,r){let{alg:n,kid:o}={...t,...r==null?void 0:r.header},a=Nr(n),i=this._jwks.keys.filter(d=>{let p=a===d.kty;if(p&&typeof o=="string"&&(p=o===d.kid),p&&typeof d.alg=="string"&&(p=n===d.alg),p&&typeof d.use=="string"&&(p=d.use==="sig"),p&&Array.isArray(d.key_ops)&&(p=d.key_ops.includes("verify")),p&&n==="EdDSA"&&(p=d.crv==="Ed25519"||d.crv==="Ed448"),p)switch(n){case"ES256":p=d.crv==="P-256";break;case"ES256K":p=d.crv==="secp256k1";break;case"ES384":p=d.crv==="P-384";break;case"ES512":p=d.crv==="P-521";break}return p}),{0:s,length:c}=i;if(c===0)throw new q;if(c!==1){let d=new pe,{_cached:p}=this;throw d[Symbol.asyncIterator]=async function*(){for(let m of i)try{yield await Vt(p,m,n)}catch(K){continue}},d}return Vt(this._cached,s,n)}};async function Vt(e,t,r){let n=e.get(t)||e.set(t,{}).get(t);if(n[r]===void 0){let o=await j({...t,ext:!0},r);if(o instanceof Uint8Array||o.type!=="public")throw new L("JSON Web Key Set members must be public keys");n[r]=o}return n[r]}function Lr(e){let t=new _e(e);return async function(r,n){return t.getKey(r,n)}}var $r=async(e,t,r)=>{let n,o,a=!1;typeof AbortController=="function"&&(n=new AbortController,o=setTimeout(()=>{a=!0,n.abort()},t));let i=await fetch(e.href,{signal:n?n.signal:void 0,redirect:"manual",headers:r.headers}).catch(s=>{throw a?new ue:s});if(o!==void 0&&clearTimeout(o),i.status!==200)throw new C("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await i.json()}catch(s){throw new C("Failed to parse the JSON Web Key Set HTTP response as JSON")}},Ft=$r;var at=class extends _e{constructor(t,r){if(super({keys:[]}),this._jwks=void 0,!(t instanceof URL))throw new TypeError("url must be an instance of URL");this._url=new URL(t.href),this._options={agent:r==null?void 0:r.agent,headers:r==null?void 0:r.headers},this._timeoutDuration=typeof(r==null?void 0:r.timeoutDuration)=="number"?r==null?void 0:r.timeoutDuration:5e3,this._cooldownDuration=typeof(r==null?void 0:r.cooldownDuration)=="number"?r==null?void 0:r.cooldownDuration:3e4,this._cacheMaxAge=typeof(r==null?void 0:r.cacheMaxAge)=="number"?r==null?void 0:r.cacheMaxAge:6e5}coolingDown(){return typeof this._jwksTimestamp=="number"?Date.now(){if(!ot(t))throw new L("JSON Web Key Set malformed");this._jwks={keys:t.keys},this._jwksTimestamp=Date.now(),this._pendingFetch=void 0}).catch(t=>{throw this._pendingFetch=void 0,t})),await this._pendingFetch}};function Gr(e,t){let r=new at(e,t);return async function(n,o){return r.getKey(n,o)}}var it=class extends z{encode(){let t=g(JSON.stringify({alg:"none"})),r=g(JSON.stringify(this._payload));return`${t}.${r}.`}static decode(t,r){if(typeof t!="string")throw new H("Unsecured JWT must be a string");let{0:n,1:o,2:a,length:i}=t.split(".");if(i!==3||a!=="")throw new H("Invalid Unsecured JWT");let s;try{if(s=JSON.parse(v.decode(S(n))),s.alg!=="none")throw new Error}catch(d){throw new H("Invalid Unsecured JWT")}return{payload:ce(s,S(o),r),header:s}}};var zt={};ct(zt,{decode:()=>ve,encode:()=>Vr});var Vr=g,ve=S;function Fr(e){let t;if(typeof e=="string"){let r=e.split(".");(r.length===3||r.length===5)&&([t]=r)}else if(typeof e=="object"&&e)if("protected"in e)t=e.protected;else throw new TypeError("Token does not contain a Protected Header");try{if(typeof t!="string"||!t)throw new Error;let r=JSON.parse(v.decode(ve(t)));if(!w(r))throw new Error;return r}catch(r){throw new TypeError("Invalid Token or Protected Header formatting")}}function zr(e){if(typeof e!="string")throw new H("JWTs must use Compact JWS serialization, JWT must be a string");let{1:t,length:r}=e.split(".");if(r===5)throw new H("Only JWTs using Compact JWS serialization can be decoded");if(r!==3)throw new H("Invalid JWT");if(!t)throw new H("JWTs must contain a payload");let n;try{n=ve(t)}catch(a){throw new H("Failed to parse the base64url encoded payload")}let o;try{o=JSON.parse(v.decode(n))}catch(a){throw new H("Failed to parse the decoded payload as JSON")}if(!w(o))throw new H("Invalid JWT Claims Set");return o}async function Xt(e,t){var r;let n,o,a;switch(e){case"HS256":case"HS384":case"HS512":n=parseInt(e.slice(-3),10),o={name:"HMAC",hash:`SHA-${n}`,length:n},a=["sign","verify"];break;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return n=parseInt(e.slice(-3),10),$(new Uint8Array(n>>3));case"A128KW":case"A192KW":case"A256KW":n=parseInt(e.slice(1,4),10),o={name:"AES-KW",length:n},a=["wrapKey","unwrapKey"];break;case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":case"A128GCM":case"A192GCM":case"A256GCM":n=parseInt(e.slice(1,4),10),o={name:"AES-GCM",length:n},a=["encrypt","decrypt"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return f.subtle.generateKey(o,(r=t==null?void 0:t.extractable)!==null&&r!==void 0?r:!1,a)}function st(e){var t;let r=(t=e==null?void 0:e.modulusLength)!==null&&t!==void 0?t:2048;if(typeof r!="number"||r<2048)throw new l("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used");return r}async function Yt(e,t){var r,n,o,a;let i,s;switch(e){case"PS256":case"PS384":case"PS512":i={name:"RSA-PSS",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["sign","verify"];break;case"RS256":case"RS384":case"RS512":i={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["sign","verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":i={name:"RSA-OAEP",hash:`SHA-${parseInt(e.slice(-3),10)||1}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["decrypt","unwrapKey","encrypt","wrapKey"];break;case"ES256":i={name:"ECDSA",namedCurve:"P-256"},s=["sign","verify"];break;case"ES384":i={name:"ECDSA",namedCurve:"P-384"},s=["sign","verify"];break;case"ES512":i={name:"ECDSA",namedCurve:"P-521"},s=["sign","verify"];break;case"EdDSA":s=["sign","verify"];let c=(r=t==null?void 0:t.crv)!==null&&r!==void 0?r:"Ed25519";switch(c){case"Ed25519":case"Ed448":i={name:c};break;default:throw new l("Invalid or unsupported crv option provided")}break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{s=["deriveKey","deriveBits"];let d=(n=t==null?void 0:t.crv)!==null&&n!==void 0?n:"P-256";switch(d){case"P-256":case"P-384":case"P-521":{i={name:"ECDH",namedCurve:d};break}case"X25519":case"X448":i={name:d};break;default:throw new l("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448")}break}default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}try{return await f.subtle.generateKey(i,(o=t==null?void 0:t.extractable)!==null&&o!==void 0?o:!1,s)}catch(c){if(i.name==="Ed25519"&&(c==null?void 0:c.name)==="NotSupportedError"&&D())return i={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.generateKey(i,(a=t==null?void 0:t.extractable)!==null&&a!==void 0?a:!1,s);throw c}}async function Xr(e,t){return Yt(e,t)}async function Yr(e,t){return Xt(e,t)}export{Ae as CompactEncrypt,be as CompactSign,Mr as EmbeddedJWK,nt as EncryptJWT,F as FlattenedEncrypt,ee as FlattenedSign,Qe as GeneralEncrypt,tt as GeneralSign,rt as SignJWT,it as UnsecuredJWT,zt as base64url,Gt as calculateJwkThumbprint,Ur as calculateJwkThumbprintUri,Xe as compactDecrypt,je as compactVerify,Lr as createLocalJWKSet,Gr as createRemoteJWKSet,zr as decodeJwt,Fr as decodeProtectedHeader,ft as errors,Ye as exportJWK,Cr as exportPKCS8,Hr as exportSPKI,we as flattenedDecrypt,Se as flattenedVerify,Kr as generalDecrypt,Jr as generalVerify,Xr as generateKeyPair,Yr as generateSecret,j as importJWK,hr as importPKCS8,lr as importSPKI,mr as importX509,Rr as jwtDecrypt,Dr as jwtVerify}; diff --git a/dist/browser/index.js b/dist/browser/index.js new file mode 100644 index 0000000000..00e8cee4cd --- /dev/null +++ b/dist/browser/index.js @@ -0,0 +1,31 @@ +export { compactDecrypt } from './jwe/compact/decrypt.js'; +export { flattenedDecrypt } from './jwe/flattened/decrypt.js'; +export { generalDecrypt } from './jwe/general/decrypt.js'; +export { GeneralEncrypt } from './jwe/general/encrypt.js'; +export { compactVerify } from './jws/compact/verify.js'; +export { flattenedVerify } from './jws/flattened/verify.js'; +export { generalVerify } from './jws/general/verify.js'; +export { jwtVerify } from './jwt/verify.js'; +export { jwtDecrypt } from './jwt/decrypt.js'; +export { CompactEncrypt } from './jwe/compact/encrypt.js'; +export { FlattenedEncrypt } from './jwe/flattened/encrypt.js'; +export { CompactSign } from './jws/compact/sign.js'; +export { FlattenedSign } from './jws/flattened/sign.js'; +export { GeneralSign } from './jws/general/sign.js'; +export { SignJWT } from './jwt/sign.js'; +export { EncryptJWT } from './jwt/encrypt.js'; +export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js'; +export { EmbeddedJWK } from './jwk/embedded.js'; +export { createLocalJWKSet } from './jwks/local.js'; +export { createRemoteJWKSet } from './jwks/remote.js'; +export { UnsecuredJWT } from './jwt/unsecured.js'; +export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js'; +export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js'; +export { decodeProtectedHeader } from './util/decode_protected_header.js'; +export { decodeJwt } from './util/decode_jwt.js'; +import * as errors_1 from './util/errors.js'; +export { errors_1 as errors }; +export { generateKeyPair } from './key/generate_key_pair.js'; +export { generateSecret } from './key/generate_secret.js'; +import * as base64url_1 from './util/base64url.js'; +export { base64url_1 as base64url }; diff --git a/dist/browser/index.umd.js b/dist/browser/index.umd.js new file mode 100644 index 0000000000..0dbff272dc --- /dev/null +++ b/dist/browser/index.umd.js @@ -0,0 +1,3368 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jose = {})); +})(this, (function (exports) { 'use strict'; + + var __defProp = Object.defineProperty; + var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); + }; + + // dist/browser/runtime/webcrypto.js + var webcrypto_default = crypto; + var isCryptoKey = (key) => key instanceof CryptoKey; + + // dist/browser/runtime/digest.js + var digest = async (algorithm, data) => { + const subtleDigest = `SHA-${algorithm.slice(-3)}`; + return new Uint8Array(await webcrypto_default.subtle.digest(subtleDigest, data)); + }; + var digest_default = digest; + + // dist/browser/lib/buffer_utils.js + 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; + buffers.forEach((buffer) => { + buf.set(buffer, i); + i += buffer.length; + }); + return buf; + } + function p2s(alg, p2sInput) { + return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput); + } + 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; + } + function uint32be(value) { + const buf = new Uint8Array(4); + writeUInt32BE(buf, value); + return buf; + } + function lengthAndInput(input) { + return concat(uint32be(input.length), input); + } + async function concatKdf(secret, bits, value) { + const iterations = Math.ceil((bits >> 3) / 32); + const res = new Uint8Array(iterations * 32); + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length); + buf.set(uint32be(iter + 1)); + buf.set(secret, 4); + buf.set(value, 4 + secret.length); + res.set(await digest_default("sha256", buf), iter * 32); + } + return res.slice(0, bits >> 3); + } + + // dist/browser/runtime/base64url.js + var encodeBase64 = (input) => { + let unencoded = input; + if (typeof unencoded === "string") { + unencoded = encoder.encode(unencoded); + } + const CHUNK_SIZE = 32768; + const arr = []; + for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) { + arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))); + } + return btoa(arr.join("")); + }; + var encode = (input) => { + return encodeBase64(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); + }; + var decodeBase64 = (encoded) => { + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; + }; + var decode = (input) => { + let encoded = input; + if (encoded instanceof Uint8Array) { + encoded = decoder.decode(encoded); + } + encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""); + try { + return decodeBase64(encoded); + } catch (_a) { + throw new TypeError("The input to be decoded is not correctly encoded."); + } + }; + + // dist/browser/util/errors.js + var errors_exports = {}; + __export(errors_exports, { + JOSEAlgNotAllowed: () => JOSEAlgNotAllowed, + JOSEError: () => JOSEError, + JOSENotSupported: () => JOSENotSupported, + JWEDecryptionFailed: () => JWEDecryptionFailed, + JWEInvalid: () => JWEInvalid, + JWKInvalid: () => JWKInvalid, + JWKSInvalid: () => JWKSInvalid, + JWKSMultipleMatchingKeys: () => JWKSMultipleMatchingKeys, + JWKSNoMatchingKey: () => JWKSNoMatchingKey, + JWKSTimeout: () => JWKSTimeout, + JWSInvalid: () => JWSInvalid, + JWSSignatureVerificationFailed: () => JWSSignatureVerificationFailed, + JWTClaimValidationFailed: () => JWTClaimValidationFailed, + JWTExpired: () => JWTExpired, + JWTInvalid: () => JWTInvalid + }); + var JOSEError = class extends Error { + static get code() { + return "ERR_JOSE_GENERIC"; + } + constructor(message2) { + var _a; + super(message2); + this.code = "ERR_JOSE_GENERIC"; + this.name = this.constructor.name; + (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); + } + }; + var JWTClaimValidationFailed = class extends JOSEError { + static get code() { + return "ERR_JWT_CLAIM_VALIDATION_FAILED"; + } + constructor(message2, claim = "unspecified", reason = "unspecified") { + super(message2); + this.code = "ERR_JWT_CLAIM_VALIDATION_FAILED"; + this.claim = claim; + this.reason = reason; + } + }; + var JWTExpired = class extends JOSEError { + static get code() { + return "ERR_JWT_EXPIRED"; + } + constructor(message2, claim = "unspecified", reason = "unspecified") { + super(message2); + this.code = "ERR_JWT_EXPIRED"; + this.claim = claim; + this.reason = reason; + } + }; + var JOSEAlgNotAllowed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JOSE_ALG_NOT_ALLOWED"; + } + static get code() { + return "ERR_JOSE_ALG_NOT_ALLOWED"; + } + }; + var JOSENotSupported = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JOSE_NOT_SUPPORTED"; + } + static get code() { + return "ERR_JOSE_NOT_SUPPORTED"; + } + }; + var JWEDecryptionFailed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWE_DECRYPTION_FAILED"; + this.message = "decryption operation failed"; + } + static get code() { + return "ERR_JWE_DECRYPTION_FAILED"; + } + }; + var JWEInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWE_INVALID"; + } + static get code() { + return "ERR_JWE_INVALID"; + } + }; + var JWSInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWS_INVALID"; + } + static get code() { + return "ERR_JWS_INVALID"; + } + }; + var JWTInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWT_INVALID"; + } + static get code() { + return "ERR_JWT_INVALID"; + } + }; + var JWKInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWK_INVALID"; + } + static get code() { + return "ERR_JWK_INVALID"; + } + }; + var JWKSInvalid = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_INVALID"; + } + static get code() { + return "ERR_JWKS_INVALID"; + } + }; + var JWKSNoMatchingKey = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_NO_MATCHING_KEY"; + this.message = "no applicable key found in the JSON Web Key Set"; + } + static get code() { + return "ERR_JWKS_NO_MATCHING_KEY"; + } + }; + var JWKSMultipleMatchingKeys = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS"; + this.message = "multiple matching keys found in the JSON Web Key Set"; + } + static get code() { + return "ERR_JWKS_MULTIPLE_MATCHING_KEYS"; + } + }; + var JWKSTimeout = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWKS_TIMEOUT"; + this.message = "request timed out"; + } + static get code() { + return "ERR_JWKS_TIMEOUT"; + } + }; + var JWSSignatureVerificationFailed = class extends JOSEError { + constructor() { + super(...arguments); + this.code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"; + this.message = "signature verification failed"; + } + static get code() { + return "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"; + } + }; + + // dist/browser/runtime/random.js + var random_default = webcrypto_default.getRandomValues.bind(webcrypto_default); + + // dist/browser/lib/iv.js + 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) => random_default(new Uint8Array(bitLength(alg) >> 3)); + + // dist/browser/lib/check_iv_length.js + var checkIvLength = (enc, iv) => { + if (iv.length << 3 !== bitLength(enc)) { + throw new JWEInvalid("Invalid Initialization Vector length"); + } + }; + var check_iv_length_default = checkIvLength; + + // dist/browser/runtime/check_cek_length.js + var checkCekLength = (cek, expected) => { + const actual = cek.byteLength << 3; + if (actual !== expected) { + throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } + }; + var check_cek_length_default = checkCekLength; + + // dist/browser/runtime/timing_safe_equal.js + var timingSafeEqual = (a, b) => { + if (!(a instanceof Uint8Array)) { + throw new TypeError("First argument must be a buffer"); + } + if (!(b instanceof Uint8Array)) { + throw new TypeError("Second argument must be a buffer"); + } + if (a.length !== b.length) { + throw new TypeError("Input buffers must have the same length"); + } + const len = a.length; + let out = 0; + let i = -1; + while (++i < len) { + out |= a[i] ^ b[i]; + } + return out === 0; + }; + var timing_safe_equal_default = timingSafeEqual; + + // dist/browser/runtime/env.js + function isCloudflareWorkers() { + return typeof WebSocketPair !== "undefined" || typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime !== "undefined" && EdgeRuntime === "vercel"; + } + + // dist/browser/lib/crypto_key.js + 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 getNamedCurve(alg) { + switch (alg) { + case "ES256": + return "P-256"; + case "ES384": + return "P-384"; + case "ES512": + return "P-521"; + default: + throw new Error("unreachable"); + } + } + function checkUsage(key, usages) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = "CryptoKey does not support this operation, its usages must include "; + if (usages.length > 2) { + const last = usages.pop(); + msg += `one of ${usages.join(", ")}, or ${last}.`; + } else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.`; + } else { + msg += `${usages[0]}.`; + } + throw new TypeError(msg); + } + } + function checkSigCryptoKey(key, alg, ...usages) { + switch (alg) { + case "HS256": + case "HS384": + case "HS512": { + if (!isAlgorithm(key.algorithm, "HMAC")) + throw unusable("HMAC"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "RS256": + case "RS384": + case "RS512": { + if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) + throw unusable("RSASSA-PKCS1-v1_5"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "PS256": + case "PS384": + case "PS512": { + if (!isAlgorithm(key.algorithm, "RSA-PSS")) + throw unusable("RSA-PSS"); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, "algorithm.hash"); + break; + } + case "EdDSA": { + if (key.algorithm.name !== "Ed25519" && key.algorithm.name !== "Ed448") { + if (isCloudflareWorkers()) { + if (isAlgorithm(key.algorithm, "NODE-ED25519")) + break; + throw unusable("Ed25519, Ed448, or NODE-ED25519"); + } + throw unusable("Ed25519 or Ed448"); + } + break; + } + case "ES256": + case "ES384": + case "ES512": { + if (!isAlgorithm(key.algorithm, "ECDSA")) + throw unusable("ECDSA"); + const expected = getNamedCurve(alg); + const actual = key.algorithm.namedCurve; + if (actual !== expected) + throw unusable(expected, "algorithm.namedCurve"); + break; + } + default: + throw new TypeError("CryptoKey does not support this operation"); + } + checkUsage(key, usages); + } + function checkEncCryptoKey(key, alg, ...usages) { + 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": + case "X448": + break; + default: + throw unusable("ECDH, X25519, or X448"); + } + 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, usages); + } + + // dist/browser/lib/invalid_key_input.js + function message(msg, actual, ...types2) { + if (types2.length > 2) { + const last = types2.pop(); + msg += `one of type ${types2.join(", ")}, or ${last}.`; + } else if (types2.length === 2) { + msg += `one of type ${types2[0]} or ${types2[1]}.`; + } else { + msg += `of type ${types2[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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}`; + } + } + return msg; + } + var invalid_key_input_default = (actual, ...types2) => { + return message("Key must be ", actual, ...types2); + }; + function withAlg(alg, actual, ...types2) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types2); + } + + // dist/browser/runtime/is_key_like.js + var is_key_like_default = (key) => { + return isCryptoKey(key); + }; + var types = ["CryptoKey"]; + + // dist/browser/runtime/decrypt.js + async function cbcDecrypt(enc, cek, ciphertext, iv, tag, 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 webcrypto_default.subtle.importKey("raw", cek.subarray(keySize >> 3), "AES-CBC", false, ["decrypt"]); + const macKey = await webcrypto_default.subtle.importKey("raw", cek.subarray(0, keySize >> 3), { + hash: `SHA-${keySize << 1}`, + name: "HMAC" + }, false, ["sign"]); + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const expectedTag = new Uint8Array((await webcrypto_default.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)); + let macCheckPassed; + try { + macCheckPassed = timing_safe_equal_default(tag, expectedTag); + } catch (_a) { + } + if (!macCheckPassed) { + throw new JWEDecryptionFailed(); + } + let plaintext; + try { + plaintext = new Uint8Array(await webcrypto_default.subtle.decrypt({ iv, name: "AES-CBC" }, encKey, ciphertext)); + } catch (_b) { + } + if (!plaintext) { + throw new JWEDecryptionFailed(); + } + return plaintext; + } + async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) { + let encKey; + if (cek instanceof Uint8Array) { + encKey = await webcrypto_default.subtle.importKey("raw", cek, "AES-GCM", false, ["decrypt"]); + } else { + checkEncCryptoKey(cek, enc, "decrypt"); + encKey = cek; + } + try { + return new Uint8Array(await webcrypto_default.subtle.decrypt({ + additionalData: aad, + iv, + name: "AES-GCM", + tagLength: 128 + }, encKey, concat(ciphertext, tag))); + } catch (_a) { + throw new JWEDecryptionFailed(); + } + } + var decrypt = async (enc, cek, ciphertext, iv, tag, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalid_key_input_default(cek, ...types, "Uint8Array")); + } + check_iv_length_default(enc, iv); + 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 cbcDecrypt(enc, cek, ciphertext, iv, tag, aad); + case "A128GCM": + case "A192GCM": + case "A256GCM": + if (cek instanceof Uint8Array) + check_cek_length_default(cek, parseInt(enc.slice(1, 4), 10)); + return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad); + default: + throw new JOSENotSupported("Unsupported JWE Content Encryption Algorithm"); + } + }; + var decrypt_default = decrypt; + + // dist/browser/runtime/zlib.js + var inflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.'); + }; + var deflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.'); + }; + + // dist/browser/lib/is_disjoint.js + var isDisjoint = (...headers) => { + const sources = headers.filter(Boolean); + if (sources.length === 0 || sources.length === 1) { + return true; + } + let acc; + for (const header of sources) { + const parameters = Object.keys(header); + if (!acc || acc.size === 0) { + acc = new Set(parameters); + continue; + } + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false; + } + acc.add(parameter); + } + } + return true; + }; + var is_disjoint_default = isDisjoint; + + // dist/browser/lib/is_object.js + function isObjectLike(value) { + return typeof value === "object" && value !== null; + } + function isObject(input) { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") { + return false; + } + if (Object.getPrototypeOf(input) === null) { + return true; + } + let proto = input; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(input) === proto; + } + + // dist/browser/runtime/bogus.js + var bogusWebCrypto = [ + { hash: "SHA-256", name: "HMAC" }, + true, + ["sign"] + ]; + var bogus_default = bogusWebCrypto; + + // dist/browser/runtime/aeskw.js + function checkKeySize(key, alg) { + if (key.algorithm.length !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`); + } + } + function getCryptoKey(key, alg, usage) { + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + return webcrypto_default.subtle.importKey("raw", key, "AES-KW", true, [usage]); + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); + } + var wrap = async (alg, key, cek) => { + const cryptoKey = await getCryptoKey(key, alg, "wrapKey"); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await webcrypto_default.subtle.importKey("raw", cek, ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW")); + }; + var unwrap = async (alg, key, encryptedKey) => { + const cryptoKey = await getCryptoKey(key, alg, "unwrapKey"); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await webcrypto_default.subtle.unwrapKey("raw", encryptedKey, cryptoKey, "AES-KW", ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.exportKey("raw", cryptoKeyCek)); + }; + + // dist/browser/runtime/ecdhes.js + async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { + if (!isCryptoKey(publicKey)) { + throw new TypeError(invalid_key_input_default(publicKey, ...types)); + } + checkEncCryptoKey(publicKey, "ECDH"); + if (!isCryptoKey(privateKey)) { + throw new TypeError(invalid_key_input_default(privateKey, ...types)); + } + checkEncCryptoKey(privateKey, "ECDH", "deriveBits"); + const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); + let length; + if (publicKey.algorithm.name === "X25519") { + length = 256; + } else if (publicKey.algorithm.name === "X448") { + length = 448; + } else { + length = Math.ceil(parseInt(publicKey.algorithm.namedCurve.substr(-3), 10) / 8) << 3; + } + const sharedSecret = new Uint8Array(await webcrypto_default.subtle.deriveBits({ + name: publicKey.algorithm.name, + public: publicKey + }, privateKey, length)); + return concatKdf(sharedSecret, keyLength, value); + } + async function generateEpk(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return webcrypto_default.subtle.generateKey(key.algorithm, true, ["deriveBits"]); + } + function ecdhAllowed(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return ["P-256", "P-384", "P-521"].includes(key.algorithm.namedCurve) || key.algorithm.name === "X25519" || key.algorithm.name === "X448"; + } + + // dist/browser/lib/check_p2s.js + function checkP2s(p2s2) { + if (!(p2s2 instanceof Uint8Array) || p2s2.length < 8) { + throw new JWEInvalid("PBES2 Salt Input must be 8 or more octets"); + } + } + + // dist/browser/runtime/pbes2kw.js + function getCryptoKey2(key, alg) { + if (key instanceof Uint8Array) { + return webcrypto_default.subtle.importKey("raw", key, "PBKDF2", false, ["deriveBits"]); + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, "deriveBits", "deriveKey"); + return key; + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); + } + async function deriveKey2(p2s2, alg, p2c, key) { + checkP2s(p2s2); + const salt = p2s(alg, p2s2); + const keylen = parseInt(alg.slice(13, 16), 10); + const subtleAlg = { + hash: `SHA-${alg.slice(8, 11)}`, + iterations: p2c, + name: "PBKDF2", + salt + }; + const wrapAlg = { + length: keylen, + name: "AES-KW" + }; + const cryptoKey = await getCryptoKey2(key, alg); + if (cryptoKey.usages.includes("deriveBits")) { + return new Uint8Array(await webcrypto_default.subtle.deriveBits(subtleAlg, cryptoKey, keylen)); + } + if (cryptoKey.usages.includes("deriveKey")) { + return webcrypto_default.subtle.deriveKey(subtleAlg, cryptoKey, wrapAlg, false, ["wrapKey", "unwrapKey"]); + } + throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"'); + } + var encrypt = async (alg, key, cek, p2c = 2048, p2s2 = random_default(new Uint8Array(16))) => { + const derived = await deriveKey2(p2s2, alg, p2c, key); + const encryptedKey = await wrap(alg.slice(-6), derived, cek); + return { encryptedKey, p2c, p2s: encode(p2s2) }; + }; + var decrypt2 = async (alg, key, encryptedKey, p2c, p2s2) => { + const derived = await deriveKey2(p2s2, alg, p2c, key); + return unwrap(alg.slice(-6), derived, encryptedKey); + }; + + // dist/browser/runtime/subtle_rsaes.js + function subtleRsaEs(alg) { + switch (alg) { + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + return "RSA-OAEP"; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + } + + // dist/browser/runtime/check_key_length.js + var check_key_length_default = (alg, key) => { + if (alg.startsWith("RS") || alg.startsWith("PS")) { + const { modulusLength } = key.algorithm; + if (typeof modulusLength !== "number" || modulusLength < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); + } + } + }; + + // dist/browser/runtime/rsaes.js + var encrypt2 = async (alg, key, cek) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + checkEncCryptoKey(key, alg, "encrypt", "wrapKey"); + check_key_length_default(alg, key); + if (key.usages.includes("encrypt")) { + return new Uint8Array(await webcrypto_default.subtle.encrypt(subtleRsaEs(alg), key, cek)); + } + if (key.usages.includes("wrapKey")) { + const cryptoKeyCek = await webcrypto_default.subtle.importKey("raw", cek, ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.wrapKey("raw", cryptoKeyCek, key, subtleRsaEs(alg))); + } + throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation'); + }; + var decrypt3 = async (alg, key, encryptedKey) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + checkEncCryptoKey(key, alg, "decrypt", "unwrapKey"); + check_key_length_default(alg, key); + if (key.usages.includes("decrypt")) { + return new Uint8Array(await webcrypto_default.subtle.decrypt(subtleRsaEs(alg), key, encryptedKey)); + } + if (key.usages.includes("unwrapKey")) { + const cryptoKeyCek = await webcrypto_default.subtle.unwrapKey("raw", encryptedKey, key, subtleRsaEs(alg), ...bogus_default); + return new Uint8Array(await webcrypto_default.subtle.exportKey("raw", cryptoKeyCek)); + } + throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation'); + }; + + // dist/browser/lib/cek.js + function bitLength2(alg) { + switch (alg) { + case "A128GCM": + return 128; + case "A192GCM": + return 192; + case "A256GCM": + case "A128CBC-HS256": + return 256; + case "A192CBC-HS384": + return 384; + case "A256CBC-HS512": + return 512; + default: + throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } + } + var cek_default = (alg) => random_default(new Uint8Array(bitLength2(alg) >> 3)); + + // dist/browser/lib/format_pem.js + var format_pem_default = (b64, descriptor) => { + const newlined = (b64.match(/.{1,64}/g) || []).join("\n"); + return `-----BEGIN ${descriptor}----- +${newlined} +-----END ${descriptor}-----`; + }; + + // dist/browser/runtime/asn1.js + var genericExport = async (keyType, keyFormat, key) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + if (!key.extractable) { + throw new TypeError("CryptoKey is not extractable"); + } + if (key.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`); + } + return format_pem_default(encodeBase64(new Uint8Array(await webcrypto_default.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`); + }; + var toSPKI = (key) => { + return genericExport("public", "spki", key); + }; + var toPKCS8 = (key) => { + return genericExport("private", "pkcs8", key); + }; + var findOid = (keyData, oid, from = 0) => { + if (from === 0) { + oid.unshift(oid.length); + oid.unshift(6); + } + let i = keyData.indexOf(oid[0], from); + if (i === -1) + return false; + const sub = keyData.subarray(i, i + oid.length); + if (sub.length !== oid.length) + return false; + return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1); + }; + var getNamedCurve2 = (keyData) => { + switch (true) { + case findOid(keyData, [42, 134, 72, 206, 61, 3, 1, 7]): + return "P-256"; + case findOid(keyData, [43, 129, 4, 0, 34]): + return "P-384"; + case findOid(keyData, [43, 129, 4, 0, 35]): + return "P-521"; + case findOid(keyData, [43, 101, 110]): + return "X25519"; + case findOid(keyData, [43, 101, 111]): + return "X448"; + case findOid(keyData, [43, 101, 112]): + return "Ed25519"; + case findOid(keyData, [43, 101, 113]): + return "Ed448"; + default: + throw new JOSENotSupported("Invalid or unsupported EC Key Curve or OKP Key Sub Type"); + } + }; + var genericImport = async (replace, keyFormat, pem, alg, options) => { + var _a, _b; + let algorithm; + let keyUsages; + const keyData = new Uint8Array(atob(pem.replace(replace, "")).split("").map((c) => c.charCodeAt(0))); + const isPublic = keyFormat === "spki"; + switch (alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { name: "RSA-PSS", hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}` + }; + keyUsages = isPublic ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"]; + break; + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + const namedCurve = getNamedCurve2(keyData); + algorithm = namedCurve.startsWith("P-") ? { name: "ECDH", namedCurve } : { name: namedCurve }; + keyUsages = isPublic ? [] : ["deriveBits"]; + break; + } + case "EdDSA": + algorithm = { name: getNamedCurve2(keyData) }; + keyUsages = isPublic ? ["verify"] : ["sign"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value'); + } + try { + return await webcrypto_default.subtle.importKey(keyFormat, keyData, algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + algorithm = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.importKey(keyFormat, keyData, algorithm, (_b = options === null || options === void 0 ? void 0 : options.extractable) !== null && _b !== void 0 ? _b : false, keyUsages); + } + throw err; + } + }; + var fromPKCS8 = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "pkcs8", pem, alg, options); + }; + var fromSPKI = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "spki", pem, alg, options); + }; + function getElement(seq) { + let result = []; + let next = 0; + while (next < seq.length) { + let nextPart = parseElement(seq.subarray(next)); + result.push(nextPart); + next += nextPart.byteLength; + } + return result; + } + function parseElement(bytes) { + let position = 0; + let tag = bytes[0] & 31; + position++; + if (tag === 31) { + tag = 0; + while (bytes[position] >= 128) { + tag = tag * 128 + bytes[position] - 128; + position++; + } + tag = tag * 128 + bytes[position] - 128; + position++; + } + let length = 0; + if (bytes[position] < 128) { + length = bytes[position]; + position++; + } else if (length === 128) { + length = 0; + while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) { + if (length > bytes.byteLength) { + throw new TypeError("invalid indefinite form length"); + } + length++; + } + const byteLength2 = position + length + 2; + return { + byteLength: byteLength2, + contents: bytes.subarray(position, position + length), + raw: bytes.subarray(0, byteLength2) + }; + } else { + let numberOfDigits = bytes[position] & 127; + position++; + length = 0; + for (let i = 0; i < numberOfDigits; i++) { + length = length * 256 + bytes[position]; + position++; + } + } + const byteLength = position + length; + return { + byteLength, + contents: bytes.subarray(position, byteLength), + raw: bytes.subarray(0, byteLength) + }; + } + function spkiFromX509(buf) { + const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents); + return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw); + } + function getSPKI(x509) { + const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""); + const raw = decodeBase64(pem); + return format_pem_default(spkiFromX509(raw), "PUBLIC KEY"); + } + var fromX509 = (pem, alg, options) => { + let spki; + try { + spki = getSPKI(pem); + } catch (cause) { + throw new TypeError("failed to parse the X.509 certificate", { cause }); + } + return fromSPKI(spki, alg, options); + }; + + // dist/browser/runtime/jwk_to_key.js + function subtleMapping(jwk) { + let algorithm; + let keyUsages; + switch (jwk.kty) { + case "oct": { + switch (jwk.alg) { + case "HS256": + case "HS384": + case "HS512": + algorithm = { name: "HMAC", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = ["sign", "verify"]; + break; + case "A128CBC-HS256": + case "A192CBC-HS384": + case "A256CBC-HS512": + throw new JOSENotSupported(`${jwk.alg} keys cannot be imported as CryptoKey instances`); + case "A128GCM": + case "A192GCM": + case "A256GCM": + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": + algorithm = { name: "AES-GCM" }; + keyUsages = ["encrypt", "decrypt"]; + break; + case "A128KW": + case "A192KW": + case "A256KW": + algorithm = { name: "AES-KW" }; + keyUsages = ["wrapKey", "unwrapKey"]; + break; + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": + algorithm = { name: "PBKDF2" }; + keyUsages = ["deriveBits"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "RSA": { + switch (jwk.alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { name: "RSA-PSS", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}` + }; + keyUsages = jwk.d ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "EC": { + switch (jwk.alg) { + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": + algorithm = { name: "ECDH", namedCurve: jwk.crv }; + keyUsages = jwk.d ? ["deriveBits"] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case "OKP": { + switch (jwk.alg) { + case "EdDSA": + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ["sign"] : ["verify"]; + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ["deriveBits"] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); + } + return { algorithm, keyUsages }; + } + var parse = async (jwk) => { + var _a, _b; + if (!jwk.alg) { + throw new TypeError('"alg" argument is required when "jwk.alg" is not present'); + } + const { algorithm, keyUsages } = subtleMapping(jwk); + const rest = [ + algorithm, + (_a = jwk.ext) !== null && _a !== void 0 ? _a : false, + (_b = jwk.key_ops) !== null && _b !== void 0 ? _b : keyUsages + ]; + if (algorithm.name === "PBKDF2") { + return webcrypto_default.subtle.importKey("raw", decode(jwk.k), ...rest); + } + const keyData = { ...jwk }; + delete keyData.alg; + delete keyData.use; + try { + return await webcrypto_default.subtle.importKey("jwk", keyData, ...rest); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + rest[0] = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.importKey("jwk", keyData, ...rest); + } + throw err; + } + }; + var jwk_to_key_default = parse; + + // dist/browser/key/import.js + async function importSPKI(spki, alg, options) { + if (typeof spki !== "string" || spki.indexOf("-----BEGIN PUBLIC KEY-----") !== 0) { + throw new TypeError('"spki" must be SPKI formatted string'); + } + return fromSPKI(spki, alg, options); + } + async function importX509(x509, alg, options) { + if (typeof x509 !== "string" || x509.indexOf("-----BEGIN CERTIFICATE-----") !== 0) { + throw new TypeError('"x509" must be X.509 formatted string'); + } + return fromX509(x509, alg, options); + } + async function importPKCS8(pkcs8, alg, options) { + if (typeof pkcs8 !== "string" || pkcs8.indexOf("-----BEGIN PRIVATE KEY-----") !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string'); + } + return fromPKCS8(pkcs8, alg, options); + } + async function importJWK(jwk, alg, octAsKeyObject) { + var _a; + if (!isObject(jwk)) { + throw new TypeError("JWK must be an object"); + } + alg || (alg = jwk.alg); + switch (jwk.kty) { + case "oct": + if (typeof jwk.k !== "string" || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value'); + } + octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : octAsKeyObject = jwk.ext !== true; + if (octAsKeyObject) { + return jwk_to_key_default({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false }); + } + return decode(jwk.k); + case "RSA": + if (jwk.oth !== void 0) { + throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported'); + } + case "EC": + case "OKP": + return jwk_to_key_default({ ...jwk, alg }); + default: + throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value'); + } + } + + // dist/browser/lib/check_key_type.js + var symmetricTypeCheck = (alg, key) => { + if (key instanceof Uint8Array) + return; + if (!is_key_like_default(key)) { + throw new TypeError(withAlg(alg, key, ...types, "Uint8Array")); + } + if (key.type !== "secret") { + throw new TypeError(`${types.join(" or ")} instances for symmetric algorithms must be of type "secret"`); + } + }; + var asymmetricTypeCheck = (alg, key, usage) => { + if (!is_key_like_default(key)) { + throw new TypeError(withAlg(alg, key, ...types)); + } + if (key.type === "secret") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithms must not be of type "secret"`); + } + if (usage === "sign" && key.type === "public") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm signing must be of type "private"`); + } + if (usage === "decrypt" && key.type === "public") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm decryption must be of type "private"`); + } + if (key.algorithm && usage === "verify" && key.type === "private") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm verifying must be of type "public"`); + } + if (key.algorithm && usage === "encrypt" && key.type === "private") { + throw new TypeError(`${types.join(" or ")} instances for asymmetric algorithm encryption must be of type "public"`); + } + }; + var checkKeyType = (alg, key, usage) => { + const symmetric = alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A\d{3}(?:GCM)?KW$/.test(alg); + if (symmetric) { + symmetricTypeCheck(alg, key); + } else { + asymmetricTypeCheck(alg, key, usage); + } + }; + var check_key_type_default = checkKeyType; + + // dist/browser/runtime/encrypt.js + 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 webcrypto_default.subtle.importKey("raw", cek.subarray(keySize >> 3), "AES-CBC", false, ["encrypt"]); + const macKey = await webcrypto_default.subtle.importKey("raw", cek.subarray(0, keySize >> 3), { + hash: `SHA-${keySize << 1}`, + name: "HMAC" + }, false, ["sign"]); + const ciphertext = new Uint8Array(await webcrypto_default.subtle.encrypt({ + iv, + name: "AES-CBC" + }, encKey, plaintext)); + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const tag = new Uint8Array((await webcrypto_default.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)); + return { ciphertext, tag }; + } + async function gcmEncrypt(enc, plaintext, cek, iv, aad) { + let encKey; + if (cek instanceof Uint8Array) { + encKey = await webcrypto_default.subtle.importKey("raw", cek, "AES-GCM", false, ["encrypt"]); + } else { + checkEncCryptoKey(cek, enc, "encrypt"); + encKey = cek; + } + const encrypted = new Uint8Array(await webcrypto_default.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 }; + } + var encrypt3 = async (enc, plaintext, cek, iv, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalid_key_input_default(cek, ...types, "Uint8Array")); + } + check_iv_length_default(enc, iv); + 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"); + } + }; + var encrypt_default = encrypt3; + + // dist/browser/lib/aesgcmkw.js + async function wrap2(alg, key, cek, iv) { + const jweAlgorithm = alg.slice(0, 7); + iv || (iv = iv_default(jweAlgorithm)); + const { ciphertext: encryptedKey, tag } = await encrypt_default(jweAlgorithm, cek, key, iv, new Uint8Array(0)); + return { encryptedKey, iv: encode(iv), tag: encode(tag) }; + } + async function unwrap2(alg, key, encryptedKey, iv, tag) { + const jweAlgorithm = alg.slice(0, 7); + return decrypt_default(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)); + } + + // dist/browser/lib/decrypt_key_management.js + async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) { + check_key_type_default(alg, key, "decrypt"); + switch (alg) { + case "dir": { + if (encryptedKey !== void 0) + throw new JWEInvalid("Encountered unexpected JWE Encrypted Key"); + return key; + } + case "ECDH-ES": + if (encryptedKey !== void 0) + throw new JWEInvalid("Encountered unexpected JWE Encrypted Key"); + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + if (!isObject(joseHeader.epk)) + throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`); + if (!ecdhAllowed(key)) + throw new JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime"); + const epk = await importJWK(joseHeader.epk, alg); + let partyUInfo; + let partyVInfo; + if (joseHeader.apu !== void 0) { + if (typeof joseHeader.apu !== "string") + throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`); + partyUInfo = decode(joseHeader.apu); + } + if (joseHeader.apv !== void 0) { + if (typeof joseHeader.apv !== "string") + throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`); + partyVInfo = decode(joseHeader.apv); + } + const sharedSecret = await deriveKey(epk, key, alg === "ECDH-ES" ? joseHeader.enc : alg, alg === "ECDH-ES" ? bitLength2(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo); + if (alg === "ECDH-ES") + return sharedSecret; + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return unwrap(alg.slice(-6), sharedSecret, encryptedKey); + } + case "RSA1_5": + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return decrypt3(alg, key, encryptedKey); + } + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + if (typeof joseHeader.p2c !== "number") + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`); + const p2cLimit = (options === null || options === void 0 ? void 0 : options.maxPBES2Count) || 1e4; + if (joseHeader.p2c > p2cLimit) + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`); + if (typeof joseHeader.p2s !== "string") + throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`); + return decrypt2(alg, key, encryptedKey, joseHeader.p2c, decode(joseHeader.p2s)); + } + case "A128KW": + case "A192KW": + case "A256KW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + return unwrap(alg, key, encryptedKey); + } + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": { + if (encryptedKey === void 0) + throw new JWEInvalid("JWE Encrypted Key missing"); + if (typeof joseHeader.iv !== "string") + throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`); + if (typeof joseHeader.tag !== "string") + throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`); + const iv = decode(joseHeader.iv); + const tag = decode(joseHeader.tag); + return unwrap2(alg, key, encryptedKey, iv, tag); + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + } + var decrypt_key_management_default = decryptKeyManagement; + + // dist/browser/lib/validate_crit.js + function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { + if (joseHeader.crit !== void 0 && protectedHeader.crit === void 0) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); + } + if (!protectedHeader || protectedHeader.crit === void 0) { + return /* @__PURE__ */ new Set(); + } + if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input !== "string" || input.length === 0)) { + throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); + } + let recognized; + if (recognizedOption !== void 0) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); + } else { + recognized = recognizedDefault; + } + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); + } + if (joseHeader[parameter] === void 0) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`); + } else if (recognized.get(parameter) && protectedHeader[parameter] === void 0) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); + } + } + return new Set(protectedHeader.crit); + } + var validate_crit_default = validateCrit; + + // dist/browser/lib/validate_algorithms.js + var validateAlgorithms = (option, algorithms) => { + if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) { + throw new TypeError(`"${option}" option must be an array of strings`); + } + if (!algorithms) { + return void 0; + } + return new Set(algorithms); + }; + var validate_algorithms_default = validateAlgorithms; + + // dist/browser/jwe/flattened/decrypt.js + async function flattenedDecrypt(jwe, key, options) { + var _a; + if (!isObject(jwe)) { + throw new JWEInvalid("Flattened JWE must be an object"); + } + if (jwe.protected === void 0 && jwe.header === void 0 && jwe.unprotected === void 0) { + throw new JWEInvalid("JOSE Header missing"); + } + if (typeof jwe.iv !== "string") { + throw new JWEInvalid("JWE Initialization Vector missing or incorrect type"); + } + if (typeof jwe.ciphertext !== "string") { + throw new JWEInvalid("JWE Ciphertext missing or incorrect type"); + } + if (typeof jwe.tag !== "string") { + throw new JWEInvalid("JWE Authentication Tag missing or incorrect type"); + } + if (jwe.protected !== void 0 && typeof jwe.protected !== "string") { + throw new JWEInvalid("JWE Protected Header incorrect type"); + } + if (jwe.encrypted_key !== void 0 && typeof jwe.encrypted_key !== "string") { + throw new JWEInvalid("JWE Encrypted Key incorrect type"); + } + if (jwe.aad !== void 0 && typeof jwe.aad !== "string") { + throw new JWEInvalid("JWE AAD incorrect type"); + } + if (jwe.header !== void 0 && !isObject(jwe.header)) { + throw new JWEInvalid("JWE Shared Unprotected Header incorrect type"); + } + if (jwe.unprotected !== void 0 && !isObject(jwe.unprotected)) { + throw new JWEInvalid("JWE Per-Recipient Unprotected Header incorrect type"); + } + let parsedProt; + if (jwe.protected) { + try { + const protectedHeader2 = decode(jwe.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader2)); + } catch (_b) { + throw new JWEInvalid("JWE Protected Header is invalid"); + } + } + if (!is_disjoint_default(parsedProt, jwe.header, jwe.unprotected)) { + throw new JWEInvalid("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected + }; + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + if (joseHeader.zip !== void 0) { + if (!parsedProt || !parsedProt.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== "DEF") { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid("missing JWE Algorithm (alg) in JWE Header"); + } + if (typeof enc !== "string" || !enc) { + throw new JWEInvalid("missing JWE Encryption Algorithm (enc) in JWE Header"); + } + const keyManagementAlgorithms = options && validate_algorithms_default("keyManagementAlgorithms", options.keyManagementAlgorithms); + const contentEncryptionAlgorithms = options && validate_algorithms_default("contentEncryptionAlgorithms", options.contentEncryptionAlgorithms); + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed'); + } + let encryptedKey; + if (jwe.encrypted_key !== void 0) { + encryptedKey = decode(jwe.encrypted_key); + } + let resolvedKey = false; + if (typeof key === "function") { + key = await key(parsedProt, jwe); + resolvedKey = true; + } + let cek; + try { + cek = await decrypt_key_management_default(alg, key, encryptedKey, joseHeader, options); + } catch (err) { + if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { + throw err; + } + cek = cek_default(enc); + } + const iv = decode(jwe.iv); + const tag = decode(jwe.tag); + const protectedHeader = encoder.encode((_a = jwe.protected) !== null && _a !== void 0 ? _a : ""); + let additionalData; + if (jwe.aad !== void 0) { + additionalData = concat(protectedHeader, encoder.encode("."), encoder.encode(jwe.aad)); + } else { + additionalData = protectedHeader; + } + let plaintext = await decrypt_default(enc, cek, decode(jwe.ciphertext), iv, tag, additionalData); + if (joseHeader.zip === "DEF") { + plaintext = await ((options === null || options === void 0 ? void 0 : options.inflateRaw) || inflate)(plaintext); + } + const result = { plaintext }; + if (jwe.protected !== void 0) { + result.protectedHeader = parsedProt; + } + if (jwe.aad !== void 0) { + result.additionalAuthenticatedData = decode(jwe.aad); + } + if (jwe.unprotected !== void 0) { + result.sharedUnprotectedHeader = jwe.unprotected; + } + if (jwe.header !== void 0) { + result.unprotectedHeader = jwe.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; + } + + // dist/browser/jwe/compact/decrypt.js + async function compactDecrypt(jwe, key, options) { + if (jwe instanceof Uint8Array) { + jwe = decoder.decode(jwe); + } + if (typeof jwe !== "string") { + throw new JWEInvalid("Compact JWE must be a string or Uint8Array"); + } + const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split("."); + if (length !== 5) { + throw new JWEInvalid("Invalid Compact JWE"); + } + const decrypted = await flattenedDecrypt({ + ciphertext, + iv: iv || void 0, + protected: protectedHeader || void 0, + tag: tag || void 0, + encrypted_key: encryptedKey || void 0 + }, key, options); + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: decrypted.key }; + } + return result; + } + + // dist/browser/jwe/general/decrypt.js + async function generalDecrypt(jwe, key, options) { + if (!isObject(jwe)) { + throw new JWEInvalid("General JWE must be an object"); + } + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { + throw new JWEInvalid("JWE Recipients missing or incorrect type"); + } + if (!jwe.recipients.length) { + throw new JWEInvalid("JWE Recipients has no members"); + } + for (const recipient of jwe.recipients) { + try { + return await flattenedDecrypt({ + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected + }, key, options); + } catch (_a) { + } + } + throw new JWEDecryptionFailed(); + } + + // dist/browser/runtime/key_to_jwk.js + var keyToJWK = async (key) => { + if (key instanceof Uint8Array) { + return { + kty: "oct", + k: encode(key) + }; + } + if (!isCryptoKey(key)) { + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); + } + if (!key.extractable) { + throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK"); + } + const { ext, key_ops, alg, use, ...jwk } = await webcrypto_default.subtle.exportKey("jwk", key); + return jwk; + }; + var key_to_jwk_default = keyToJWK; + + // dist/browser/key/export.js + async function exportSPKI(key) { + return toSPKI(key); + } + async function exportPKCS8(key) { + return toPKCS8(key); + } + async function exportJWK(key) { + return key_to_jwk_default(key); + } + + // dist/browser/lib/encrypt_key_management.js + async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) { + let encryptedKey; + let parameters; + let cek; + check_key_type_default(alg, key, "encrypt"); + switch (alg) { + case "dir": { + cek = key; + break; + } + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + if (!ecdhAllowed(key)) { + throw new JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime"); + } + const { apu, apv } = providedParameters; + let { epk: ephemeralKey } = providedParameters; + ephemeralKey || (ephemeralKey = (await generateEpk(key)).privateKey); + const { x, y, crv, kty } = await exportJWK(ephemeralKey); + const sharedSecret = await deriveKey(key, ephemeralKey, alg === "ECDH-ES" ? enc : alg, alg === "ECDH-ES" ? bitLength2(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv); + parameters = { epk: { x, crv, kty } }; + if (kty === "EC") + parameters.epk.y = y; + if (apu) + parameters.apu = encode(apu); + if (apv) + parameters.apv = encode(apv); + if (alg === "ECDH-ES") { + cek = sharedSecret; + break; + } + cek = providedCek || cek_default(enc); + const kwAlg = alg.slice(-6); + encryptedKey = await wrap(kwAlg, sharedSecret, cek); + break; + } + case "RSA1_5": + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": { + cek = providedCek || cek_default(enc); + encryptedKey = await encrypt2(alg, key, cek); + break; + } + case "PBES2-HS256+A128KW": + case "PBES2-HS384+A192KW": + case "PBES2-HS512+A256KW": { + cek = providedCek || cek_default(enc); + const { p2c, p2s: p2s2 } = providedParameters; + ({ encryptedKey, ...parameters } = await encrypt(alg, key, cek, p2c, p2s2)); + break; + } + case "A128KW": + case "A192KW": + case "A256KW": { + cek = providedCek || cek_default(enc); + encryptedKey = await wrap(alg, key, cek); + break; + } + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": { + cek = providedCek || cek_default(enc); + const { iv } = providedParameters; + ({ encryptedKey, ...parameters } = await wrap2(alg, key, cek, iv)); + break; + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + return { cek, encryptedKey, parameters }; + } + var encrypt_key_management_default = encryptKeyManagement; + + // dist/browser/jwe/flattened/encrypt.js + var unprotected = Symbol(); + var FlattenedEncrypt = class { + constructor(plaintext) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError("plaintext must be an instance of Uint8Array"); + } + this._plaintext = plaintext; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError("setKeyManagementParameters can only be called once"); + } + this._keyManagementParameters = parameters; + return this; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._sharedUnprotectedHeader) { + throw new TypeError("setSharedUnprotectedHeader can only be called once"); + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError("setContentEncryptionKey can only be called once"); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError("setInitializationVector can only be called once"); + } + this._iv = iv; + return this; + } + async encrypt(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new JWEInvalid("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()"); + } + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) { + throw new JWEInvalid("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader + }; + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== void 0) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== "DEF") { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (typeof enc !== "string" || !enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + let encryptedKey; + if (alg === "dir") { + if (this._cek) { + throw new TypeError("setContentEncryptionKey cannot be called when using Direct Encryption"); + } + } else if (alg === "ECDH-ES") { + if (this._cek) { + throw new TypeError("setContentEncryptionKey cannot be called when using Direct Key Agreement"); + } + } + let cek; + { + let parameters; + ({ cek, encryptedKey, parameters } = await encrypt_key_management_default(alg, enc, key, this._cek, this._keyManagementParameters)); + if (parameters) { + if (options && unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters); + } else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters }; + } + } else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters); + } else { + this._protectedHeader = { ...this._protectedHeader, ...parameters }; + } + } + } + } + this._iv || (this._iv = iv_default(enc)); + let additionalData; + let protectedHeader; + let aadMember; + if (this._protectedHeader) { + protectedHeader = encoder.encode(encode(JSON.stringify(this._protectedHeader))); + } else { + protectedHeader = encoder.encode(""); + } + if (this._aad) { + aadMember = encode(this._aad); + additionalData = concat(protectedHeader, encoder.encode("."), encoder.encode(aadMember)); + } else { + additionalData = protectedHeader; + } + let ciphertext; + let tag; + if (joseHeader.zip === "DEF") { + const deflated = await ((options === null || options === void 0 ? void 0 : options.deflateRaw) || deflate)(this._plaintext); + ({ ciphertext, tag } = await encrypt_default(enc, deflated, cek, this._iv, additionalData)); + } else { + ({ ciphertext, tag } = await encrypt_default(enc, this._plaintext, cek, this._iv, additionalData)); + } + const jwe = { + ciphertext: encode(ciphertext), + iv: encode(this._iv), + tag: encode(tag) + }; + if (encryptedKey) { + jwe.encrypted_key = encode(encryptedKey); + } + if (aadMember) { + jwe.aad = aadMember; + } + if (this._protectedHeader) { + jwe.protected = decoder.decode(protectedHeader); + } + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader; + } + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader; + } + return jwe; + } + }; + + // dist/browser/jwe/general/encrypt.js + var IndividualRecipient = class { + constructor(enc, key, options) { + this.parent = enc; + this.key = key; + this.options = options; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addRecipient(...args) { + return this.parent.addRecipient(...args); + } + encrypt(...args) { + return this.parent.encrypt(...args); + } + done() { + return this.parent; + } + }; + var GeneralEncrypt = class { + constructor(plaintext) { + this._recipients = []; + this._plaintext = plaintext; + } + addRecipient(key, options) { + const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit }); + this._recipients.push(recipient); + return recipient; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setSharedUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = sharedUnprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + async encrypt(options) { + var _a, _b, _c; + if (!this._recipients.length) { + throw new JWEInvalid("at least one recipient must be added"); + } + options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw }; + if (this._recipients.length === 1) { + const [recipient] = this._recipients; + const flattened = await new FlattenedEncrypt(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(recipient.unprotectedHeader).encrypt(recipient.key, { ...recipient.options, ...options }); + let jwe2 = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag + }; + if (flattened.aad) + jwe2.aad = flattened.aad; + if (flattened.protected) + jwe2.protected = flattened.protected; + if (flattened.unprotected) + jwe2.unprotected = flattened.unprotected; + if (flattened.encrypted_key) + jwe2.recipients[0].encrypted_key = flattened.encrypted_key; + if (flattened.header) + jwe2.recipients[0].header = flattened.header; + return jwe2; + } + let enc; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) { + throw new JWEInvalid("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader + }; + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (alg === "dir" || alg === "ECDH-ES") { + throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient'); + } + if (typeof joseHeader.enc !== "string" || !joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + if (!enc) { + enc = joseHeader.enc; + } else if (enc !== joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients'); + } + validate_crit_default(JWEInvalid, /* @__PURE__ */ new Map(), recipient.options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== void 0) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + } + } + const cek = cek_default(enc); + let jwe = { + ciphertext: "", + iv: "", + recipients: [], + tag: "" + }; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + const target = {}; + jwe.recipients.push(target); + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader + }; + const p2c = joseHeader.alg.startsWith("PBES2") ? 2048 + i : void 0; + if (i === 0) { + const flattened = await new FlattenedEncrypt(this._plaintext).setAdditionalAuthenticatedData(this._aad).setContentEncryptionKey(cek).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(recipient.unprotectedHeader).setKeyManagementParameters({ p2c }).encrypt(recipient.key, { + ...recipient.options, + ...options, + [unprotected]: true + }); + jwe.ciphertext = flattened.ciphertext; + jwe.iv = flattened.iv; + jwe.tag = flattened.tag; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + target.encrypted_key = flattened.encrypted_key; + if (flattened.header) + target.header = flattened.header; + continue; + } + const { encryptedKey, parameters } = await encrypt_key_management_default(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) || ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c }); + target.encrypted_key = encode(encryptedKey); + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters }; + } + return jwe; + } + }; + + // dist/browser/runtime/subtle_dsa.js + function subtleDsa(alg, algorithm) { + const hash = `SHA-${alg.slice(-3)}`; + switch (alg) { + case "HS256": + case "HS384": + case "HS512": + return { hash, name: "HMAC" }; + case "PS256": + case "PS384": + case "PS512": + return { hash, name: "RSA-PSS", saltLength: alg.slice(-3) >> 3 }; + case "RS256": + case "RS384": + case "RS512": + return { hash, name: "RSASSA-PKCS1-v1_5" }; + case "ES256": + case "ES384": + case "ES512": + return { hash, name: "ECDSA", namedCurve: algorithm.namedCurve }; + case "EdDSA": + if (isCloudflareWorkers() && algorithm.name === "NODE-ED25519") { + return { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + } + return { name: algorithm.name }; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + } + + // dist/browser/runtime/get_sign_verify_key.js + function getCryptoKey3(alg, key, usage) { + if (isCryptoKey(key)) { + checkSigCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + if (!alg.startsWith("HS")) { + throw new TypeError(invalid_key_input_default(key, ...types)); + } + return webcrypto_default.subtle.importKey("raw", key, { hash: `SHA-${alg.slice(-3)}`, name: "HMAC" }, false, [usage]); + } + throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array")); + } + + // dist/browser/runtime/verify.js + var verify = async (alg, key, signature, data) => { + const cryptoKey = await getCryptoKey3(alg, key, "verify"); + check_key_length_default(alg, cryptoKey); + const algorithm = subtleDsa(alg, cryptoKey.algorithm); + try { + return await webcrypto_default.subtle.verify(algorithm, cryptoKey, signature, data); + } catch (_a) { + return false; + } + }; + var verify_default = verify; + + // dist/browser/jws/flattened/verify.js + async function flattenedVerify(jws, key, options) { + var _a; + if (!isObject(jws)) { + throw new JWSInvalid("Flattened JWS must be an object"); + } + if (jws.protected === void 0 && jws.header === void 0) { + throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); + } + if (jws.protected !== void 0 && typeof jws.protected !== "string") { + throw new JWSInvalid("JWS Protected Header incorrect type"); + } + if (jws.payload === void 0) { + throw new JWSInvalid("JWS Payload missing"); + } + if (typeof jws.signature !== "string") { + throw new JWSInvalid("JWS Signature missing or incorrect type"); + } + if (jws.header !== void 0 && !isObject(jws.header)) { + throw new JWSInvalid("JWS Unprotected Header incorrect type"); + } + let parsedProt = {}; + if (jws.protected) { + try { + const protectedHeader = decode(jws.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } catch (_b) { + throw new JWSInvalid("JWS Protected Header is invalid"); + } + } + if (!is_disjoint_default(parsedProt, jws.header)) { + throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...parsedProt, + ...jws.header + }; + const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + let b64 = true; + if (extensions.has("b64")) { + b64 = parsedProt.b64; + if (typeof b64 !== "boolean") { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + const algorithms = options && validate_algorithms_default("algorithms", options.algorithms); + if (algorithms && !algorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (b64) { + if (typeof jws.payload !== "string") { + throw new JWSInvalid("JWS Payload must be a string"); + } + } else if (typeof jws.payload !== "string" && !(jws.payload instanceof Uint8Array)) { + throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance"); + } + let resolvedKey = false; + if (typeof key === "function") { + key = await key(parsedProt, jws); + resolvedKey = true; + } + check_key_type_default(alg, key, "verify"); + const data = concat(encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ""), encoder.encode("."), typeof jws.payload === "string" ? encoder.encode(jws.payload) : jws.payload); + const signature = decode(jws.signature); + const verified = await verify_default(alg, key, signature, data); + if (!verified) { + throw new JWSSignatureVerificationFailed(); + } + let payload; + if (b64) { + payload = decode(jws.payload); + } else if (typeof jws.payload === "string") { + payload = encoder.encode(jws.payload); + } else { + payload = jws.payload; + } + const result = { payload }; + if (jws.protected !== void 0) { + result.protectedHeader = parsedProt; + } + if (jws.header !== void 0) { + result.unprotectedHeader = jws.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; + } + + // dist/browser/jws/compact/verify.js + async function compactVerify(jws, key, options) { + if (jws instanceof Uint8Array) { + jws = decoder.decode(jws); + } + if (typeof jws !== "string") { + throw new JWSInvalid("Compact JWS must be a string or Uint8Array"); + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split("."); + if (length !== 3) { + throw new JWSInvalid("Invalid Compact JWS"); + } + const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options); + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: verified.key }; + } + return result; + } + + // dist/browser/jws/general/verify.js + async function generalVerify(jws, key, options) { + if (!isObject(jws)) { + throw new JWSInvalid("General JWS must be an object"); + } + if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { + throw new JWSInvalid("JWS Signatures missing or incorrect type"); + } + for (const signature of jws.signatures) { + try { + return await flattenedVerify({ + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature + }, key, options); + } catch (_a) { + } + } + throw new JWSSignatureVerificationFailed(); + } + + // dist/browser/lib/epoch.js + var epoch_default = (date) => Math.floor(date.getTime() / 1e3); + + // dist/browser/lib/secs.js + var minute = 60; + var hour = minute * 60; + var day = hour * 24; + var week = day * 7; + var year = day * 365.25; + var REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i; + var secs_default = (str) => { + const matched = REGEX.exec(str); + if (!matched) { + throw new TypeError("Invalid time period format"); + } + const value = parseFloat(matched[1]); + const unit = matched[2].toLowerCase(); + switch (unit) { + case "sec": + case "secs": + case "second": + case "seconds": + case "s": + return Math.round(value); + case "minute": + case "minutes": + case "min": + case "mins": + case "m": + return Math.round(value * minute); + case "hour": + case "hours": + case "hr": + case "hrs": + case "h": + return Math.round(value * hour); + case "day": + case "days": + case "d": + return Math.round(value * day); + case "week": + case "weeks": + case "w": + return Math.round(value * week); + default: + return Math.round(value * year); + } + }; + + // dist/browser/lib/jwt_claims_set.js + var normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, ""); + var checkAudiencePresence = (audPayload, audOption) => { + if (typeof audPayload === "string") { + return audOption.includes(audPayload); + } + if (Array.isArray(audPayload)) { + return audOption.some(Set.prototype.has.bind(new Set(audPayload))); + } + return false; + }; + var jwt_claims_set_default = (protectedHeader, encodedPayload, options = {}) => { + const { typ } = options; + if (typ && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) { + throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', "typ", "check_failed"); + } + let payload; + try { + payload = JSON.parse(decoder.decode(encodedPayload)); + } catch (_a) { + } + if (!isObject(payload)) { + throw new JWTInvalid("JWT Claims Set must be a top-level JSON object"); + } + const { issuer } = options; + if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) { + throw new JWTClaimValidationFailed('unexpected "iss" claim value', "iss", "check_failed"); + } + const { subject } = options; + if (subject && payload.sub !== subject) { + throw new JWTClaimValidationFailed('unexpected "sub" claim value', "sub", "check_failed"); + } + const { audience } = options; + if (audience && !checkAudiencePresence(payload.aud, typeof audience === "string" ? [audience] : audience)) { + throw new JWTClaimValidationFailed('unexpected "aud" claim value', "aud", "check_failed"); + } + let tolerance; + switch (typeof options.clockTolerance) { + case "string": + tolerance = secs_default(options.clockTolerance); + break; + case "number": + tolerance = options.clockTolerance; + break; + case "undefined": + tolerance = 0; + break; + default: + throw new TypeError("Invalid clockTolerance option type"); + } + const { currentDate } = options; + const now = epoch_default(currentDate || /* @__PURE__ */ new Date()); + if ((payload.iat !== void 0 || options.maxTokenAge) && typeof payload.iat !== "number") { + throw new JWTClaimValidationFailed('"iat" claim must be a number', "iat", "invalid"); + } + if (payload.nbf !== void 0) { + if (typeof payload.nbf !== "number") { + throw new JWTClaimValidationFailed('"nbf" claim must be a number', "nbf", "invalid"); + } + if (payload.nbf > now + tolerance) { + throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', "nbf", "check_failed"); + } + } + if (payload.exp !== void 0) { + if (typeof payload.exp !== "number") { + throw new JWTClaimValidationFailed('"exp" claim must be a number', "exp", "invalid"); + } + if (payload.exp <= now - tolerance) { + throw new JWTExpired('"exp" claim timestamp check failed', "exp", "check_failed"); + } + } + if (options.maxTokenAge) { + const age = now - payload.iat; + const max = typeof options.maxTokenAge === "number" ? options.maxTokenAge : secs_default(options.maxTokenAge); + if (age - tolerance > max) { + throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', "iat", "check_failed"); + } + if (age < 0 - tolerance) { + throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', "iat", "check_failed"); + } + } + return payload; + }; + + // dist/browser/jwt/verify.js + async function jwtVerify(jwt, key, options) { + var _a; + const verified = await compactVerify(jwt, key, options); + if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes("b64")) && verified.protectedHeader.b64 === false) { + throw new JWTInvalid("JWTs MUST NOT use unencoded payload"); + } + const payload = jwt_claims_set_default(verified.protectedHeader, verified.payload, options); + const result = { payload, protectedHeader: verified.protectedHeader }; + if (typeof key === "function") { + return { ...result, key: verified.key }; + } + return result; + } + + // dist/browser/jwt/decrypt.js + async function jwtDecrypt(jwt, key, options) { + const decrypted = await compactDecrypt(jwt, key, options); + const payload = jwt_claims_set_default(decrypted.protectedHeader, decrypted.plaintext, options); + const { protectedHeader } = decrypted; + if (protectedHeader.iss !== void 0 && protectedHeader.iss !== payload.iss) { + throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', "iss", "mismatch"); + } + if (protectedHeader.sub !== void 0 && protectedHeader.sub !== payload.sub) { + throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', "sub", "mismatch"); + } + if (protectedHeader.aud !== void 0 && JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { + throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', "aud", "mismatch"); + } + const result = { payload, protectedHeader }; + if (typeof key === "function") { + return { ...result, key: decrypted.key }; + } + return result; + } + + // dist/browser/jwe/compact/encrypt.js + var CompactEncrypt = class { + constructor(plaintext) { + this._flattened = new FlattenedEncrypt(plaintext); + } + setContentEncryptionKey(cek) { + this._flattened.setContentEncryptionKey(cek); + return this; + } + setInitializationVector(iv) { + this._flattened.setInitializationVector(iv); + return this; + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + setKeyManagementParameters(parameters) { + this._flattened.setKeyManagementParameters(parameters); + return this; + } + async encrypt(key, options) { + const jwe = await this._flattened.encrypt(key, options); + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join("."); + } + }; + + // dist/browser/runtime/sign.js + var sign = async (alg, key, data) => { + const cryptoKey = await getCryptoKey3(alg, key, "sign"); + check_key_length_default(alg, cryptoKey); + const signature = await webcrypto_default.subtle.sign(subtleDsa(alg, cryptoKey.algorithm), cryptoKey, data); + return new Uint8Array(signature); + }; + var sign_default = sign; + + // dist/browser/jws/flattened/sign.js + var FlattenedSign = class { + constructor(payload) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError("payload must be an instance of Uint8Array"); + } + this._payload = payload; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + async sign(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new JWSInvalid("either setProtectedHeader or setUnprotectedHeader must be called before #sign()"); + } + if (!is_disjoint_default(this._protectedHeader, this._unprotectedHeader)) { + throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint"); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader + }; + const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + let b64 = true; + if (extensions.has("b64")) { + b64 = this._protectedHeader.b64; + if (typeof b64 !== "boolean") { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== "string" || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + check_key_type_default(alg, key, "sign"); + let payload = this._payload; + if (b64) { + payload = encoder.encode(encode(payload)); + } + let protectedHeader; + if (this._protectedHeader) { + protectedHeader = encoder.encode(encode(JSON.stringify(this._protectedHeader))); + } else { + protectedHeader = encoder.encode(""); + } + const data = concat(protectedHeader, encoder.encode("."), payload); + const signature = await sign_default(alg, key, data); + const jws = { + signature: encode(signature), + payload: "" + }; + if (b64) { + jws.payload = decoder.decode(payload); + } + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader; + } + if (this._protectedHeader) { + jws.protected = decoder.decode(protectedHeader); + } + return jws; + } + }; + + // dist/browser/jws/compact/sign.js + var CompactSign = class { + constructor(payload) { + this._flattened = new FlattenedSign(payload); + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + async sign(key, options) { + const jws = await this._flattened.sign(key, options); + if (jws.payload === void 0) { + throw new TypeError("use the flattened module for creating JWS with b64: false"); + } + return `${jws.protected}.${jws.payload}.${jws.signature}`; + } + }; + + // dist/browser/jws/general/sign.js + var IndividualSignature = class { + constructor(sig, key, options) { + this.parent = sig; + this.key = key; + this.options = options; + } + setProtectedHeader(protectedHeader) { + if (this.protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this.protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError("setUnprotectedHeader can only be called once"); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addSignature(...args) { + return this.parent.addSignature(...args); + } + sign(...args) { + return this.parent.sign(...args); + } + done() { + return this.parent; + } + }; + var GeneralSign = class { + constructor(payload) { + this._signatures = []; + this._payload = payload; + } + addSignature(key, options) { + const signature = new IndividualSignature(this, key, options); + this._signatures.push(signature); + return signature; + } + async sign() { + if (!this._signatures.length) { + throw new JWSInvalid("at least one signature must be added"); + } + const jws = { + signatures: [], + payload: "" + }; + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i]; + const flattened = new FlattenedSign(this._payload); + flattened.setProtectedHeader(signature.protectedHeader); + flattened.setUnprotectedHeader(signature.unprotectedHeader); + const { payload, ...rest } = await flattened.sign(signature.key, signature.options); + if (i === 0) { + jws.payload = payload; + } else if (jws.payload !== payload) { + throw new JWSInvalid("inconsistent use of JWS Unencoded Payload (RFC7797)"); + } + jws.signatures.push(rest); + } + return jws; + } + }; + + // dist/browser/jwt/produce.js + var ProduceJWT = class { + constructor(payload) { + if (!isObject(payload)) { + throw new TypeError("JWT Claims Set MUST be an object"); + } + this._payload = payload; + } + setIssuer(issuer) { + this._payload = { ...this._payload, iss: issuer }; + return this; + } + setSubject(subject) { + this._payload = { ...this._payload, sub: subject }; + return this; + } + setAudience(audience) { + this._payload = { ...this._payload, aud: audience }; + return this; + } + setJti(jwtId) { + this._payload = { ...this._payload, jti: jwtId }; + return this; + } + setNotBefore(input) { + if (typeof input === "number") { + this._payload = { ...this._payload, nbf: input }; + } else { + this._payload = { ...this._payload, nbf: epoch_default(/* @__PURE__ */ new Date()) + secs_default(input) }; + } + return this; + } + setExpirationTime(input) { + if (typeof input === "number") { + this._payload = { ...this._payload, exp: input }; + } else { + this._payload = { ...this._payload, exp: epoch_default(/* @__PURE__ */ new Date()) + secs_default(input) }; + } + return this; + } + setIssuedAt(input) { + if (typeof input === "undefined") { + this._payload = { ...this._payload, iat: epoch_default(/* @__PURE__ */ new Date()) }; + } else { + this._payload = { ...this._payload, iat: input }; + } + return this; + } + }; + + // dist/browser/jwt/sign.js + var SignJWT = class extends ProduceJWT { + setProtectedHeader(protectedHeader) { + this._protectedHeader = protectedHeader; + return this; + } + async sign(key, options) { + var _a; + const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload))); + sig.setProtectedHeader(this._protectedHeader); + if (Array.isArray((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.crit) && this._protectedHeader.crit.includes("b64") && this._protectedHeader.b64 === false) { + throw new JWTInvalid("JWTs MUST NOT use unencoded payload"); + } + return sig.sign(key, options); + } + }; + + // dist/browser/jwt/encrypt.js + var EncryptJWT = class extends ProduceJWT { + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError("setProtectedHeader can only be called once"); + } + this._protectedHeader = protectedHeader; + return this; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError("setKeyManagementParameters can only be called once"); + } + this._keyManagementParameters = parameters; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError("setContentEncryptionKey can only be called once"); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError("setInitializationVector can only be called once"); + } + this._iv = iv; + return this; + } + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true; + return this; + } + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true; + return this; + } + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true; + return this; + } + async encrypt(key, options) { + const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload))); + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss }; + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub }; + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud }; + } + enc.setProtectedHeader(this._protectedHeader); + if (this._iv) { + enc.setInitializationVector(this._iv); + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek); + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters); + } + return enc.encrypt(key, options); + } + }; + + // dist/browser/jwk/thumbprint.js + var check = (value, description) => { + if (typeof value !== "string" || !value) { + throw new JWKInvalid(`${description} missing or invalid`); + } + }; + async function calculateJwkThumbprint(jwk, digestAlgorithm) { + if (!isObject(jwk)) { + throw new TypeError("JWK must be an object"); + } + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : digestAlgorithm = "sha256"; + if (digestAlgorithm !== "sha256" && digestAlgorithm !== "sha384" && digestAlgorithm !== "sha512") { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"'); + } + let components; + switch (jwk.kty) { + case "EC": + check(jwk.crv, '"crv" (Curve) Parameter'); + check(jwk.x, '"x" (X Coordinate) Parameter'); + check(jwk.y, '"y" (Y Coordinate) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; + break; + case "OKP": + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'); + check(jwk.x, '"x" (Public Key) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; + break; + case "RSA": + check(jwk.e, '"e" (Exponent) Parameter'); + check(jwk.n, '"n" (Modulus) Parameter'); + components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; + break; + case "oct": + check(jwk.k, '"k" (Key Value) Parameter'); + components = { k: jwk.k, kty: jwk.kty }; + break; + default: + throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported'); + } + const data = encoder.encode(JSON.stringify(components)); + return encode(await digest_default(digestAlgorithm, data)); + } + async function calculateJwkThumbprintUri(jwk, digestAlgorithm) { + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : digestAlgorithm = "sha256"; + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm); + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`; + } + + // dist/browser/jwk/embedded.js + async function EmbeddedJWK(protectedHeader, token) { + const joseHeader = { + ...protectedHeader, + ...token === null || token === void 0 ? void 0 : token.header + }; + if (!isObject(joseHeader.jwk)) { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object'); + } + const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg, true); + if (key instanceof Uint8Array || key.type !== "public") { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key'); + } + return key; + } + + // dist/browser/jwks/local.js + function getKtyFromAlg(alg) { + switch (typeof alg === "string" && alg.slice(0, 2)) { + case "RS": + case "PS": + return "RSA"; + case "ES": + return "EC"; + case "Ed": + return "OKP"; + default: + throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set'); + } + } + function isJWKSLike(jwks) { + return jwks && typeof jwks === "object" && Array.isArray(jwks.keys) && jwks.keys.every(isJWKLike); + } + function isJWKLike(key) { + return isObject(key); + } + function clone(obj) { + if (typeof structuredClone === "function") { + return structuredClone(obj); + } + return JSON.parse(JSON.stringify(obj)); + } + var LocalJWKSet = class { + constructor(jwks) { + this._cached = /* @__PURE__ */ new WeakMap(); + if (!isJWKSLike(jwks)) { + throw new JWKSInvalid("JSON Web Key Set malformed"); + } + this._jwks = clone(jwks); + } + async getKey(protectedHeader, token) { + const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header }; + const kty = getKtyFromAlg(alg); + const candidates = this._jwks.keys.filter((jwk2) => { + let candidate = kty === jwk2.kty; + if (candidate && typeof kid === "string") { + candidate = kid === jwk2.kid; + } + if (candidate && typeof jwk2.alg === "string") { + candidate = alg === jwk2.alg; + } + if (candidate && typeof jwk2.use === "string") { + candidate = jwk2.use === "sig"; + } + if (candidate && Array.isArray(jwk2.key_ops)) { + candidate = jwk2.key_ops.includes("verify"); + } + if (candidate && alg === "EdDSA") { + candidate = jwk2.crv === "Ed25519" || jwk2.crv === "Ed448"; + } + if (candidate) { + switch (alg) { + case "ES256": + candidate = jwk2.crv === "P-256"; + break; + case "ES256K": + candidate = jwk2.crv === "secp256k1"; + break; + case "ES384": + candidate = jwk2.crv === "P-384"; + break; + case "ES512": + candidate = jwk2.crv === "P-521"; + break; + } + } + return candidate; + }); + const { 0: jwk, length } = candidates; + if (length === 0) { + throw new JWKSNoMatchingKey(); + } else if (length !== 1) { + const error = new JWKSMultipleMatchingKeys(); + const { _cached } = this; + error[Symbol.asyncIterator] = async function* () { + for (const jwk2 of candidates) { + try { + yield await importWithAlgCache(_cached, jwk2, alg); + } catch (_a) { + continue; + } + } + }; + throw error; + } + return importWithAlgCache(this._cached, jwk, alg); + } + }; + async function importWithAlgCache(cache, jwk, alg) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk); + if (cached[alg] === void 0) { + const key = await importJWK({ ...jwk, ext: true }, alg); + if (key instanceof Uint8Array || key.type !== "public") { + throw new JWKSInvalid("JSON Web Key Set members must be public keys"); + } + cached[alg] = key; + } + return cached[alg]; + } + function createLocalJWKSet(jwks) { + const set = new LocalJWKSet(jwks); + return async function(protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; + } + + // dist/browser/runtime/fetch_jwks.js + var fetchJwks = async (url, timeout, options) => { + let controller; + let id; + let timedOut = false; + if (typeof AbortController === "function") { + controller = new AbortController(); + id = setTimeout(() => { + timedOut = true; + controller.abort(); + }, timeout); + } + const response = await fetch(url.href, { + signal: controller ? controller.signal : void 0, + redirect: "manual", + headers: options.headers + }).catch((err) => { + if (timedOut) + throw new JWKSTimeout(); + throw err; + }); + if (id !== void 0) + clearTimeout(id); + if (response.status !== 200) { + throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response"); + } + try { + return await response.json(); + } catch (_a) { + throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON"); + } + }; + var fetch_jwks_default = fetchJwks; + + // dist/browser/jwks/remote.js + var RemoteJWKSet = class extends LocalJWKSet { + constructor(url, options) { + super({ keys: [] }); + this._jwks = void 0; + if (!(url instanceof URL)) { + throw new TypeError("url must be an instance of URL"); + } + this._url = new URL(url.href); + this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers }; + this._timeoutDuration = typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === "number" ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5e3; + this._cooldownDuration = typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === "number" ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 3e4; + this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === "number" ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 6e5; + } + coolingDown() { + return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cooldownDuration : false; + } + fresh() { + return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cacheMaxAge : false; + } + async getKey(protectedHeader, token) { + if (!this._jwks || !this.fresh()) { + await this.reload(); + } + try { + return await super.getKey(protectedHeader, token); + } catch (err) { + if (err instanceof JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload(); + return super.getKey(protectedHeader, token); + } + } + throw err; + } + } + async reload() { + if (this._pendingFetch && isCloudflareWorkers()) { + this._pendingFetch = void 0; + } + this._pendingFetch || (this._pendingFetch = fetch_jwks_default(this._url, this._timeoutDuration, this._options).then((json) => { + if (!isJWKSLike(json)) { + throw new JWKSInvalid("JSON Web Key Set malformed"); + } + this._jwks = { keys: json.keys }; + this._jwksTimestamp = Date.now(); + this._pendingFetch = void 0; + }).catch((err) => { + this._pendingFetch = void 0; + throw err; + })); + await this._pendingFetch; + } + }; + function createRemoteJWKSet(url, options) { + const set = new RemoteJWKSet(url, options); + return async function(protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; + } + + // dist/browser/jwt/unsecured.js + var UnsecuredJWT = class extends ProduceJWT { + encode() { + const header = encode(JSON.stringify({ alg: "none" })); + const payload = encode(JSON.stringify(this._payload)); + return `${header}.${payload}.`; + } + static decode(jwt, options) { + if (typeof jwt !== "string") { + throw new JWTInvalid("Unsecured JWT must be a string"); + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split("."); + if (length !== 3 || signature !== "") { + throw new JWTInvalid("Invalid Unsecured JWT"); + } + let header; + try { + header = JSON.parse(decoder.decode(decode(encodedHeader))); + if (header.alg !== "none") + throw new Error(); + } catch (_a) { + throw new JWTInvalid("Invalid Unsecured JWT"); + } + const payload = jwt_claims_set_default(header, decode(encodedPayload), options); + return { payload, header }; + } + }; + + // dist/browser/util/base64url.js + var base64url_exports2 = {}; + __export(base64url_exports2, { + decode: () => decode2, + encode: () => encode2 + }); + var encode2 = encode; + var decode2 = decode; + + // dist/browser/util/decode_protected_header.js + function decodeProtectedHeader(token) { + let protectedB64u; + if (typeof token === "string") { + const parts = token.split("."); + if (parts.length === 3 || parts.length === 5) { + [protectedB64u] = parts; + } + } else if (typeof token === "object" && token) { + if ("protected" in token) { + protectedB64u = token.protected; + } else { + throw new TypeError("Token does not contain a Protected Header"); + } + } + try { + if (typeof protectedB64u !== "string" || !protectedB64u) { + throw new Error(); + } + const result = JSON.parse(decoder.decode(decode2(protectedB64u))); + if (!isObject(result)) { + throw new Error(); + } + return result; + } catch (_a) { + throw new TypeError("Invalid Token or Protected Header formatting"); + } + } + + // dist/browser/util/decode_jwt.js + function decodeJwt(jwt) { + if (typeof jwt !== "string") + throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string"); + const { 1: payload, length } = jwt.split("."); + if (length === 5) + throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded"); + if (length !== 3) + throw new JWTInvalid("Invalid JWT"); + if (!payload) + throw new JWTInvalid("JWTs must contain a payload"); + let decoded; + try { + decoded = decode2(payload); + } catch (_a) { + throw new JWTInvalid("Failed to parse the base64url encoded payload"); + } + let result; + try { + result = JSON.parse(decoder.decode(decoded)); + } catch (_b) { + throw new JWTInvalid("Failed to parse the decoded payload as JSON"); + } + if (!isObject(result)) + throw new JWTInvalid("Invalid JWT Claims Set"); + return result; + } + + // dist/browser/runtime/generate.js + async function generateSecret(alg, options) { + var _a; + let length; + let algorithm; + let keyUsages; + switch (alg) { + case "HS256": + case "HS384": + case "HS512": + length = parseInt(alg.slice(-3), 10); + algorithm = { name: "HMAC", hash: `SHA-${length}`, length }; + keyUsages = ["sign", "verify"]; + break; + case "A128CBC-HS256": + case "A192CBC-HS384": + case "A256CBC-HS512": + length = parseInt(alg.slice(-3), 10); + return random_default(new Uint8Array(length >> 3)); + case "A128KW": + case "A192KW": + case "A256KW": + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: "AES-KW", length }; + keyUsages = ["wrapKey", "unwrapKey"]; + break; + case "A128GCMKW": + case "A192GCMKW": + case "A256GCMKW": + case "A128GCM": + case "A192GCM": + case "A256GCM": + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: "AES-GCM", length }; + keyUsages = ["encrypt", "decrypt"]; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + return webcrypto_default.subtle.generateKey(algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); + } + function getModulusLengthOption(options) { + var _a; + const modulusLength = (_a = options === null || options === void 0 ? void 0 : options.modulusLength) !== null && _a !== void 0 ? _a : 2048; + if (typeof modulusLength !== "number" || modulusLength < 2048) { + throw new JOSENotSupported("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used"); + } + return modulusLength; + } + async function generateKeyPair(alg, options) { + var _a, _b, _c, _d; + let algorithm; + let keyUsages; + switch (alg) { + case "PS256": + case "PS384": + case "PS512": + algorithm = { + name: "RSA-PSS", + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["sign", "verify"]; + break; + case "RS256": + case "RS384": + case "RS512": + algorithm = { + name: "RSASSA-PKCS1-v1_5", + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["sign", "verify"]; + break; + case "RSA-OAEP": + case "RSA-OAEP-256": + case "RSA-OAEP-384": + case "RSA-OAEP-512": + algorithm = { + name: "RSA-OAEP", + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + publicExponent: new Uint8Array([1, 0, 1]), + modulusLength: getModulusLengthOption(options) + }; + keyUsages = ["decrypt", "unwrapKey", "encrypt", "wrapKey"]; + break; + case "ES256": + algorithm = { name: "ECDSA", namedCurve: "P-256" }; + keyUsages = ["sign", "verify"]; + break; + case "ES384": + algorithm = { name: "ECDSA", namedCurve: "P-384" }; + keyUsages = ["sign", "verify"]; + break; + case "ES512": + algorithm = { name: "ECDSA", namedCurve: "P-521" }; + keyUsages = ["sign", "verify"]; + break; + case "EdDSA": + keyUsages = ["sign", "verify"]; + const crv = (_a = options === null || options === void 0 ? void 0 : options.crv) !== null && _a !== void 0 ? _a : "Ed25519"; + switch (crv) { + case "Ed25519": + case "Ed448": + algorithm = { name: crv }; + break; + default: + throw new JOSENotSupported("Invalid or unsupported crv option provided"); + } + break; + case "ECDH-ES": + case "ECDH-ES+A128KW": + case "ECDH-ES+A192KW": + case "ECDH-ES+A256KW": { + keyUsages = ["deriveKey", "deriveBits"]; + const crv2 = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : "P-256"; + switch (crv2) { + case "P-256": + case "P-384": + case "P-521": { + algorithm = { name: "ECDH", namedCurve: crv2 }; + break; + } + case "X25519": + case "X448": + algorithm = { name: crv2 }; + break; + default: + throw new JOSENotSupported("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448"); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + try { + return await webcrypto_default.subtle.generateKey(algorithm, (_c = options === null || options === void 0 ? void 0 : options.extractable) !== null && _c !== void 0 ? _c : false, keyUsages); + } catch (err) { + if (algorithm.name === "Ed25519" && (err === null || err === void 0 ? void 0 : err.name) === "NotSupportedError" && isCloudflareWorkers()) { + algorithm = { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + return await webcrypto_default.subtle.generateKey(algorithm, (_d = options === null || options === void 0 ? void 0 : options.extractable) !== null && _d !== void 0 ? _d : false, keyUsages); + } + throw err; + } + } + + // dist/browser/key/generate_key_pair.js + async function generateKeyPair2(alg, options) { + return generateKeyPair(alg, options); + } + + // dist/browser/key/generate_secret.js + async function generateSecret2(alg, options) { + return generateSecret(alg, options); + } + + exports.CompactEncrypt = CompactEncrypt; + exports.CompactSign = CompactSign; + exports.EmbeddedJWK = EmbeddedJWK; + exports.EncryptJWT = EncryptJWT; + exports.FlattenedEncrypt = FlattenedEncrypt; + exports.FlattenedSign = FlattenedSign; + exports.GeneralEncrypt = GeneralEncrypt; + exports.GeneralSign = GeneralSign; + exports.SignJWT = SignJWT; + exports.UnsecuredJWT = UnsecuredJWT; + exports.base64url = base64url_exports2; + exports.calculateJwkThumbprint = calculateJwkThumbprint; + exports.calculateJwkThumbprintUri = calculateJwkThumbprintUri; + exports.compactDecrypt = compactDecrypt; + exports.compactVerify = compactVerify; + exports.createLocalJWKSet = createLocalJWKSet; + exports.createRemoteJWKSet = createRemoteJWKSet; + exports.decodeJwt = decodeJwt; + exports.decodeProtectedHeader = decodeProtectedHeader; + exports.errors = errors_exports; + exports.exportJWK = exportJWK; + exports.exportPKCS8 = exportPKCS8; + exports.exportSPKI = exportSPKI; + exports.flattenedDecrypt = flattenedDecrypt; + exports.flattenedVerify = flattenedVerify; + exports.generalDecrypt = generalDecrypt; + exports.generalVerify = generalVerify; + exports.generateKeyPair = generateKeyPair2; + exports.generateSecret = generateSecret2; + exports.importJWK = importJWK; + exports.importPKCS8 = importPKCS8; + exports.importSPKI = importSPKI; + exports.importX509 = importX509; + exports.jwtDecrypt = jwtDecrypt; + exports.jwtVerify = jwtVerify; + +})); diff --git a/dist/browser/index.umd.min.js b/dist/browser/index.umd.min.js new file mode 100644 index 0000000000..5fbfa4d38b --- /dev/null +++ b/dist/browser/index.umd.min.js @@ -0,0 +1,5 @@ +(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports):typeof define==='function'&&define.amd?define(['exports'],f):(g=typeof globalThis!=='undefined'?globalThis:g||self,f(g.jose={}));})(this,(function(exports){'use strict';var qt=Object.defineProperty;var ct=(e,t)=>{for(var r in t)qt(e,r,{get:t[r],enumerable:!0});};var f=crypto,b=e=>e instanceof CryptoKey;var Zt=async(e,t)=>{let r=`SHA-${e.slice(-3)}`;return new Uint8Array(await f.subtle.digest(r,t))},Ke=Zt;var E=new TextEncoder,v=new TextDecoder,xe=2**32;function W(...e){let t=e.reduce((o,{length:a})=>o+a,0),r=new Uint8Array(t),n=0;return e.forEach(o=>{r.set(o,n),n+=o.length;}),r}function dt(e,t){return W(E.encode(e),new Uint8Array([0]),t)}function Ne(e,t,r){if(t<0||t>=xe)throw new RangeError(`value must be >= 0 and <= ${xe-1}. Received ${t}`);e.set([t>>>24,t>>>16,t>>>8,t&255],r);}function He(e){let t=Math.floor(e/xe),r=e%xe,n=new Uint8Array(8);return Ne(n,t,0),Ne(n,r,4),n}function Ce(e){let t=new Uint8Array(4);return Ne(t,e),t}function Pe(e){return W(Ce(e.length),e)}async function pt(e,t,r){let n=Math.ceil((t>>3)/32),o=new Uint8Array(n*32);for(let a=0;a>3)}var We=e=>{let t=e;typeof t=="string"&&(t=E.encode(t));let r=32768,n=[];for(let o=0;oWe(e).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"),ke=e=>{let t=atob(e),r=new Uint8Array(t.length);for(let n=0;n{let t=e;t instanceof Uint8Array&&(t=v.decode(t)),t=t.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"");try{return ke(t)}catch(r){throw new TypeError("The input to be decoded is not correctly encoded.")}};var ft={};ct(ft,{JOSEAlgNotAllowed:()=>B,JOSEError:()=>C,JOSENotSupported:()=>l,JWEDecryptionFailed:()=>M,JWEInvalid:()=>u,JWKInvalid:()=>de,JWKSInvalid:()=>L,JWKSMultipleMatchingKeys:()=>pe,JWKSNoMatchingKey:()=>q,JWKSTimeout:()=>ue,JWSInvalid:()=>h,JWSSignatureVerificationFailed:()=>Z,JWTClaimValidationFailed:()=>J,JWTExpired:()=>re,JWTInvalid:()=>H});var C=class extends Error{static get code(){return "ERR_JOSE_GENERIC"}constructor(t){var r;super(t),this.code="ERR_JOSE_GENERIC",this.name=this.constructor.name,(r=Error.captureStackTrace)===null||r===void 0||r.call(Error,this,this.constructor);}},J=class extends C{static get code(){return "ERR_JWT_CLAIM_VALIDATION_FAILED"}constructor(t,r="unspecified",n="unspecified"){super(t),this.code="ERR_JWT_CLAIM_VALIDATION_FAILED",this.claim=r,this.reason=n;}},re=class extends C{static get code(){return "ERR_JWT_EXPIRED"}constructor(t,r="unspecified",n="unspecified"){super(t),this.code="ERR_JWT_EXPIRED",this.claim=r,this.reason=n;}},B=class extends C{constructor(){super(...arguments),this.code="ERR_JOSE_ALG_NOT_ALLOWED";}static get code(){return "ERR_JOSE_ALG_NOT_ALLOWED"}},l=class extends C{constructor(){super(...arguments),this.code="ERR_JOSE_NOT_SUPPORTED";}static get code(){return "ERR_JOSE_NOT_SUPPORTED"}},M=class extends C{constructor(){super(...arguments),this.code="ERR_JWE_DECRYPTION_FAILED",this.message="decryption operation failed";}static get code(){return "ERR_JWE_DECRYPTION_FAILED"}},u=class extends C{constructor(){super(...arguments),this.code="ERR_JWE_INVALID";}static get code(){return "ERR_JWE_INVALID"}},h=class extends C{constructor(){super(...arguments),this.code="ERR_JWS_INVALID";}static get code(){return "ERR_JWS_INVALID"}},H=class extends C{constructor(){super(...arguments),this.code="ERR_JWT_INVALID";}static get code(){return "ERR_JWT_INVALID"}},de=class extends C{constructor(){super(...arguments),this.code="ERR_JWK_INVALID";}static get code(){return "ERR_JWK_INVALID"}},L=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_INVALID";}static get code(){return "ERR_JWKS_INVALID"}},q=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_NO_MATCHING_KEY",this.message="no applicable key found in the JSON Web Key Set";}static get code(){return "ERR_JWKS_NO_MATCHING_KEY"}},pe=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS",this.message="multiple matching keys found in the JSON Web Key Set";}static get code(){return "ERR_JWKS_MULTIPLE_MATCHING_KEYS"}},ue=class extends C{constructor(){super(...arguments),this.code="ERR_JWKS_TIMEOUT",this.message="request timed out";}static get code(){return "ERR_JWKS_TIMEOUT"}},Z=class extends C{constructor(){super(...arguments),this.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED",this.message="signature verification failed";}static get code(){return "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}};var $=f.getRandomValues.bind(f);function Be(e){switch(e){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 l(`Unsupported JWE Algorithm: ${e}`)}}var Je=e=>$(new Uint8Array(Be(e)>>3));var Qt=(e,t)=>{if(t.length<<3!==Be(e))throw new u("Invalid Initialization Vector length")},Te=Qt;var jt=(e,t)=>{let r=e.byteLength<<3;if(r!==t)throw new u(`Invalid Content Encryption Key length. Expected ${t} bits, got ${r} bits`)},ne=jt;var er=(e,t)=>{if(!(e instanceof Uint8Array))throw new TypeError("First argument must be a buffer");if(!(t instanceof Uint8Array))throw new TypeError("Second argument must be a buffer");if(e.length!==t.length)throw new TypeError("Input buffers must have the same length");let r=e.length,n=0,o=-1;for(;++oe.usages.includes(r))){let r="CryptoKey does not support this operation, its usages must include ";if(t.length>2){let n=t.pop();r+=`one of ${t.join(", ")}, or ${n}.`;}else t.length===2?r+=`one of ${t[0]} or ${t[1]}.`:r+=`${t[0]}.`;throw new TypeError(r)}}function ht(e,t,...r){switch(t){case"HS256":case"HS384":case"HS512":{if(!N(e.algorithm,"HMAC"))throw P("HMAC");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!N(e.algorithm,"RSASSA-PKCS1-v1_5"))throw P("RSASSA-PKCS1-v1_5");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!N(e.algorithm,"RSA-PSS"))throw P("RSA-PSS");let n=parseInt(t.slice(2),10);if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}case"EdDSA":{if(e.algorithm.name!=="Ed25519"&&e.algorithm.name!=="Ed448"){if(D()){if(N(e.algorithm,"NODE-ED25519"))break;throw P("Ed25519, Ed448, or NODE-ED25519")}throw P("Ed25519 or Ed448")}break}case"ES256":case"ES384":case"ES512":{if(!N(e.algorithm,"ECDSA"))throw P("ECDSA");let n=tr(t);if(e.algorithm.namedCurve!==n)throw P(n,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}mt(e,r);}function I(e,t,...r){switch(t){case"A128GCM":case"A192GCM":case"A256GCM":{if(!N(e.algorithm,"AES-GCM"))throw P("AES-GCM");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw P(n,"algorithm.length");break}case"A128KW":case"A192KW":case"A256KW":{if(!N(e.algorithm,"AES-KW"))throw P("AES-KW");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw P(n,"algorithm.length");break}case"ECDH":{switch(e.algorithm.name){case"ECDH":case"X25519":case"X448":break;default:throw P("ECDH, X25519, or X448")}break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":if(!N(e.algorithm,"PBKDF2"))throw P("PBKDF2");break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(!N(e.algorithm,"RSA-OAEP"))throw P("RSA-OAEP");let n=parseInt(t.slice(9),10)||1;if(Ie(e.algorithm.hash)!==n)throw P(`SHA-${n}`,"algorithm.hash");break}default:throw new TypeError("CryptoKey does not support this operation")}mt(e,r);}function yt(e,t,...r){if(r.length>2){let n=r.pop();e+=`one of type ${r.join(", ")}, or ${n}.`;}else r.length===2?e+=`one of type ${r[0]} or ${r[1]}.`:e+=`of type ${r[0]}.`;return t==null?e+=` Received ${t}`:typeof t=="function"&&t.name?e+=` Received function ${t.name}`:typeof t=="object"&&t!=null&&t.constructor&&t.constructor.name&&(e+=` Received an instance of ${t.constructor.name}`),e}var A=(e,...t)=>yt("Key must be ",e,...t);function Le(e,t,...r){return yt(`Key for the ${e} algorithm must be `,t,...r)}var $e=e=>b(e),y=["CryptoKey"];async function rr(e,t,r,n,o,a){if(!(t instanceof Uint8Array))throw new TypeError(A(t,"Uint8Array"));let i=parseInt(e.slice(1,4),10),s=await f.subtle.importKey("raw",t.subarray(i>>3),"AES-CBC",!1,["decrypt"]),c=await f.subtle.importKey("raw",t.subarray(0,i>>3),{hash:`SHA-${i<<1}`,name:"HMAC"},!1,["sign"]),d=W(a,n,r,He(a.length<<3)),p=new Uint8Array((await f.subtle.sign("HMAC",c,d)).slice(0,i>>3)),m;try{m=lt(o,p);}catch(T){}if(!m)throw new M;let K;try{K=new Uint8Array(await f.subtle.decrypt({iv:n,name:"AES-CBC"},s,r));}catch(T){}if(!K)throw new M;return K}async function nr(e,t,r,n,o,a){let i;t instanceof Uint8Array?i=await f.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]):(I(t,e,"decrypt"),i=t);try{return new Uint8Array(await f.subtle.decrypt({additionalData:a,iv:n,name:"AES-GCM",tagLength:128},i,W(r,o)))}catch(s){throw new M}}var or=async(e,t,r,n,o,a)=>{if(!b(t)&&!(t instanceof Uint8Array))throw new TypeError(A(t,...y,"Uint8Array"));switch(Te(e,n),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return t instanceof Uint8Array&&ne(t,parseInt(e.slice(-3),10)),rr(e,t,r,n,o,a);case"A128GCM":case"A192GCM":case"A256GCM":return t instanceof Uint8Array&&ne(t,parseInt(e.slice(1,4),10)),nr(e,t,r,n,o,a);default:throw new l("Unsupported JWE Content Encryption Algorithm")}},De=or;var wt=async()=>{throw new l('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.')},Et=async()=>{throw new l('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.')};var ar=(...e)=>{let t=e.filter(Boolean);if(t.length===0||t.length===1)return !0;let r;for(let n of t){let o=Object.keys(n);if(!r||r.size===0){r=new Set(o);continue}for(let a of o){if(r.has(a))return !1;r.add(a);}}return !0},R=ar;function ir(e){return typeof e=="object"&&e!==null}function w(e){if(!ir(e)||Object.prototype.toString.call(e)!=="[object Object]")return !1;if(Object.getPrototypeOf(e)===null)return !0;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var sr=[{hash:"SHA-256",name:"HMAC"},!0,["sign"]],oe=sr;function gt(e,t){if(e.algorithm.length!==parseInt(t.slice(1,4),10))throw new TypeError(`Invalid key size for alg: ${t}`)}function St(e,t,r){if(b(e))return I(e,t,r),e;if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"AES-KW",!0,[r]);throw new TypeError(A(e,...y,"Uint8Array"))}var fe=async(e,t,r)=>{let n=await St(t,e,"wrapKey");gt(n,e);let o=await f.subtle.importKey("raw",r,...oe);return new Uint8Array(await f.subtle.wrapKey("raw",o,n,"AES-KW"))},le=async(e,t,r)=>{let n=await St(t,e,"unwrapKey");gt(n,e);let o=await f.subtle.unwrapKey("raw",r,n,"AES-KW",...oe);return new Uint8Array(await f.subtle.exportKey("raw",o))};async function Re(e,t,r,n,o=new Uint8Array(0),a=new Uint8Array(0)){if(!b(e))throw new TypeError(A(e,...y));if(I(e,"ECDH"),!b(t))throw new TypeError(A(t,...y));I(t,"ECDH","deriveBits");let i=W(Pe(E.encode(r)),Pe(o),Pe(a),Ce(n)),s;e.algorithm.name==="X25519"?s=256:e.algorithm.name==="X448"?s=448:s=Math.ceil(parseInt(e.algorithm.namedCurve.substr(-3),10)/8)<<3;let c=new Uint8Array(await f.subtle.deriveBits({name:e.algorithm.name,public:e},t,s));return pt(c,n,i)}async function At(e){if(!b(e))throw new TypeError(A(e,...y));return f.subtle.generateKey(e.algorithm,!0,["deriveBits"])}function Oe(e){if(!b(e))throw new TypeError(A(e,...y));return ["P-256","P-384","P-521"].includes(e.algorithm.namedCurve)||e.algorithm.name==="X25519"||e.algorithm.name==="X448"}function Ge(e){if(!(e instanceof Uint8Array)||e.length<8)throw new u("PBES2 Salt Input must be 8 or more octets")}function cr(e,t){if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"PBKDF2",!1,["deriveBits"]);if(b(e))return I(e,t,"deriveBits","deriveKey"),e;throw new TypeError(A(e,...y,"Uint8Array"))}async function _t(e,t,r,n){Ge(e);let o=dt(t,e),a=parseInt(t.slice(13,16),10),i={hash:`SHA-${t.slice(8,11)}`,iterations:r,name:"PBKDF2",salt:o},s={length:a,name:"AES-KW"},c=await cr(n,t);if(c.usages.includes("deriveBits"))return new Uint8Array(await f.subtle.deriveBits(i,c,a));if(c.usages.includes("deriveKey"))return f.subtle.deriveKey(i,c,s,!1,["wrapKey","unwrapKey"]);throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"')}var vt=async(e,t,r,n=2048,o=$(new Uint8Array(16)))=>{let a=await _t(o,e,n,t);return {encryptedKey:await fe(e.slice(-6),a,r),p2c:n,p2s:g(o)}},Kt=async(e,t,r,n,o)=>{let a=await _t(o,e,n,t);return le(e.slice(-6),a,r)};function ae(e){switch(e){case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":return "RSA-OAEP";default:throw new l(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}var Q=(e,t)=>{if(e.startsWith("RS")||e.startsWith("PS")){let{modulusLength:r}=t.algorithm;if(typeof r!="number"||r<2048)throw new TypeError(`${e} requires key modulusLength to be 2048 bits or larger`)}};var xt=async(e,t,r)=>{if(!b(t))throw new TypeError(A(t,...y));if(I(t,e,"encrypt","wrapKey"),Q(e,t),t.usages.includes("encrypt"))return new Uint8Array(await f.subtle.encrypt(ae(e),t,r));if(t.usages.includes("wrapKey")){let n=await f.subtle.importKey("raw",r,...oe);return new Uint8Array(await f.subtle.wrapKey("raw",n,t,ae(e)))}throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation')},Ht=async(e,t,r)=>{if(!b(t))throw new TypeError(A(t,...y));if(I(t,e,"decrypt","unwrapKey"),Q(e,t),t.usages.includes("decrypt"))return new Uint8Array(await f.subtle.decrypt(ae(e),t,r));if(t.usages.includes("unwrapKey")){let n=await f.subtle.unwrapKey("raw",r,t,ae(e),...oe);return new Uint8Array(await f.subtle.exportKey("raw",n))}throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation')};function me(e){switch(e){case"A128GCM":return 128;case"A192GCM":return 192;case"A256GCM":case"A128CBC-HS256":return 256;case"A192CBC-HS384":return 384;case"A256CBC-HS512":return 512;default:throw new l(`Unsupported JWE Algorithm: ${e}`)}}var O=e=>$(new Uint8Array(me(e)>>3));var Ve=(e,t)=>{let r=(e.match(/.{1,64}/g)||[]).join(` +`);return `-----BEGIN ${t}----- +${r} +-----END ${t}-----`};var Wt=async(e,t,r)=>{if(!b(r))throw new TypeError(A(r,...y));if(!r.extractable)throw new TypeError("CryptoKey is not extractable");if(r.type!==e)throw new TypeError(`key is not a ${e} key`);return Ve(We(new Uint8Array(await f.subtle.exportKey(t,r))),`${e.toUpperCase()} KEY`)},Jt=e=>Wt("public","spki",e),Tt=e=>Wt("private","pkcs8",e),G=(e,t,r=0)=>{r===0&&(t.unshift(t.length),t.unshift(6));let n=e.indexOf(t[0],r);if(n===-1)return !1;let o=e.subarray(n,n+t.length);return o.length!==t.length?!1:o.every((a,i)=>a===t[i])||G(e,t,n+1)},Ct=e=>{switch(!0){case G(e,[42,134,72,206,61,3,1,7]):return "P-256";case G(e,[43,129,4,0,34]):return "P-384";case G(e,[43,129,4,0,35]):return "P-521";case G(e,[43,101,110]):return "X25519";case G(e,[43,101,111]):return "X448";case G(e,[43,101,112]):return "Ed25519";case G(e,[43,101,113]):return "Ed448";default:throw new l("Invalid or unsupported EC Key Curve or OKP Key Sub Type")}},It=async(e,t,r,n,o)=>{var a,i;let s,c,d=new Uint8Array(atob(r.replace(e,"")).split("").map(m=>m.charCodeAt(0))),p=t==="spki";switch(n){case"PS256":case"PS384":case"PS512":s={name:"RSA-PSS",hash:`SHA-${n.slice(-3)}`},c=p?["verify"]:["sign"];break;case"RS256":case"RS384":case"RS512":s={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${n.slice(-3)}`},c=p?["verify"]:["sign"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":s={name:"RSA-OAEP",hash:`SHA-${parseInt(n.slice(-3),10)||1}`},c=p?["encrypt","wrapKey"]:["decrypt","unwrapKey"];break;case"ES256":s={name:"ECDSA",namedCurve:"P-256"},c=p?["verify"]:["sign"];break;case"ES384":s={name:"ECDSA",namedCurve:"P-384"},c=p?["verify"]:["sign"];break;case"ES512":s={name:"ECDSA",namedCurve:"P-521"},c=p?["verify"]:["sign"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{let m=Ct(d);s=m.startsWith("P-")?{name:"ECDH",namedCurve:m}:{name:m},c=p?[]:["deriveBits"];break}case"EdDSA":s={name:Ct(d)},c=p?["verify"]:["sign"];break;default:throw new l('Invalid or unsupported "alg" (Algorithm) value')}try{return await f.subtle.importKey(t,d,s,(a=o==null?void 0:o.extractable)!==null&&a!==void 0?a:!1,c)}catch(m){if(s.name==="Ed25519"&&(m==null?void 0:m.name)==="NotSupportedError"&&D())return s={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.importKey(t,d,s,(i=o==null?void 0:o.extractable)!==null&&i!==void 0?i:!1,c);throw m}},Dt=(e,t,r)=>It(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g,"pkcs8",e,t,r),Fe=(e,t,r)=>It(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g,"spki",e,t,r);function Pt(e){let t=[],r=0;for(;r=128;)r=r*128+e[t]-128,t++;r=r*128+e[t]-128,t++;}let n=0;if(e[t]<128)n=e[t],t++;else if(n===128){for(n=0;e[t+n]!==0||e[t+n+1]!==0;){if(n>e.byteLength)throw new TypeError("invalid indefinite form length");n++;}let a=t+n+2;return {byteLength:a,contents:e.subarray(t,t+n),raw:e.subarray(0,a)}}else {let a=e[t]&127;t++,n=0;for(let i=0;i{let n;try{n=pr(e);}catch(o){throw new TypeError("failed to parse the X.509 certificate",{cause:o})}return Fe(n,t,r)};function ur(e){let t,r;switch(e.kty){case"oct":{switch(e.alg){case"HS256":case"HS384":case"HS512":t={name:"HMAC",hash:`SHA-${e.alg.slice(-3)}`},r=["sign","verify"];break;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":throw new l(`${e.alg} keys cannot be imported as CryptoKey instances`);case"A128GCM":case"A192GCM":case"A256GCM":case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":t={name:"AES-GCM"},r=["encrypt","decrypt"];break;case"A128KW":case"A192KW":case"A256KW":t={name:"AES-KW"},r=["wrapKey","unwrapKey"];break;case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":t={name:"PBKDF2"},r=["deriveBits"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"RSA":{switch(e.alg){case"PS256":case"PS384":case"PS512":t={name:"RSA-PSS",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":t={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":t={name:"RSA-OAEP",hash:`SHA-${parseInt(e.alg.slice(-3),10)||1}`},r=e.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"EC":{switch(e.alg){case"ES256":t={name:"ECDSA",namedCurve:"P-256"},r=e.d?["sign"]:["verify"];break;case"ES384":t={name:"ECDSA",namedCurve:"P-384"},r=e.d?["sign"]:["verify"];break;case"ES512":t={name:"ECDSA",namedCurve:"P-521"},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:"ECDH",namedCurve:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"OKP":{switch(e.alg){case"EdDSA":t={name:e.crv},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}default:throw new l('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return {algorithm:t,keyUsages:r}}var fr=async e=>{var t,r;if(!e.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');let{algorithm:n,keyUsages:o}=ur(e),a=[n,(t=e.ext)!==null&&t!==void 0?t:!1,(r=e.key_ops)!==null&&r!==void 0?r:o];if(n.name==="PBKDF2")return f.subtle.importKey("raw",S(e.k),...a);let i={...e};delete i.alg,delete i.use;try{return await f.subtle.importKey("jwk",i,...a)}catch(s){if(n.name==="Ed25519"&&(s==null?void 0:s.name)==="NotSupportedError"&&D())return a[0]={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.importKey("jwk",i,...a);throw s}},ze=fr;async function lr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PUBLIC KEY-----")!==0)throw new TypeError('"spki" must be SPKI formatted string');return Fe(e,t,r)}async function mr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN CERTIFICATE-----")!==0)throw new TypeError('"x509" must be X.509 formatted string');return Ot(e,t,r)}async function hr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PRIVATE KEY-----")!==0)throw new TypeError('"pkcs8" must be PKCS#8 formatted string');return Dt(e,t,r)}async function j(e,t,r){var n;if(!w(e))throw new TypeError("JWK must be an object");switch(t||(t=e.alg),e.kty){case"oct":if(typeof e.k!="string"||!e.k)throw new TypeError('missing "k" (Key Value) Parameter value');return r!=null||(r=e.ext!==!0),r?ze({...e,alg:t,ext:(n=e.ext)!==null&&n!==void 0?n:!1}):S(e.k);case"RSA":if(e.oth!==void 0)throw new l('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');case"EC":case"OKP":return ze({...e,alg:t});default:throw new l('Unsupported "kty" (Key Type) Parameter value')}}var yr=(e,t)=>{if(!(t instanceof Uint8Array)){if(!$e(t))throw new TypeError(Le(e,t,...y,"Uint8Array"));if(t.type!=="secret")throw new TypeError(`${y.join(" or ")} instances for symmetric algorithms must be of type "secret"`)}},wr=(e,t,r)=>{if(!$e(t))throw new TypeError(Le(e,t,...y));if(t.type==="secret")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithms must not be of type "secret"`);if(r==="sign"&&t.type==="public")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm signing must be of type "private"`);if(r==="decrypt"&&t.type==="public")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm decryption must be of type "private"`);if(t.algorithm&&r==="verify"&&t.type==="private")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm verifying must be of type "public"`);if(t.algorithm&&r==="encrypt"&&t.type==="private")throw new TypeError(`${y.join(" or ")} instances for asymmetric algorithm encryption must be of type "public"`)},Er=(e,t,r)=>{e.startsWith("HS")||e==="dir"||e.startsWith("PBES2")||/^A\d{3}(?:GCM)?KW$/.test(e)?yr(e,t):wr(e,t,r);},V=Er;async function gr(e,t,r,n,o){if(!(r instanceof Uint8Array))throw new TypeError(A(r,"Uint8Array"));let a=parseInt(e.slice(1,4),10),i=await f.subtle.importKey("raw",r.subarray(a>>3),"AES-CBC",!1,["encrypt"]),s=await f.subtle.importKey("raw",r.subarray(0,a>>3),{hash:`SHA-${a<<1}`,name:"HMAC"},!1,["sign"]),c=new Uint8Array(await f.subtle.encrypt({iv:n,name:"AES-CBC"},i,t)),d=W(o,n,c,He(o.length<<3)),p=new Uint8Array((await f.subtle.sign("HMAC",s,d)).slice(0,a>>3));return {ciphertext:c,tag:p}}async function Sr(e,t,r,n,o){let a;r instanceof Uint8Array?a=await f.subtle.importKey("raw",r,"AES-GCM",!1,["encrypt"]):(I(r,e,"encrypt"),a=r);let i=new Uint8Array(await f.subtle.encrypt({additionalData:o,iv:n,name:"AES-GCM",tagLength:128},a,t)),s=i.slice(-16);return {ciphertext:i.slice(0,-16),tag:s}}var Ar=async(e,t,r,n,o)=>{if(!b(r)&&!(r instanceof Uint8Array))throw new TypeError(A(r,...y,"Uint8Array"));switch(Te(e,n),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return r instanceof Uint8Array&&ne(r,parseInt(e.slice(-3),10)),gr(e,t,r,n,o);case"A128GCM":case"A192GCM":case"A256GCM":return r instanceof Uint8Array&&ne(r,parseInt(e.slice(1,4),10)),Sr(e,t,r,n,o);default:throw new l("Unsupported JWE Content Encryption Algorithm")}},he=Ar;async function Ut(e,t,r,n){let o=e.slice(0,7);n||(n=Je(o));let{ciphertext:a,tag:i}=await he(o,r,t,n,new Uint8Array(0));return {encryptedKey:a,iv:g(n),tag:g(i)}}async function Mt(e,t,r,n,o){let a=e.slice(0,7);return De(a,t,r,n,o,new Uint8Array(0))}async function br(e,t,r,n,o){switch(V(e,t,"decrypt"),e){case"dir":{if(r!==void 0)throw new u("Encountered unexpected JWE Encrypted Key");return t}case"ECDH-ES":if(r!==void 0)throw new u("Encountered unexpected JWE Encrypted Key");case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!w(n.epk))throw new u('JOSE Header "epk" (Ephemeral Public Key) missing or invalid');if(!Oe(t))throw new l("ECDH with the provided key is not allowed or not supported by your javascript runtime");let a=await j(n.epk,e),i,s;if(n.apu!==void 0){if(typeof n.apu!="string")throw new u('JOSE Header "apu" (Agreement PartyUInfo) invalid');i=S(n.apu);}if(n.apv!==void 0){if(typeof n.apv!="string")throw new u('JOSE Header "apv" (Agreement PartyVInfo) invalid');s=S(n.apv);}let c=await Re(a,t,e==="ECDH-ES"?n.enc:e,e==="ECDH-ES"?me(n.enc):parseInt(e.slice(-5,-2),10),i,s);if(e==="ECDH-ES")return c;if(r===void 0)throw new u("JWE Encrypted Key missing");return le(e.slice(-6),c,r)}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(r===void 0)throw new u("JWE Encrypted Key missing");return Ht(e,t,r)}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{if(r===void 0)throw new u("JWE Encrypted Key missing");if(typeof n.p2c!="number")throw new u('JOSE Header "p2c" (PBES2 Count) missing or invalid');let a=(o==null?void 0:o.maxPBES2Count)||1e4;if(n.p2c>a)throw new u('JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds');if(typeof n.p2s!="string")throw new u('JOSE Header "p2s" (PBES2 Salt) missing or invalid');return Kt(e,t,r,n.p2c,S(n.p2s))}case"A128KW":case"A192KW":case"A256KW":{if(r===void 0)throw new u("JWE Encrypted Key missing");return le(e,t,r)}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{if(r===void 0)throw new u("JWE Encrypted Key missing");if(typeof n.iv!="string")throw new u('JOSE Header "iv" (Initialization Vector) missing or invalid');if(typeof n.tag!="string")throw new u('JOSE Header "tag" (Authentication Tag) missing or invalid');let a=S(n.iv),i=S(n.tag);return Mt(e,t,r,a,i)}default:throw new l('Invalid or unsupported "alg" (JWE Algorithm) header value')}}var Nt=br;function _r(e,t,r,n,o){if(o.crit!==void 0&&n.crit===void 0)throw new e('"crit" (Critical) Header Parameter MUST be integrity protected');if(!n||n.crit===void 0)return new Set;if(!Array.isArray(n.crit)||n.crit.length===0||n.crit.some(i=>typeof i!="string"||i.length===0))throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let a;r!==void 0?a=new Map([...Object.entries(r),...t.entries()]):a=t;for(let i of n.crit){if(!a.has(i))throw new l(`Extension Header Parameter "${i}" is not recognized`);if(o[i]===void 0)throw new e(`Extension Header Parameter "${i}" is missing`);if(a.get(i)&&n[i]===void 0)throw new e(`Extension Header Parameter "${i}" MUST be integrity protected`)}return new Set(n.crit)}var U=_r;var vr=(e,t)=>{if(t!==void 0&&(!Array.isArray(t)||t.some(r=>typeof r!="string")))throw new TypeError(`"${e}" option must be an array of strings`);if(t)return new Set(t)},ye=vr;async function we(e,t,r){var n;if(!w(e))throw new u("Flattened JWE must be an object");if(e.protected===void 0&&e.header===void 0&&e.unprotected===void 0)throw new u("JOSE Header missing");if(typeof e.iv!="string")throw new u("JWE Initialization Vector missing or incorrect type");if(typeof e.ciphertext!="string")throw new u("JWE Ciphertext missing or incorrect type");if(typeof e.tag!="string")throw new u("JWE Authentication Tag missing or incorrect type");if(e.protected!==void 0&&typeof e.protected!="string")throw new u("JWE Protected Header incorrect type");if(e.encrypted_key!==void 0&&typeof e.encrypted_key!="string")throw new u("JWE Encrypted Key incorrect type");if(e.aad!==void 0&&typeof e.aad!="string")throw new u("JWE AAD incorrect type");if(e.header!==void 0&&!w(e.header))throw new u("JWE Shared Unprotected Header incorrect type");if(e.unprotected!==void 0&&!w(e.unprotected))throw new u("JWE Per-Recipient Unprotected Header incorrect type");let o;if(e.protected)try{let Y=S(e.protected);o=JSON.parse(v.decode(Y));}catch(Y){throw new u("JWE Protected Header is invalid")}if(!R(o,e.header,e.unprotected))throw new u("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint");let a={...o,...e.header,...e.unprotected};if(U(u,new Map,r==null?void 0:r.crit,o,a),a.zip!==void 0){if(!o||!o.zip)throw new u('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');if(a.zip!=="DEF")throw new l('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}let{alg:i,enc:s}=a;if(typeof i!="string"||!i)throw new u("missing JWE Algorithm (alg) in JWE Header");if(typeof s!="string"||!s)throw new u("missing JWE Encryption Algorithm (enc) in JWE Header");let c=r&&ye("keyManagementAlgorithms",r.keyManagementAlgorithms),d=r&&ye("contentEncryptionAlgorithms",r.contentEncryptionAlgorithms);if(c&&!c.has(i))throw new B('"alg" (Algorithm) Header Parameter not allowed');if(d&&!d.has(s))throw new B('"enc" (Encryption Algorithm) Header Parameter not allowed');let p;e.encrypted_key!==void 0&&(p=S(e.encrypted_key));let m=!1;typeof t=="function"&&(t=await t(o,e),m=!0);let K;try{K=await Nt(i,t,p,a,r);}catch(Y){if(Y instanceof TypeError||Y instanceof u||Y instanceof l)throw Y;K=O(s);}let T=S(e.iv),x=S(e.tag),_=E.encode((n=e.protected)!==null&&n!==void 0?n:""),k;e.aad!==void 0?k=W(_,E.encode("."),E.encode(e.aad)):k=_;let Me=await De(s,K,S(e.ciphertext),T,x,k);a.zip==="DEF"&&(Me=await((r==null?void 0:r.inflateRaw)||wt)(Me));let te={plaintext:Me};return e.protected!==void 0&&(te.protectedHeader=o),e.aad!==void 0&&(te.additionalAuthenticatedData=S(e.aad)),e.unprotected!==void 0&&(te.sharedUnprotectedHeader=e.unprotected),e.header!==void 0&&(te.unprotectedHeader=e.header),m?{...te,key:t}:te}async function Xe(e,t,r){if(e instanceof Uint8Array&&(e=v.decode(e)),typeof e!="string")throw new u("Compact JWE must be a string or Uint8Array");let{0:n,1:o,2:a,3:i,4:s,length:c}=e.split(".");if(c!==5)throw new u("Invalid Compact JWE");let d=await we({ciphertext:i,iv:a||void 0,protected:n||void 0,tag:s||void 0,encrypted_key:o||void 0},t,r),p={plaintext:d.plaintext,protectedHeader:d.protectedHeader};return typeof t=="function"?{...p,key:d.key}:p}async function Kr(e,t,r){if(!w(e))throw new u("General JWE must be an object");if(!Array.isArray(e.recipients)||!e.recipients.every(w))throw new u("JWE Recipients missing or incorrect type");if(!e.recipients.length)throw new u("JWE Recipients has no members");for(let n of e.recipients)try{return await we({aad:e.aad,ciphertext:e.ciphertext,encrypted_key:n.encrypted_key,header:n.header,iv:e.iv,protected:e.protected,tag:e.tag,unprotected:e.unprotected},t,r)}catch(o){}throw new M}var xr=async e=>{if(e instanceof Uint8Array)return {kty:"oct",k:g(e)};if(!b(e))throw new TypeError(A(e,...y,"Uint8Array"));if(!e.extractable)throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");let{ext:t,key_ops:r,alg:n,use:o,...a}=await f.subtle.exportKey("jwk",e);return a},kt=xr;async function Hr(e){return Jt(e)}async function Cr(e){return Tt(e)}async function Ye(e){return kt(e)}async function Pr(e,t,r,n,o={}){let a,i,s;switch(V(e,r,"encrypt"),e){case"dir":{s=r;break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!Oe(r))throw new l("ECDH with the provided key is not allowed or not supported by your javascript runtime");let{apu:c,apv:d}=o,{epk:p}=o;p||(p=(await At(r)).privateKey);let{x:m,y:K,crv:T,kty:x}=await Ye(p),_=await Re(r,p,e==="ECDH-ES"?t:e,e==="ECDH-ES"?me(t):parseInt(e.slice(-5,-2),10),c,d);if(i={epk:{x:m,crv:T,kty:x}},x==="EC"&&(i.epk.y=K),c&&(i.apu=g(c)),d&&(i.apv=g(d)),e==="ECDH-ES"){s=_;break}s=n||O(t);let k=e.slice(-6);a=await fe(k,_,s);break}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{s=n||O(t),a=await xt(e,r,s);break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{s=n||O(t);let{p2c:c,p2s:d}=o;({encryptedKey:a,...i}=await vt(e,r,s,c,d));break}case"A128KW":case"A192KW":case"A256KW":{s=n||O(t),a=await fe(e,r,s);break}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{s=n||O(t);let{iv:c}=o;({encryptedKey:a,...i}=await Ut(e,r,s,c));break}default:throw new l('Invalid or unsupported "alg" (JWE Algorithm) header value')}return {cek:s,encryptedKey:a,parameters:i}}var Ue=Pr;var qe=Symbol(),F=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("plaintext must be an instance of Uint8Array");this._plaintext=t;}setKeyManagementParameters(t){if(this._keyManagementParameters)throw new TypeError("setKeyManagementParameters can only be called once");return this._keyManagementParameters=t,this}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._sharedUnprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._sharedUnprotectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}setContentEncryptionKey(t){if(this._cek)throw new TypeError("setContentEncryptionKey can only be called once");return this._cek=t,this}setInitializationVector(t){if(this._iv)throw new TypeError("setInitializationVector can only be called once");return this._iv=t,this}async encrypt(t,r){if(!this._protectedHeader&&!this._unprotectedHeader&&!this._sharedUnprotectedHeader)throw new u("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()");if(!R(this._protectedHeader,this._unprotectedHeader,this._sharedUnprotectedHeader))throw new u("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader,...this._sharedUnprotectedHeader};if(U(u,new Map,r==null?void 0:r.crit,this._protectedHeader,n),n.zip!==void 0){if(!this._protectedHeader||!this._protectedHeader.zip)throw new u('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');if(n.zip!=="DEF")throw new l('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}let{alg:o,enc:a}=n;if(typeof o!="string"||!o)throw new u('JWE "alg" (Algorithm) Header Parameter missing or invalid');if(typeof a!="string"||!a)throw new u('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');let i;if(o==="dir"){if(this._cek)throw new TypeError("setContentEncryptionKey cannot be called when using Direct Encryption")}else if(o==="ECDH-ES"&&this._cek)throw new TypeError("setContentEncryptionKey cannot be called when using Direct Key Agreement");let s;{let x;(({cek:s,encryptedKey:i,parameters:x}=await Ue(o,a,t,this._cek,this._keyManagementParameters))),x&&(r&&qe in r?this._unprotectedHeader?this._unprotectedHeader={...this._unprotectedHeader,...x}:this.setUnprotectedHeader(x):this._protectedHeader?this._protectedHeader={...this._protectedHeader,...x}:this.setProtectedHeader(x));}this._iv||(this._iv=Je(a));let c,d,p;this._protectedHeader?d=E.encode(g(JSON.stringify(this._protectedHeader))):d=E.encode(""),this._aad?(p=g(this._aad),c=W(d,E.encode("."),E.encode(p))):c=d;let m,K;if(n.zip==="DEF"){let x=await((r==null?void 0:r.deflateRaw)||Et)(this._plaintext);({ciphertext:m,tag:K}=await he(a,x,s,this._iv,c));}else ({ciphertext:m,tag:K}=await he(a,this._plaintext,s,this._iv,c));let T={ciphertext:g(m),iv:g(this._iv),tag:g(K)};return i&&(T.encrypted_key=g(i)),p&&(T.aad=p),this._protectedHeader&&(T.protected=v.decode(d)),this._sharedUnprotectedHeader&&(T.unprotected=this._sharedUnprotectedHeader),this._unprotectedHeader&&(T.header=this._unprotectedHeader),T}};var Ze=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n;}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addRecipient(...t){return this.parent.addRecipient(...t)}encrypt(...t){return this.parent.encrypt(...t)}done(){return this.parent}},Qe=class{constructor(t){this._recipients=[],this._plaintext=t;}addRecipient(t,r){let n=new Ze(this,t,{crit:r==null?void 0:r.crit});return this._recipients.push(n),n}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}async encrypt(t){var r,n,o;if(!this._recipients.length)throw new u("at least one recipient must be added");if(t={deflateRaw:t==null?void 0:t.deflateRaw},this._recipients.length===1){let[c]=this._recipients,d=await new F(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(c.unprotectedHeader).encrypt(c.key,{...c.options,...t}),p={ciphertext:d.ciphertext,iv:d.iv,recipients:[{}],tag:d.tag};return d.aad&&(p.aad=d.aad),d.protected&&(p.protected=d.protected),d.unprotected&&(p.unprotected=d.unprotected),d.encrypted_key&&(p.recipients[0].encrypted_key=d.encrypted_key),d.header&&(p.recipients[0].header=d.header),p}let a;for(let c=0;c>3};case"RS256":case"RS384":case"RS512":return {hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return {hash:r,name:"ECDSA",namedCurve:t.namedCurve};case"EdDSA":return D()&&t.name==="NODE-ED25519"?{name:"NODE-ED25519",namedCurve:"NODE-ED25519"}:{name:t.name};default:throw new l(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}function ge(e,t,r){if(b(t))return ht(t,e,r),t;if(t instanceof Uint8Array){if(!e.startsWith("HS"))throw new TypeError(A(t,...y));return f.subtle.importKey("raw",t,{hash:`SHA-${e.slice(-3)}`,name:"HMAC"},!1,[r])}throw new TypeError(A(t,...y,"Uint8Array"))}var Wr=async(e,t,r,n)=>{let o=await ge(e,t,"verify");Q(e,o);let a=Ee(e,o.algorithm);try{return await f.subtle.verify(a,o,r,n)}catch(i){return !1}},Bt=Wr;async function Se(e,t,r){var n;if(!w(e))throw new h("Flattened JWS must be an object");if(e.protected===void 0&&e.header===void 0)throw new h('Flattened JWS must have either of the "protected" or "header" members');if(e.protected!==void 0&&typeof e.protected!="string")throw new h("JWS Protected Header incorrect type");if(e.payload===void 0)throw new h("JWS Payload missing");if(typeof e.signature!="string")throw new h("JWS Signature missing or incorrect type");if(e.header!==void 0&&!w(e.header))throw new h("JWS Unprotected Header incorrect type");let o={};if(e.protected)try{let k=S(e.protected);o=JSON.parse(v.decode(k));}catch(k){throw new h("JWS Protected Header is invalid")}if(!R(o,e.header))throw new h("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let a={...o,...e.header},i=U(h,new Map([["b64",!0]]),r==null?void 0:r.crit,o,a),s=!0;if(i.has("b64")&&(s=o.b64,typeof s!="boolean"))throw new h('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:c}=a;if(typeof c!="string"||!c)throw new h('JWS "alg" (Algorithm) Header Parameter missing or invalid');let d=r&&ye("algorithms",r.algorithms);if(d&&!d.has(c))throw new B('"alg" (Algorithm) Header Parameter not allowed');if(s){if(typeof e.payload!="string")throw new h("JWS Payload must be a string")}else if(typeof e.payload!="string"&&!(e.payload instanceof Uint8Array))throw new h("JWS Payload must be a string or an Uint8Array instance");let p=!1;typeof t=="function"&&(t=await t(o,e),p=!0),V(c,t,"verify");let m=W(E.encode((n=e.protected)!==null&&n!==void 0?n:""),E.encode("."),typeof e.payload=="string"?E.encode(e.payload):e.payload),K=S(e.signature);if(!await Bt(c,t,K,m))throw new Z;let x;s?x=S(e.payload):typeof e.payload=="string"?x=E.encode(e.payload):x=e.payload;let _={payload:x};return e.protected!==void 0&&(_.protectedHeader=o),e.header!==void 0&&(_.unprotectedHeader=e.header),p?{..._,key:t}:_}async function je(e,t,r){if(e instanceof Uint8Array&&(e=v.decode(e)),typeof e!="string")throw new h("Compact JWS must be a string or Uint8Array");let{0:n,1:o,2:a,length:i}=e.split(".");if(i!==3)throw new h("Invalid Compact JWS");let s=await Se({payload:o,protected:n,signature:a},t,r),c={payload:s.payload,protectedHeader:s.protectedHeader};return typeof t=="function"?{...c,key:s.key}:c}async function Jr(e,t,r){if(!w(e))throw new h("General JWS must be an object");if(!Array.isArray(e.signatures)||!e.signatures.every(w))throw new h("JWS Signatures missing or incorrect type");for(let n of e.signatures)try{return await Se({header:n.header,payload:e.payload,protected:n.protected,signature:n.signature},t,r)}catch(o){}throw new Z}var ie=e=>Math.floor(e.getTime()/1e3);var Tr=/^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i,se=e=>{let t=Tr.exec(e);if(!t)throw new TypeError("Invalid time period format");let r=parseFloat(t[1]);switch(t[2].toLowerCase()){case"sec":case"secs":case"second":case"seconds":case"s":return Math.round(r);case"minute":case"minutes":case"min":case"mins":case"m":return Math.round(r*60);case"hour":case"hours":case"hr":case"hrs":case"h":return Math.round(r*3600);case"day":case"days":case"d":return Math.round(r*86400);case"week":case"weeks":case"w":return Math.round(r*604800);default:return Math.round(r*31557600)}};var Lt=e=>e.toLowerCase().replace(/^application\//,""),Ir=(e,t)=>typeof e=="string"?t.includes(e):Array.isArray(e)?t.some(Set.prototype.has.bind(new Set(e))):!1,ce=(e,t,r={})=>{let{typ:n}=r;if(n&&(typeof e.typ!="string"||Lt(e.typ)!==Lt(n)))throw new J('unexpected "typ" JWT header value',"typ","check_failed");let o;try{o=JSON.parse(v.decode(t));}catch(m){}if(!w(o))throw new H("JWT Claims Set must be a top-level JSON object");let{issuer:a}=r;if(a&&!(Array.isArray(a)?a:[a]).includes(o.iss))throw new J('unexpected "iss" claim value',"iss","check_failed");let{subject:i}=r;if(i&&o.sub!==i)throw new J('unexpected "sub" claim value',"sub","check_failed");let{audience:s}=r;if(s&&!Ir(o.aud,typeof s=="string"?[s]:s))throw new J('unexpected "aud" claim value',"aud","check_failed");let c;switch(typeof r.clockTolerance){case"string":c=se(r.clockTolerance);break;case"number":c=r.clockTolerance;break;case"undefined":c=0;break;default:throw new TypeError("Invalid clockTolerance option type")}let{currentDate:d}=r,p=ie(d||new Date);if((o.iat!==void 0||r.maxTokenAge)&&typeof o.iat!="number")throw new J('"iat" claim must be a number',"iat","invalid");if(o.nbf!==void 0){if(typeof o.nbf!="number")throw new J('"nbf" claim must be a number',"nbf","invalid");if(o.nbf>p+c)throw new J('"nbf" claim timestamp check failed',"nbf","check_failed")}if(o.exp!==void 0){if(typeof o.exp!="number")throw new J('"exp" claim must be a number',"exp","invalid");if(o.exp<=p-c)throw new re('"exp" claim timestamp check failed',"exp","check_failed")}if(r.maxTokenAge){let m=p-o.iat,K=typeof r.maxTokenAge=="number"?r.maxTokenAge:se(r.maxTokenAge);if(m-c>K)throw new re('"iat" claim timestamp check failed (too far in the past)',"iat","check_failed");if(m<0-c)throw new J('"iat" claim timestamp check failed (it should be in the past)',"iat","check_failed")}return o};async function Dr(e,t,r){var n;let o=await je(e,t,r);if(!((n=o.protectedHeader.crit)===null||n===void 0)&&n.includes("b64")&&o.protectedHeader.b64===!1)throw new H("JWTs MUST NOT use unencoded payload");let i={payload:ce(o.protectedHeader,o.payload,r),protectedHeader:o.protectedHeader};return typeof t=="function"?{...i,key:o.key}:i}async function Rr(e,t,r){let n=await Xe(e,t,r),o=ce(n.protectedHeader,n.plaintext,r),{protectedHeader:a}=n;if(a.iss!==void 0&&a.iss!==o.iss)throw new J('replicated "iss" claim header parameter mismatch',"iss","mismatch");if(a.sub!==void 0&&a.sub!==o.sub)throw new J('replicated "sub" claim header parameter mismatch',"sub","mismatch");if(a.aud!==void 0&&JSON.stringify(a.aud)!==JSON.stringify(o.aud))throw new J('replicated "aud" claim header parameter mismatch',"aud","mismatch");let i={payload:o,protectedHeader:a};return typeof t=="function"?{...i,key:n.key}:i}var Ae=class{constructor(t){this._flattened=new F(t);}setContentEncryptionKey(t){return this._flattened.setContentEncryptionKey(t),this}setInitializationVector(t){return this._flattened.setInitializationVector(t),this}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}setKeyManagementParameters(t){return this._flattened.setKeyManagementParameters(t),this}async encrypt(t,r){let n=await this._flattened.encrypt(t,r);return [n.protected,n.encrypted_key,n.iv,n.ciphertext,n.tag].join(".")}};var Or=async(e,t,r)=>{let n=await ge(e,t,"sign");Q(e,n);let o=await f.subtle.sign(Ee(e,n.algorithm),n,r);return new Uint8Array(o)},$t=Or;var ee=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("payload must be an instance of Uint8Array");this._payload=t;}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}async sign(t,r){if(!this._protectedHeader&&!this._unprotectedHeader)throw new h("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");if(!R(this._protectedHeader,this._unprotectedHeader))throw new h("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader},o=U(h,new Map([["b64",!0]]),r==null?void 0:r.crit,this._protectedHeader,n),a=!0;if(o.has("b64")&&(a=this._protectedHeader.b64,typeof a!="boolean"))throw new h('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:i}=n;if(typeof i!="string"||!i)throw new h('JWS "alg" (Algorithm) Header Parameter missing or invalid');V(i,t,"sign");let s=this._payload;a&&(s=E.encode(g(s)));let c;this._protectedHeader?c=E.encode(g(JSON.stringify(this._protectedHeader))):c=E.encode("");let d=W(c,E.encode("."),s),p=await $t(i,t,d),m={signature:g(p),payload:""};return a&&(m.payload=v.decode(s)),this._unprotectedHeader&&(m.header=this._unprotectedHeader),this._protectedHeader&&(m.protected=v.decode(c)),m}};var be=class{constructor(t){this._flattened=new ee(t);}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}async sign(t,r){let n=await this._flattened.sign(t,r);if(n.payload===void 0)throw new TypeError("use the flattened module for creating JWS with b64: false");return `${n.protected}.${n.payload}.${n.signature}`}};var et=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n;}setProtectedHeader(t){if(this.protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this.protectedHeader=t,this}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addSignature(...t){return this.parent.addSignature(...t)}sign(...t){return this.parent.sign(...t)}done(){return this.parent}},tt=class{constructor(t){this._signatures=[],this._payload=t;}addSignature(t,r){let n=new et(this,t,r);return this._signatures.push(n),n}async sign(){if(!this._signatures.length)throw new h("at least one signature must be added");let t={signatures:[],payload:""};for(let r=0;r{if(typeof e!="string"||!e)throw new de(`${t} missing or invalid`)};async function Gt(e,t){if(!w(e))throw new TypeError("JWK must be an object");if(t!=null||(t="sha256"),t!=="sha256"&&t!=="sha384"&&t!=="sha512")throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');let r;switch(e.kty){case"EC":X(e.crv,'"crv" (Curve) Parameter'),X(e.x,'"x" (X Coordinate) Parameter'),X(e.y,'"y" (Y Coordinate) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x,y:e.y};break;case"OKP":X(e.crv,'"crv" (Subtype of Key Pair) Parameter'),X(e.x,'"x" (Public Key) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x};break;case"RSA":X(e.e,'"e" (Exponent) Parameter'),X(e.n,'"n" (Modulus) Parameter'),r={e:e.e,kty:e.kty,n:e.n};break;case"oct":X(e.k,'"k" (Key Value) Parameter'),r={k:e.k,kty:e.kty};break;default:throw new l('"kty" (Key Type) Parameter missing or unsupported')}let n=E.encode(JSON.stringify(r));return g(await Ke(t,n))}async function Ur(e,t){t!=null||(t="sha256");let r=await Gt(e,t);return `urn:ietf:params:oauth:jwk-thumbprint:sha-${t.slice(-3)}:${r}`}async function Mr(e,t){let r={...e,...t==null?void 0:t.header};if(!w(r.jwk))throw new h('"jwk" (JSON Web Key) Header Parameter must be a JSON object');let n=await j({...r.jwk,ext:!0},r.alg,!0);if(n instanceof Uint8Array||n.type!=="public")throw new h('"jwk" (JSON Web Key) Header Parameter must be a public key');return n}function Nr(e){switch(typeof e=="string"&&e.slice(0,2)){case"RS":case"PS":return "RSA";case"ES":return "EC";case"Ed":return "OKP";default:throw new l('Unsupported "alg" value for a JSON Web Key Set')}}function ot(e){return e&&typeof e=="object"&&Array.isArray(e.keys)&&e.keys.every(kr)}function kr(e){return w(e)}function Br(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}var _e=class{constructor(t){if(this._cached=new WeakMap,!ot(t))throw new L("JSON Web Key Set malformed");this._jwks=Br(t);}async getKey(t,r){let{alg:n,kid:o}={...t,...r==null?void 0:r.header},a=Nr(n),i=this._jwks.keys.filter(d=>{let p=a===d.kty;if(p&&typeof o=="string"&&(p=o===d.kid),p&&typeof d.alg=="string"&&(p=n===d.alg),p&&typeof d.use=="string"&&(p=d.use==="sig"),p&&Array.isArray(d.key_ops)&&(p=d.key_ops.includes("verify")),p&&n==="EdDSA"&&(p=d.crv==="Ed25519"||d.crv==="Ed448"),p)switch(n){case"ES256":p=d.crv==="P-256";break;case"ES256K":p=d.crv==="secp256k1";break;case"ES384":p=d.crv==="P-384";break;case"ES512":p=d.crv==="P-521";break}return p}),{0:s,length:c}=i;if(c===0)throw new q;if(c!==1){let d=new pe,{_cached:p}=this;throw d[Symbol.asyncIterator]=async function*(){for(let m of i)try{yield await Vt(p,m,n);}catch(K){continue}},d}return Vt(this._cached,s,n)}};async function Vt(e,t,r){let n=e.get(t)||e.set(t,{}).get(t);if(n[r]===void 0){let o=await j({...t,ext:!0},r);if(o instanceof Uint8Array||o.type!=="public")throw new L("JSON Web Key Set members must be public keys");n[r]=o;}return n[r]}function Lr(e){let t=new _e(e);return async function(r,n){return t.getKey(r,n)}}var $r=async(e,t,r)=>{let n,o,a=!1;typeof AbortController=="function"&&(n=new AbortController,o=setTimeout(()=>{a=!0,n.abort();},t));let i=await fetch(e.href,{signal:n?n.signal:void 0,redirect:"manual",headers:r.headers}).catch(s=>{throw a?new ue:s});if(o!==void 0&&clearTimeout(o),i.status!==200)throw new C("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await i.json()}catch(s){throw new C("Failed to parse the JSON Web Key Set HTTP response as JSON")}},Ft=$r;var at=class extends _e{constructor(t,r){if(super({keys:[]}),this._jwks=void 0,!(t instanceof URL))throw new TypeError("url must be an instance of URL");this._url=new URL(t.href),this._options={agent:r==null?void 0:r.agent,headers:r==null?void 0:r.headers},this._timeoutDuration=typeof(r==null?void 0:r.timeoutDuration)=="number"?r==null?void 0:r.timeoutDuration:5e3,this._cooldownDuration=typeof(r==null?void 0:r.cooldownDuration)=="number"?r==null?void 0:r.cooldownDuration:3e4,this._cacheMaxAge=typeof(r==null?void 0:r.cacheMaxAge)=="number"?r==null?void 0:r.cacheMaxAge:6e5;}coolingDown(){return typeof this._jwksTimestamp=="number"?Date.now(){if(!ot(t))throw new L("JSON Web Key Set malformed");this._jwks={keys:t.keys},this._jwksTimestamp=Date.now(),this._pendingFetch=void 0;}).catch(t=>{throw this._pendingFetch=void 0,t})),await this._pendingFetch;}};function Gr(e,t){let r=new at(e,t);return async function(n,o){return r.getKey(n,o)}}var it=class extends z{encode(){let t=g(JSON.stringify({alg:"none"})),r=g(JSON.stringify(this._payload));return `${t}.${r}.`}static decode(t,r){if(typeof t!="string")throw new H("Unsecured JWT must be a string");let{0:n,1:o,2:a,length:i}=t.split(".");if(i!==3||a!=="")throw new H("Invalid Unsecured JWT");let s;try{if(s=JSON.parse(v.decode(S(n))),s.alg!=="none")throw new Error}catch(d){throw new H("Invalid Unsecured JWT")}return {payload:ce(s,S(o),r),header:s}}};var zt={};ct(zt,{decode:()=>ve,encode:()=>Vr});var Vr=g,ve=S;function Fr(e){let t;if(typeof e=="string"){let r=e.split(".");(r.length===3||r.length===5)&&([t]=r);}else if(typeof e=="object"&&e)if("protected"in e)t=e.protected;else throw new TypeError("Token does not contain a Protected Header");try{if(typeof t!="string"||!t)throw new Error;let r=JSON.parse(v.decode(ve(t)));if(!w(r))throw new Error;return r}catch(r){throw new TypeError("Invalid Token or Protected Header formatting")}}function zr(e){if(typeof e!="string")throw new H("JWTs must use Compact JWS serialization, JWT must be a string");let{1:t,length:r}=e.split(".");if(r===5)throw new H("Only JWTs using Compact JWS serialization can be decoded");if(r!==3)throw new H("Invalid JWT");if(!t)throw new H("JWTs must contain a payload");let n;try{n=ve(t);}catch(a){throw new H("Failed to parse the base64url encoded payload")}let o;try{o=JSON.parse(v.decode(n));}catch(a){throw new H("Failed to parse the decoded payload as JSON")}if(!w(o))throw new H("Invalid JWT Claims Set");return o}async function Xt(e,t){var r;let n,o,a;switch(e){case"HS256":case"HS384":case"HS512":n=parseInt(e.slice(-3),10),o={name:"HMAC",hash:`SHA-${n}`,length:n},a=["sign","verify"];break;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return n=parseInt(e.slice(-3),10),$(new Uint8Array(n>>3));case"A128KW":case"A192KW":case"A256KW":n=parseInt(e.slice(1,4),10),o={name:"AES-KW",length:n},a=["wrapKey","unwrapKey"];break;case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":case"A128GCM":case"A192GCM":case"A256GCM":n=parseInt(e.slice(1,4),10),o={name:"AES-GCM",length:n},a=["encrypt","decrypt"];break;default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return f.subtle.generateKey(o,(r=t==null?void 0:t.extractable)!==null&&r!==void 0?r:!1,a)}function st(e){var t;let r=(t=e==null?void 0:e.modulusLength)!==null&&t!==void 0?t:2048;if(typeof r!="number"||r<2048)throw new l("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used");return r}async function Yt(e,t){var r,n,o,a;let i,s;switch(e){case"PS256":case"PS384":case"PS512":i={name:"RSA-PSS",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["sign","verify"];break;case"RS256":case"RS384":case"RS512":i={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["sign","verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":i={name:"RSA-OAEP",hash:`SHA-${parseInt(e.slice(-3),10)||1}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:st(t)},s=["decrypt","unwrapKey","encrypt","wrapKey"];break;case"ES256":i={name:"ECDSA",namedCurve:"P-256"},s=["sign","verify"];break;case"ES384":i={name:"ECDSA",namedCurve:"P-384"},s=["sign","verify"];break;case"ES512":i={name:"ECDSA",namedCurve:"P-521"},s=["sign","verify"];break;case"EdDSA":s=["sign","verify"];let c=(r=t==null?void 0:t.crv)!==null&&r!==void 0?r:"Ed25519";switch(c){case"Ed25519":case"Ed448":i={name:c};break;default:throw new l("Invalid or unsupported crv option provided")}break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{s=["deriveKey","deriveBits"];let d=(n=t==null?void 0:t.crv)!==null&&n!==void 0?n:"P-256";switch(d){case"P-256":case"P-384":case"P-521":{i={name:"ECDH",namedCurve:d};break}case"X25519":case"X448":i={name:d};break;default:throw new l("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448")}break}default:throw new l('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}try{return await f.subtle.generateKey(i,(o=t==null?void 0:t.extractable)!==null&&o!==void 0?o:!1,s)}catch(c){if(i.name==="Ed25519"&&(c==null?void 0:c.name)==="NotSupportedError"&&D())return i={name:"NODE-ED25519",namedCurve:"NODE-ED25519"},await f.subtle.generateKey(i,(a=t==null?void 0:t.extractable)!==null&&a!==void 0?a:!1,s);throw c}}async function Xr(e,t){return Yt(e,t)}async function Yr(e,t){return Xt(e,t)} +exports.CompactEncrypt=Ae;exports.CompactSign=be;exports.EmbeddedJWK=Mr;exports.EncryptJWT=nt;exports.FlattenedEncrypt=F;exports.FlattenedSign=ee;exports.GeneralEncrypt=Qe;exports.GeneralSign=tt;exports.SignJWT=rt;exports.UnsecuredJWT=it;exports.base64url=zt;exports.calculateJwkThumbprint=Gt;exports.calculateJwkThumbprintUri=Ur;exports.compactDecrypt=Xe;exports.compactVerify=je;exports.createLocalJWKSet=Lr;exports.createRemoteJWKSet=Gr;exports.decodeJwt=zr;exports.decodeProtectedHeader=Fr;exports.errors=ft;exports.exportJWK=Ye;exports.exportPKCS8=Cr;exports.exportSPKI=Hr;exports.flattenedDecrypt=we;exports.flattenedVerify=Se;exports.generalDecrypt=Kr;exports.generalVerify=Jr;exports.generateKeyPair=Xr;exports.generateSecret=Yr;exports.importJWK=j;exports.importPKCS8=hr;exports.importSPKI=lr;exports.importX509=mr;exports.jwtDecrypt=Rr;exports.jwtVerify=Dr;})); \ No newline at end of file diff --git a/dist/browser/jwe/compact/decrypt.js b/dist/browser/jwe/compact/decrypt.js new file mode 100644 index 0000000000..129aeb6e39 --- /dev/null +++ b/dist/browser/jwe/compact/decrypt.js @@ -0,0 +1,27 @@ +import { flattenedDecrypt } from '../flattened/decrypt.js'; +import { JWEInvalid } from '../../util/errors.js'; +import { decoder } from '../../lib/buffer_utils.js'; +export async function compactDecrypt(jwe, key, options) { + if (jwe instanceof Uint8Array) { + jwe = decoder.decode(jwe); + } + if (typeof jwe !== 'string') { + throw new JWEInvalid('Compact JWE must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length, } = jwe.split('.'); + if (length !== 5) { + throw new JWEInvalid('Invalid Compact JWE'); + } + const decrypted = await flattenedDecrypt({ + ciphertext, + iv: (iv || undefined), + protected: protectedHeader || undefined, + tag: (tag || undefined), + encrypted_key: encryptedKey || undefined, + }, key, options); + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} diff --git a/dist/browser/jwe/compact/encrypt.js b/dist/browser/jwe/compact/encrypt.js new file mode 100644 index 0000000000..e689139465 --- /dev/null +++ b/dist/browser/jwe/compact/encrypt.js @@ -0,0 +1,26 @@ +import { FlattenedEncrypt } from '../flattened/encrypt.js'; +export class CompactEncrypt { + constructor(plaintext) { + this._flattened = new FlattenedEncrypt(plaintext); + } + setContentEncryptionKey(cek) { + this._flattened.setContentEncryptionKey(cek); + return this; + } + setInitializationVector(iv) { + this._flattened.setInitializationVector(iv); + return this; + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + setKeyManagementParameters(parameters) { + this._flattened.setKeyManagementParameters(parameters); + return this; + } + async encrypt(key, options) { + const jwe = await this._flattened.encrypt(key, options); + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.'); + } +} diff --git a/dist/browser/jwe/flattened/decrypt.js b/dist/browser/jwe/flattened/decrypt.js new file mode 100644 index 0000000000..2373a48f54 --- /dev/null +++ b/dist/browser/jwe/flattened/decrypt.js @@ -0,0 +1,137 @@ +import { decode as base64url } from '../../runtime/base64url.js'; +import decrypt from '../../runtime/decrypt.js'; +import { inflate } from '../../runtime/zlib.js'; +import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import isObject from '../../lib/is_object.js'; +import decryptKeyManagement from '../../lib/decrypt_key_management.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import generateCek from '../../lib/cek.js'; +import validateCrit from '../../lib/validate_crit.js'; +import validateAlgorithms from '../../lib/validate_algorithms.js'; +export async function flattenedDecrypt(jwe, key, options) { + var _a; + if (!isObject(jwe)) { + throw new JWEInvalid('Flattened JWE must be an object'); + } + if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { + throw new JWEInvalid('JOSE Header missing'); + } + if (typeof jwe.iv !== 'string') { + throw new JWEInvalid('JWE Initialization Vector missing or incorrect type'); + } + if (typeof jwe.ciphertext !== 'string') { + throw new JWEInvalid('JWE Ciphertext missing or incorrect type'); + } + if (typeof jwe.tag !== 'string') { + throw new JWEInvalid('JWE Authentication Tag missing or incorrect type'); + } + if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { + throw new JWEInvalid('JWE Protected Header incorrect type'); + } + if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { + throw new JWEInvalid('JWE Encrypted Key incorrect type'); + } + if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { + throw new JWEInvalid('JWE AAD incorrect type'); + } + if (jwe.header !== undefined && !isObject(jwe.header)) { + throw new JWEInvalid('JWE Shared Unprotected Header incorrect type'); + } + if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { + throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); + } + let parsedProt; + if (jwe.protected) { + try { + const protectedHeader = base64url(jwe.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } + catch (_b) { + throw new JWEInvalid('JWE Protected Header is invalid'); + } + } + if (!isDisjoint(parsedProt, jwe.header, jwe.unprotected)) { + throw new JWEInvalid('JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected, + }; + validateCrit(JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + if (joseHeader.zip !== undefined) { + if (!parsedProt || !parsedProt.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); + } + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); + } + const keyManagementAlgorithms = options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms); + const contentEncryptionAlgorithms = options && + validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms); + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed'); + } + let encryptedKey; + if (jwe.encrypted_key !== undefined) { + encryptedKey = base64url(jwe.encrypted_key); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jwe); + resolvedKey = true; + } + let cek; + try { + cek = await decryptKeyManagement(alg, key, encryptedKey, joseHeader, options); + } + catch (err) { + if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { + throw err; + } + cek = generateCek(enc); + } + const iv = base64url(jwe.iv); + const tag = base64url(jwe.tag); + const protectedHeader = encoder.encode((_a = jwe.protected) !== null && _a !== void 0 ? _a : ''); + let additionalData; + if (jwe.aad !== undefined) { + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)); + } + else { + additionalData = protectedHeader; + } + let plaintext = await decrypt(enc, cek, base64url(jwe.ciphertext), iv, tag, additionalData); + if (joseHeader.zip === 'DEF') { + plaintext = await ((options === null || options === void 0 ? void 0 : options.inflateRaw) || inflate)(plaintext); + } + const result = { plaintext }; + if (jwe.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jwe.aad !== undefined) { + result.additionalAuthenticatedData = base64url(jwe.aad); + } + if (jwe.unprotected !== undefined) { + result.sharedUnprotectedHeader = jwe.unprotected; + } + if (jwe.header !== undefined) { + result.unprotectedHeader = jwe.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} diff --git a/dist/browser/jwe/flattened/encrypt.js b/dist/browser/jwe/flattened/encrypt.js new file mode 100644 index 0000000000..1e60ecd8c4 --- /dev/null +++ b/dist/browser/jwe/flattened/encrypt.js @@ -0,0 +1,175 @@ +import { encode as base64url } from '../../runtime/base64url.js'; +import encrypt from '../../runtime/encrypt.js'; +import { deflate } from '../../runtime/zlib.js'; +import generateIv from '../../lib/iv.js'; +import encryptKeyManagement from '../../lib/encrypt_key_management.js'; +import { JOSENotSupported, JWEInvalid } from '../../util/errors.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import validateCrit from '../../lib/validate_crit.js'; +export const unprotected = Symbol(); +export class FlattenedEncrypt { + constructor(plaintext) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError('plaintext must be an instance of Uint8Array'); + } + this._plaintext = plaintext; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._sharedUnprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + async encrypt(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()'); + } + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) { + throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader, + }; + validateCrit(JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + let encryptedKey; + if (alg === 'dir') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption'); + } + } + else if (alg === 'ECDH-ES') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement'); + } + } + let cek; + { + let parameters; + ({ cek, encryptedKey, parameters } = await encryptKeyManagement(alg, enc, key, this._cek, this._keyManagementParameters)); + if (parameters) { + if (options && unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters); + } + else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters }; + } + } + else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters); + } + else { + this._protectedHeader = { ...this._protectedHeader, ...parameters }; + } + } + } + } + this._iv || (this._iv = generateIv(enc)); + let additionalData; + let protectedHeader; + let aadMember; + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = encoder.encode(''); + } + if (this._aad) { + aadMember = base64url(this._aad); + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(aadMember)); + } + else { + additionalData = protectedHeader; + } + let ciphertext; + let tag; + if (joseHeader.zip === 'DEF') { + const deflated = await ((options === null || options === void 0 ? void 0 : options.deflateRaw) || deflate)(this._plaintext); + ({ ciphertext, tag } = await encrypt(enc, deflated, cek, this._iv, additionalData)); + } + else { + ; + ({ ciphertext, tag } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData)); + } + const jwe = { + ciphertext: base64url(ciphertext), + iv: base64url(this._iv), + tag: base64url(tag), + }; + if (encryptedKey) { + jwe.encrypted_key = base64url(encryptedKey); + } + if (aadMember) { + jwe.aad = aadMember; + } + if (this._protectedHeader) { + jwe.protected = decoder.decode(protectedHeader); + } + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader; + } + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader; + } + return jwe; + } +} diff --git a/dist/browser/jwe/general/decrypt.js b/dist/browser/jwe/general/decrypt.js new file mode 100644 index 0000000000..d21b6d1fa8 --- /dev/null +++ b/dist/browser/jwe/general/decrypt.js @@ -0,0 +1,31 @@ +import { flattenedDecrypt } from '../flattened/decrypt.js'; +import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.js'; +import isObject from '../../lib/is_object.js'; +export async function generalDecrypt(jwe, key, options) { + if (!isObject(jwe)) { + throw new JWEInvalid('General JWE must be an object'); + } + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { + throw new JWEInvalid('JWE Recipients missing or incorrect type'); + } + if (!jwe.recipients.length) { + throw new JWEInvalid('JWE Recipients has no members'); + } + for (const recipient of jwe.recipients) { + try { + return await flattenedDecrypt({ + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected, + }, key, options); + } + catch (_a) { + } + } + throw new JWEDecryptionFailed(); +} diff --git a/dist/browser/jwe/general/encrypt.js b/dist/browser/jwe/general/encrypt.js new file mode 100644 index 0000000000..3ee53dec44 --- /dev/null +++ b/dist/browser/jwe/general/encrypt.js @@ -0,0 +1,178 @@ +import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.js'; +import { JWEInvalid } from '../../util/errors.js'; +import generateCek from '../../lib/cek.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import encryptKeyManagement from '../../lib/encrypt_key_management.js'; +import { encode as base64url } from '../../runtime/base64url.js'; +import validateCrit from '../../lib/validate_crit.js'; +class IndividualRecipient { + constructor(enc, key, options) { + this.parent = enc; + this.key = key; + this.options = options; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addRecipient(...args) { + return this.parent.addRecipient(...args); + } + encrypt(...args) { + return this.parent.encrypt(...args); + } + done() { + return this.parent; + } +} +export class GeneralEncrypt { + constructor(plaintext) { + this._recipients = []; + this._plaintext = plaintext; + } + addRecipient(key, options) { + const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit }); + this._recipients.push(recipient); + return recipient; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = sharedUnprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + async encrypt(options) { + var _a, _b, _c; + if (!this._recipients.length) { + throw new JWEInvalid('at least one recipient must be added'); + } + options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw }; + if (this._recipients.length === 1) { + const [recipient] = this._recipients; + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .encrypt(recipient.key, { ...recipient.options, ...options }); + let jwe = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag, + }; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + if (flattened.encrypted_key) + jwe.recipients[0].encrypted_key = flattened.encrypted_key; + if (flattened.header) + jwe.recipients[0].header = flattened.header; + return jwe; + } + let enc; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) { + throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (alg === 'dir' || alg === 'ECDH-ES') { + throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient'); + } + if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + if (!enc) { + enc = joseHeader.enc; + } + else if (enc !== joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients'); + } + validateCrit(JWEInvalid, new Map(), recipient.options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + } + } + const cek = generateCek(enc); + let jwe = { + ciphertext: '', + iv: '', + recipients: [], + tag: '', + }; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + const target = {}; + jwe.recipients.push(target); + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const p2c = joseHeader.alg.startsWith('PBES2') ? 2048 + i : undefined; + if (i === 0) { + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setContentEncryptionKey(cek) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .setKeyManagementParameters({ p2c }) + .encrypt(recipient.key, { + ...recipient.options, + ...options, + [unprotected]: true, + }); + jwe.ciphertext = flattened.ciphertext; + jwe.iv = flattened.iv; + jwe.tag = flattened.tag; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + target.encrypted_key = flattened.encrypted_key; + if (flattened.header) + target.header = flattened.header; + continue; + } + const { encryptedKey, parameters } = await encryptKeyManagement(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || + ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) || + ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c }); + target.encrypted_key = base64url(encryptedKey); + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters }; + } + return jwe; + } +} diff --git a/dist/browser/jwk/embedded.js b/dist/browser/jwk/embedded.js new file mode 100644 index 0000000000..561ae2505b --- /dev/null +++ b/dist/browser/jwk/embedded.js @@ -0,0 +1,17 @@ +import { importJWK } from '../key/import.js'; +import isObject from '../lib/is_object.js'; +import { JWSInvalid } from '../util/errors.js'; +export async function EmbeddedJWK(protectedHeader, token) { + const joseHeader = { + ...protectedHeader, + ...token === null || token === void 0 ? void 0 : token.header, + }; + if (!isObject(joseHeader.jwk)) { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object'); + } + const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg, true); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key'); + } + return key; +} diff --git a/dist/browser/jwk/thumbprint.js b/dist/browser/jwk/thumbprint.js new file mode 100644 index 0000000000..49f86b1743 --- /dev/null +++ b/dist/browser/jwk/thumbprint.js @@ -0,0 +1,53 @@ +import digest from '../runtime/digest.js'; +import { encode as base64url } from '../runtime/base64url.js'; +import { JOSENotSupported, JWKInvalid } from '../util/errors.js'; +import { encoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +const check = (value, description) => { + if (typeof value !== 'string' || !value) { + throw new JWKInvalid(`${description} missing or invalid`); + } +}; +export async function calculateJwkThumbprint(jwk, digestAlgorithm) { + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object'); + } + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + if (digestAlgorithm !== 'sha256' && + digestAlgorithm !== 'sha384' && + digestAlgorithm !== 'sha512') { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"'); + } + let components; + switch (jwk.kty) { + case 'EC': + check(jwk.crv, '"crv" (Curve) Parameter'); + check(jwk.x, '"x" (X Coordinate) Parameter'); + check(jwk.y, '"y" (Y Coordinate) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; + break; + case 'OKP': + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'); + check(jwk.x, '"x" (Public Key) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; + break; + case 'RSA': + check(jwk.e, '"e" (Exponent) Parameter'); + check(jwk.n, '"n" (Modulus) Parameter'); + components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; + break; + case 'oct': + check(jwk.k, '"k" (Key Value) Parameter'); + components = { k: jwk.k, kty: jwk.kty }; + break; + default: + throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported'); + } + const data = encoder.encode(JSON.stringify(components)); + return base64url(await digest(digestAlgorithm, data)); +} +export async function calculateJwkThumbprintUri(jwk, digestAlgorithm) { + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm); + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`; +} diff --git a/dist/browser/jwks/local.js b/dist/browser/jwks/local.js new file mode 100644 index 0000000000..292aa27768 --- /dev/null +++ b/dist/browser/jwks/local.js @@ -0,0 +1,116 @@ +import { importJWK } from '../key/import.js'; +import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js'; +import isObject from '../lib/is_object.js'; +function getKtyFromAlg(alg) { + switch (typeof alg === 'string' && alg.slice(0, 2)) { + case 'RS': + case 'PS': + return 'RSA'; + case 'ES': + return 'EC'; + case 'Ed': + return 'OKP'; + default: + throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set'); + } +} +export function isJWKSLike(jwks) { + return (jwks && + typeof jwks === 'object' && + Array.isArray(jwks.keys) && + jwks.keys.every(isJWKLike)); +} +function isJWKLike(key) { + return isObject(key); +} +function clone(obj) { + if (typeof structuredClone === 'function') { + return structuredClone(obj); + } + return JSON.parse(JSON.stringify(obj)); +} +export class LocalJWKSet { + constructor(jwks) { + this._cached = new WeakMap(); + if (!isJWKSLike(jwks)) { + throw new JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = clone(jwks); + } + async getKey(protectedHeader, token) { + const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header }; + const kty = getKtyFromAlg(alg); + const candidates = this._jwks.keys.filter((jwk) => { + let candidate = kty === jwk.kty; + if (candidate && typeof kid === 'string') { + candidate = kid === jwk.kid; + } + if (candidate && typeof jwk.alg === 'string') { + candidate = alg === jwk.alg; + } + if (candidate && typeof jwk.use === 'string') { + candidate = jwk.use === 'sig'; + } + if (candidate && Array.isArray(jwk.key_ops)) { + candidate = jwk.key_ops.includes('verify'); + } + if (candidate && alg === 'EdDSA') { + candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448'; + } + if (candidate) { + switch (alg) { + case 'ES256': + candidate = jwk.crv === 'P-256'; + break; + case 'ES256K': + candidate = jwk.crv === 'secp256k1'; + break; + case 'ES384': + candidate = jwk.crv === 'P-384'; + break; + case 'ES512': + candidate = jwk.crv === 'P-521'; + break; + } + } + return candidate; + }); + const { 0: jwk, length } = candidates; + if (length === 0) { + throw new JWKSNoMatchingKey(); + } + else if (length !== 1) { + const error = new JWKSMultipleMatchingKeys(); + const { _cached } = this; + error[Symbol.asyncIterator] = async function* () { + for (const jwk of candidates) { + try { + yield await importWithAlgCache(_cached, jwk, alg); + } + catch (_a) { + continue; + } + } + }; + throw error; + } + return importWithAlgCache(this._cached, jwk, alg); + } +} +async function importWithAlgCache(cache, jwk, alg) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk); + if (cached[alg] === undefined) { + const key = await importJWK({ ...jwk, ext: true }, alg); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWKSInvalid('JSON Web Key Set members must be public keys'); + } + cached[alg] = key; + } + return cached[alg]; +} +export function createLocalJWKSet(jwks) { + const set = new LocalJWKSet(jwks); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} diff --git a/dist/browser/jwks/remote.js b/dist/browser/jwks/remote.js new file mode 100644 index 0000000000..d370579412 --- /dev/null +++ b/dist/browser/jwks/remote.js @@ -0,0 +1,72 @@ +import fetchJwks from '../runtime/fetch_jwks.js'; +import { isCloudflareWorkers } from '../runtime/env.js'; +import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js'; +import { isJWKSLike, LocalJWKSet } from './local.js'; +class RemoteJWKSet extends LocalJWKSet { + constructor(url, options) { + super({ keys: [] }); + this._jwks = undefined; + if (!(url instanceof URL)) { + throw new TypeError('url must be an instance of URL'); + } + this._url = new URL(url.href); + this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers }; + this._timeoutDuration = + typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; + this._cooldownDuration = + typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 30000; + this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === 'number' ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 600000; + } + coolingDown() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cooldownDuration + : false; + } + fresh() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cacheMaxAge + : false; + } + async getKey(protectedHeader, token) { + if (!this._jwks || !this.fresh()) { + await this.reload(); + } + try { + return await super.getKey(protectedHeader, token); + } + catch (err) { + if (err instanceof JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload(); + return super.getKey(protectedHeader, token); + } + } + throw err; + } + } + async reload() { + if (this._pendingFetch && isCloudflareWorkers()) { + this._pendingFetch = undefined; + } + this._pendingFetch || (this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options) + .then((json) => { + if (!isJWKSLike(json)) { + throw new JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = { keys: json.keys }; + this._jwksTimestamp = Date.now(); + this._pendingFetch = undefined; + }) + .catch((err) => { + this._pendingFetch = undefined; + throw err; + })); + await this._pendingFetch; + } +} +export function createRemoteJWKSet(url, options) { + const set = new RemoteJWKSet(url, options); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} diff --git a/dist/browser/jws/compact/sign.js b/dist/browser/jws/compact/sign.js new file mode 100644 index 0000000000..b8e5ba0e2b --- /dev/null +++ b/dist/browser/jws/compact/sign.js @@ -0,0 +1,17 @@ +import { FlattenedSign } from '../flattened/sign.js'; +export class CompactSign { + constructor(payload) { + this._flattened = new FlattenedSign(payload); + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + async sign(key, options) { + const jws = await this._flattened.sign(key, options); + if (jws.payload === undefined) { + throw new TypeError('use the flattened module for creating JWS with b64: false'); + } + return `${jws.protected}.${jws.payload}.${jws.signature}`; + } +} diff --git a/dist/browser/jws/compact/verify.js b/dist/browser/jws/compact/verify.js new file mode 100644 index 0000000000..c651ffb944 --- /dev/null +++ b/dist/browser/jws/compact/verify.js @@ -0,0 +1,21 @@ +import { flattenedVerify } from '../flattened/verify.js'; +import { JWSInvalid } from '../../util/errors.js'; +import { decoder } from '../../lib/buffer_utils.js'; +export async function compactVerify(jws, key, options) { + if (jws instanceof Uint8Array) { + jws = decoder.decode(jws); + } + if (typeof jws !== 'string') { + throw new JWSInvalid('Compact JWS must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); + if (length !== 3) { + throw new JWSInvalid('Invalid Compact JWS'); + } + const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options); + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} diff --git a/dist/browser/jws/flattened/sign.js b/dist/browser/jws/flattened/sign.js new file mode 100644 index 0000000000..76ae289654 --- /dev/null +++ b/dist/browser/jws/flattened/sign.js @@ -0,0 +1,81 @@ +import { encode as base64url } from '../../runtime/base64url.js'; +import sign from '../../runtime/sign.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import { JWSInvalid } from '../../util/errors.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import checkKeyType from '../../lib/check_key_type.js'; +import validateCrit from '../../lib/validate_crit.js'; +export class FlattenedSign { + constructor(payload) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError('payload must be an instance of Uint8Array'); + } + this._payload = payload; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + async sign(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); + } + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader)) { + throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + }; + const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = this._protectedHeader.b64; + if (typeof b64 !== 'boolean') { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + checkKeyType(alg, key, 'sign'); + let payload = this._payload; + if (b64) { + payload = encoder.encode(base64url(payload)); + } + let protectedHeader; + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = encoder.encode(''); + } + const data = concat(protectedHeader, encoder.encode('.'), payload); + const signature = await sign(alg, key, data); + const jws = { + signature: base64url(signature), + payload: '', + }; + if (b64) { + jws.payload = decoder.decode(payload); + } + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader; + } + if (this._protectedHeader) { + jws.protected = decoder.decode(protectedHeader); + } + return jws; + } +} diff --git a/dist/browser/jws/flattened/verify.js b/dist/browser/jws/flattened/verify.js new file mode 100644 index 0000000000..a55287ed8e --- /dev/null +++ b/dist/browser/jws/flattened/verify.js @@ -0,0 +1,104 @@ +import { decode as base64url } from '../../runtime/base64url.js'; +import verify from '../../runtime/verify.js'; +import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; +import { concat, encoder, decoder } from '../../lib/buffer_utils.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import isObject from '../../lib/is_object.js'; +import checkKeyType from '../../lib/check_key_type.js'; +import validateCrit from '../../lib/validate_crit.js'; +import validateAlgorithms from '../../lib/validate_algorithms.js'; +export async function flattenedVerify(jws, key, options) { + var _a; + if (!isObject(jws)) { + throw new JWSInvalid('Flattened JWS must be an object'); + } + if (jws.protected === undefined && jws.header === undefined) { + throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); + } + if (jws.protected !== undefined && typeof jws.protected !== 'string') { + throw new JWSInvalid('JWS Protected Header incorrect type'); + } + if (jws.payload === undefined) { + throw new JWSInvalid('JWS Payload missing'); + } + if (typeof jws.signature !== 'string') { + throw new JWSInvalid('JWS Signature missing or incorrect type'); + } + if (jws.header !== undefined && !isObject(jws.header)) { + throw new JWSInvalid('JWS Unprotected Header incorrect type'); + } + let parsedProt = {}; + if (jws.protected) { + try { + const protectedHeader = base64url(jws.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } + catch (_b) { + throw new JWSInvalid('JWS Protected Header is invalid'); + } + } + if (!isDisjoint(parsedProt, jws.header)) { + throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jws.header, + }; + const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = parsedProt.b64; + if (typeof b64 !== 'boolean') { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + const algorithms = options && validateAlgorithms('algorithms', options.algorithms); + if (algorithms && !algorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (b64) { + if (typeof jws.payload !== 'string') { + throw new JWSInvalid('JWS Payload must be a string'); + } + } + else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) { + throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance'); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jws); + resolvedKey = true; + } + checkKeyType(alg, key, 'verify'); + const data = concat(encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ''), encoder.encode('.'), typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload); + const signature = base64url(jws.signature); + const verified = await verify(alg, key, signature, data); + if (!verified) { + throw new JWSSignatureVerificationFailed(); + } + let payload; + if (b64) { + payload = base64url(jws.payload); + } + else if (typeof jws.payload === 'string') { + payload = encoder.encode(jws.payload); + } + else { + payload = jws.payload; + } + const result = { payload }; + if (jws.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jws.header !== undefined) { + result.unprotectedHeader = jws.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} diff --git a/dist/browser/jws/general/sign.js b/dist/browser/jws/general/sign.js new file mode 100644 index 0000000000..0e0d645cac --- /dev/null +++ b/dist/browser/jws/general/sign.js @@ -0,0 +1,67 @@ +import { FlattenedSign } from '../flattened/sign.js'; +import { JWSInvalid } from '../../util/errors.js'; +class IndividualSignature { + constructor(sig, key, options) { + this.parent = sig; + this.key = key; + this.options = options; + } + setProtectedHeader(protectedHeader) { + if (this.protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this.protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addSignature(...args) { + return this.parent.addSignature(...args); + } + sign(...args) { + return this.parent.sign(...args); + } + done() { + return this.parent; + } +} +export class GeneralSign { + constructor(payload) { + this._signatures = []; + this._payload = payload; + } + addSignature(key, options) { + const signature = new IndividualSignature(this, key, options); + this._signatures.push(signature); + return signature; + } + async sign() { + if (!this._signatures.length) { + throw new JWSInvalid('at least one signature must be added'); + } + const jws = { + signatures: [], + payload: '', + }; + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i]; + const flattened = new FlattenedSign(this._payload); + flattened.setProtectedHeader(signature.protectedHeader); + flattened.setUnprotectedHeader(signature.unprotectedHeader); + const { payload, ...rest } = await flattened.sign(signature.key, signature.options); + if (i === 0) { + jws.payload = payload; + } + else if (jws.payload !== payload) { + throw new JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)'); + } + jws.signatures.push(rest); + } + return jws; + } +} diff --git a/dist/browser/jws/general/verify.js b/dist/browser/jws/general/verify.js new file mode 100644 index 0000000000..459090afee --- /dev/null +++ b/dist/browser/jws/general/verify.js @@ -0,0 +1,24 @@ +import { flattenedVerify } from '../flattened/verify.js'; +import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; +import isObject from '../../lib/is_object.js'; +export async function generalVerify(jws, key, options) { + if (!isObject(jws)) { + throw new JWSInvalid('General JWS must be an object'); + } + if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { + throw new JWSInvalid('JWS Signatures missing or incorrect type'); + } + for (const signature of jws.signatures) { + try { + return await flattenedVerify({ + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature, + }, key, options); + } + catch (_a) { + } + } + throw new JWSSignatureVerificationFailed(); +} diff --git a/dist/browser/jwt/decrypt.js b/dist/browser/jwt/decrypt.js new file mode 100644 index 0000000000..1ec2be28f2 --- /dev/null +++ b/dist/browser/jwt/decrypt.js @@ -0,0 +1,23 @@ +import { compactDecrypt } from '../jwe/compact/decrypt.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { JWTClaimValidationFailed } from '../util/errors.js'; +export async function jwtDecrypt(jwt, key, options) { + const decrypted = await compactDecrypt(jwt, key, options); + const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options); + const { protectedHeader } = decrypted; + if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { + throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); + } + if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { + throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); + } + if (protectedHeader.aud !== undefined && + JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { + throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); + } + const result = { payload, protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} diff --git a/dist/browser/jwt/encrypt.js b/dist/browser/jwt/encrypt.js new file mode 100644 index 0000000000..15252957ae --- /dev/null +++ b/dist/browser/jwt/encrypt.js @@ -0,0 +1,68 @@ +import { CompactEncrypt } from '../jwe/compact/encrypt.js'; +import { encoder } from '../lib/buffer_utils.js'; +import { ProduceJWT } from './produce.js'; +export class EncryptJWT extends ProduceJWT { + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true; + return this; + } + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true; + return this; + } + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true; + return this; + } + async encrypt(key, options) { + const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload))); + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss }; + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub }; + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud }; + } + enc.setProtectedHeader(this._protectedHeader); + if (this._iv) { + enc.setInitializationVector(this._iv); + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek); + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters); + } + return enc.encrypt(key, options); + } +} diff --git a/dist/browser/jwt/produce.js b/dist/browser/jwt/produce.js new file mode 100644 index 0000000000..31c929a07c --- /dev/null +++ b/dist/browser/jwt/produce.js @@ -0,0 +1,54 @@ +import epoch from '../lib/epoch.js'; +import isObject from '../lib/is_object.js'; +import secs from '../lib/secs.js'; +export class ProduceJWT { + constructor(payload) { + if (!isObject(payload)) { + throw new TypeError('JWT Claims Set MUST be an object'); + } + this._payload = payload; + } + setIssuer(issuer) { + this._payload = { ...this._payload, iss: issuer }; + return this; + } + setSubject(subject) { + this._payload = { ...this._payload, sub: subject }; + return this; + } + setAudience(audience) { + this._payload = { ...this._payload, aud: audience }; + return this; + } + setJti(jwtId) { + this._payload = { ...this._payload, jti: jwtId }; + return this; + } + setNotBefore(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, nbf: input }; + } + else { + this._payload = { ...this._payload, nbf: epoch(new Date()) + secs(input) }; + } + return this; + } + setExpirationTime(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, exp: input }; + } + else { + this._payload = { ...this._payload, exp: epoch(new Date()) + secs(input) }; + } + return this; + } + setIssuedAt(input) { + if (typeof input === 'undefined') { + this._payload = { ...this._payload, iat: epoch(new Date()) }; + } + else { + this._payload = { ...this._payload, iat: input }; + } + return this; + } +} diff --git a/dist/browser/jwt/sign.js b/dist/browser/jwt/sign.js new file mode 100644 index 0000000000..62352fbfa2 --- /dev/null +++ b/dist/browser/jwt/sign.js @@ -0,0 +1,21 @@ +import { CompactSign } from '../jws/compact/sign.js'; +import { JWTInvalid } from '../util/errors.js'; +import { encoder } from '../lib/buffer_utils.js'; +import { ProduceJWT } from './produce.js'; +export class SignJWT extends ProduceJWT { + setProtectedHeader(protectedHeader) { + this._protectedHeader = protectedHeader; + return this; + } + async sign(key, options) { + var _a; + const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload))); + sig.setProtectedHeader(this._protectedHeader); + if (Array.isArray((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.crit) && + this._protectedHeader.crit.includes('b64') && + this._protectedHeader.b64 === false) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + return sig.sign(key, options); + } +} diff --git a/dist/browser/jwt/unsecured.js b/dist/browser/jwt/unsecured.js new file mode 100644 index 0000000000..b0276512fb --- /dev/null +++ b/dist/browser/jwt/unsecured.js @@ -0,0 +1,32 @@ +import * as base64url from '../runtime/base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import { JWTInvalid } from '../util/errors.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { ProduceJWT } from './produce.js'; +export class UnsecuredJWT extends ProduceJWT { + encode() { + const header = base64url.encode(JSON.stringify({ alg: 'none' })); + const payload = base64url.encode(JSON.stringify(this._payload)); + return `${header}.${payload}.`; + } + static decode(jwt, options) { + if (typeof jwt !== 'string') { + throw new JWTInvalid('Unsecured JWT must be a string'); + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.'); + if (length !== 3 || signature !== '') { + throw new JWTInvalid('Invalid Unsecured JWT'); + } + let header; + try { + header = JSON.parse(decoder.decode(base64url.decode(encodedHeader))); + if (header.alg !== 'none') + throw new Error(); + } + catch (_a) { + throw new JWTInvalid('Invalid Unsecured JWT'); + } + const payload = jwtPayload(header, base64url.decode(encodedPayload), options); + return { payload, header }; + } +} diff --git a/dist/browser/jwt/verify.js b/dist/browser/jwt/verify.js new file mode 100644 index 0000000000..89571c1847 --- /dev/null +++ b/dist/browser/jwt/verify.js @@ -0,0 +1,16 @@ +import { compactVerify } from '../jws/compact/verify.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { JWTInvalid } from '../util/errors.js'; +export async function jwtVerify(jwt, key, options) { + var _a; + const verified = await compactVerify(jwt, key, options); + if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes('b64')) && verified.protectedHeader.b64 === false) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + const payload = jwtPayload(verified.protectedHeader, verified.payload, options); + const result = { payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} diff --git a/dist/browser/key/export.js b/dist/browser/key/export.js new file mode 100644 index 0000000000..e4017047cd --- /dev/null +++ b/dist/browser/key/export.js @@ -0,0 +1,12 @@ +import { toSPKI as exportPublic } from '../runtime/asn1.js'; +import { toPKCS8 as exportPrivate } from '../runtime/asn1.js'; +import keyToJWK from '../runtime/key_to_jwk.js'; +export async function exportSPKI(key) { + return exportPublic(key); +} +export async function exportPKCS8(key) { + return exportPrivate(key); +} +export async function exportJWK(key) { + return keyToJWK(key); +} diff --git a/dist/browser/key/generate_key_pair.js b/dist/browser/key/generate_key_pair.js new file mode 100644 index 0000000000..03b9ee54cd --- /dev/null +++ b/dist/browser/key/generate_key_pair.js @@ -0,0 +1,4 @@ +import { generateKeyPair as generate } from '../runtime/generate.js'; +export async function generateKeyPair(alg, options) { + return generate(alg, options); +} diff --git a/dist/browser/key/generate_secret.js b/dist/browser/key/generate_secret.js new file mode 100644 index 0000000000..58f308a543 --- /dev/null +++ b/dist/browser/key/generate_secret.js @@ -0,0 +1,4 @@ +import { generateSecret as generate } from '../runtime/generate.js'; +export async function generateSecret(alg, options) { + return generate(alg, options); +} diff --git a/dist/browser/key/import.js b/dist/browser/key/import.js new file mode 100644 index 0000000000..25bb0e1659 --- /dev/null +++ b/dist/browser/key/import.js @@ -0,0 +1,50 @@ +import { decode as decodeBase64URL } from '../runtime/base64url.js'; +import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.js'; +import asKeyObject from '../runtime/jwk_to_key.js'; +import { JOSENotSupported } from '../util/errors.js'; +import isObject from '../lib/is_object.js'; +export async function importSPKI(spki, alg, options) { + if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) { + throw new TypeError('"spki" must be SPKI formatted string'); + } + return fromSPKI(spki, alg, options); +} +export async function importX509(x509, alg, options) { + if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) { + throw new TypeError('"x509" must be X.509 formatted string'); + } + return fromX509(x509, alg, options); +} +export async function importPKCS8(pkcs8, alg, options) { + if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string'); + } + return fromPKCS8(pkcs8, alg, options); +} +export async function importJWK(jwk, alg, octAsKeyObject) { + var _a; + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object'); + } + alg || (alg = jwk.alg); + switch (jwk.kty) { + case 'oct': + if (typeof jwk.k !== 'string' || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value'); + } + octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : (octAsKeyObject = jwk.ext !== true); + if (octAsKeyObject) { + return asKeyObject({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false }); + } + return decodeBase64URL(jwk.k); + case 'RSA': + if (jwk.oth !== undefined) { + throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported'); + } + case 'EC': + case 'OKP': + return asKeyObject({ ...jwk, alg }); + default: + throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value'); + } +} diff --git a/dist/browser/lib/aesgcmkw.js b/dist/browser/lib/aesgcmkw.js new file mode 100644 index 0000000000..de3f4f91df --- /dev/null +++ b/dist/browser/lib/aesgcmkw.js @@ -0,0 +1,14 @@ +import encrypt from '../runtime/encrypt.js'; +import decrypt from '../runtime/decrypt.js'; +import generateIv from './iv.js'; +import { encode as base64url } from '../runtime/base64url.js'; +export async function wrap(alg, key, cek, iv) { + const jweAlgorithm = alg.slice(0, 7); + iv || (iv = generateIv(jweAlgorithm)); + const { ciphertext: encryptedKey, tag } = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0)); + return { encryptedKey, iv: base64url(iv), tag: base64url(tag) }; +} +export async function unwrap(alg, key, encryptedKey, iv, tag) { + const jweAlgorithm = alg.slice(0, 7); + return decrypt(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)); +} diff --git a/dist/browser/lib/buffer_utils.js b/dist/browser/lib/buffer_utils.js new file mode 100644 index 0000000000..5a1a7b334d --- /dev/null +++ b/dist/browser/lib/buffer_utils.js @@ -0,0 +1,51 @@ +import digest from '../runtime/digest.js'; +export const encoder = new TextEncoder(); +export const decoder = new TextDecoder(); +const MAX_INT32 = 2 ** 32; +export function concat(...buffers) { + const size = buffers.reduce((acc, { length }) => acc + length, 0); + const buf = new Uint8Array(size); + let i = 0; + buffers.forEach((buffer) => { + buf.set(buffer, i); + i += buffer.length; + }); + return buf; +} +export function p2s(alg, p2sInput) { + return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput); +} +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 & 0xff], offset); +} +export 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; +} +export function uint32be(value) { + const buf = new Uint8Array(4); + writeUInt32BE(buf, value); + return buf; +} +export function lengthAndInput(input) { + return concat(uint32be(input.length), input); +} +export async function concatKdf(secret, bits, value) { + const iterations = Math.ceil((bits >> 3) / 32); + const res = new Uint8Array(iterations * 32); + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length); + buf.set(uint32be(iter + 1)); + buf.set(secret, 4); + buf.set(value, 4 + secret.length); + res.set(await digest('sha256', buf), iter * 32); + } + return res.slice(0, bits >> 3); +} diff --git a/dist/browser/lib/cek.js b/dist/browser/lib/cek.js new file mode 100644 index 0000000000..34697d3ac2 --- /dev/null +++ b/dist/browser/lib/cek.js @@ -0,0 +1,20 @@ +import { JOSENotSupported } from '../util/errors.js'; +import random from '../runtime/random.js'; +export function bitLength(alg) { + switch (alg) { + case 'A128GCM': + return 128; + case 'A192GCM': + return 192; + case 'A256GCM': + case 'A128CBC-HS256': + return 256; + case 'A192CBC-HS384': + return 384; + case 'A256CBC-HS512': + return 512; + default: + throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } +} +export default (alg) => random(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/browser/lib/check_iv_length.js b/dist/browser/lib/check_iv_length.js new file mode 100644 index 0000000000..dcd28d3a9d --- /dev/null +++ b/dist/browser/lib/check_iv_length.js @@ -0,0 +1,8 @@ +import { JWEInvalid } from '../util/errors.js'; +import { bitLength } from './iv.js'; +const checkIvLength = (enc, iv) => { + if (iv.length << 3 !== bitLength(enc)) { + throw new JWEInvalid('Invalid Initialization Vector length'); + } +}; +export default checkIvLength; diff --git a/dist/browser/lib/check_key_type.js b/dist/browser/lib/check_key_type.js new file mode 100644 index 0000000000..43f3dcbf14 --- /dev/null +++ b/dist/browser/lib/check_key_type.js @@ -0,0 +1,45 @@ +import { withAlg as invalidKeyInput } from './invalid_key_input.js'; +import isKeyLike, { types } from '../runtime/is_key_like.js'; +const symmetricTypeCheck = (alg, key) => { + if (key instanceof Uint8Array) + return; + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array')); + } + if (key.type !== 'secret') { + throw new TypeError(`${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`); + } +}; +const asymmetricTypeCheck = (alg, key, usage) => { + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types)); + } + if (key.type === 'secret') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`); + } + if (usage === 'sign' && key.type === 'public') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm signing must be of type "private"`); + } + if (usage === 'decrypt' && key.type === 'public') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm decryption must be of type "private"`); + } + if (key.algorithm && usage === 'verify' && key.type === 'private') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm verifying must be of type "public"`); + } + if (key.algorithm && usage === 'encrypt' && key.type === 'private') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm encryption must be of type "public"`); + } +}; +const checkKeyType = (alg, key, usage) => { + const symmetric = alg.startsWith('HS') || + alg === 'dir' || + alg.startsWith('PBES2') || + /^A\d{3}(?:GCM)?KW$/.test(alg); + if (symmetric) { + symmetricTypeCheck(alg, key); + } + else { + asymmetricTypeCheck(alg, key, usage); + } +}; +export default checkKeyType; diff --git a/dist/browser/lib/check_p2s.js b/dist/browser/lib/check_p2s.js new file mode 100644 index 0000000000..a65289fa7a --- /dev/null +++ b/dist/browser/lib/check_p2s.js @@ -0,0 +1,6 @@ +import { JWEInvalid } from '../util/errors.js'; +export default function checkP2s(p2s) { + if (!(p2s instanceof Uint8Array) || p2s.length < 8) { + throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets'); + } +} diff --git a/dist/browser/lib/crypto_key.js b/dist/browser/lib/crypto_key.js new file mode 100644 index 0000000000..4405b1915c --- /dev/null +++ b/dist/browser/lib/crypto_key.js @@ -0,0 +1,158 @@ +import { isCloudflareWorkers } from '../runtime/env.js'; +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 getNamedCurve(alg) { + switch (alg) { + case 'ES256': + return 'P-256'; + case 'ES384': + return 'P-384'; + case 'ES512': + return 'P-521'; + default: + throw new Error('unreachable'); + } +} +function checkUsage(key, usages) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = 'CryptoKey does not support this operation, its usages must include '; + if (usages.length > 2) { + const last = usages.pop(); + msg += `one of ${usages.join(', ')}, or ${last}.`; + } + else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.`; + } + else { + msg += `${usages[0]}.`; + } + throw new TypeError(msg); + } +} +export function checkSigCryptoKey(key, alg, ...usages) { + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': { + if (!isAlgorithm(key.algorithm, 'HMAC')) + throw unusable('HMAC'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'RS256': + case 'RS384': + case 'RS512': { + if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5')) + throw unusable('RSASSA-PKCS1-v1_5'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'PS256': + case 'PS384': + case 'PS512': { + if (!isAlgorithm(key.algorithm, 'RSA-PSS')) + throw unusable('RSA-PSS'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'EdDSA': { + if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') { + if (isCloudflareWorkers()) { + if (isAlgorithm(key.algorithm, 'NODE-ED25519')) + break; + throw unusable('Ed25519, Ed448, or NODE-ED25519'); + } + throw unusable('Ed25519 or Ed448'); + } + break; + } + case 'ES256': + case 'ES384': + case 'ES512': { + if (!isAlgorithm(key.algorithm, 'ECDSA')) + throw unusable('ECDSA'); + const expected = getNamedCurve(alg); + const actual = key.algorithm.namedCurve; + if (actual !== expected) + throw unusable(expected, 'algorithm.namedCurve'); + break; + } + default: + throw new TypeError('CryptoKey does not support this operation'); + } + checkUsage(key, usages); +} +export function checkEncCryptoKey(key, alg, ...usages) { + 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': + case 'X448': + break; + default: + throw unusable('ECDH, X25519, or X448'); + } + 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, usages); +} diff --git a/dist/browser/lib/decrypt_key_management.js b/dist/browser/lib/decrypt_key_management.js new file mode 100644 index 0000000000..87890a4fa0 --- /dev/null +++ b/dist/browser/lib/decrypt_key_management.js @@ -0,0 +1,98 @@ +import { unwrap as aesKw } from '../runtime/aeskw.js'; +import * as ECDH from '../runtime/ecdhes.js'; +import { decrypt as pbes2Kw } from '../runtime/pbes2kw.js'; +import { decrypt as rsaEs } from '../runtime/rsaes.js'; +import { decode as base64url } from '../runtime/base64url.js'; +import { JOSENotSupported, JWEInvalid } from '../util/errors.js'; +import { bitLength as cekLength } from '../lib/cek.js'; +import { importJWK } from '../key/import.js'; +import checkKeyType from './check_key_type.js'; +import isObject from './is_object.js'; +import { unwrap as aesGcmKw } from './aesgcmkw.js'; +async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) { + checkKeyType(alg, key, 'decrypt'); + switch (alg) { + case 'dir': { + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key'); + return key; + } + case 'ECDH-ES': + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key'); + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!isObject(joseHeader.epk)) + throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`); + if (!ECDH.ecdhAllowed(key)) + throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + const epk = await importJWK(joseHeader.epk, alg); + let partyUInfo; + let partyVInfo; + if (joseHeader.apu !== undefined) { + if (typeof joseHeader.apu !== 'string') + throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`); + partyUInfo = base64url(joseHeader.apu); + } + if (joseHeader.apv !== undefined) { + if (typeof joseHeader.apv !== 'string') + throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`); + partyVInfo = base64url(joseHeader.apv); + } + const sharedSecret = await ECDH.deriveKey(epk, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, alg === 'ECDH-ES' ? cekLength(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo); + if (alg === 'ECDH-ES') + return sharedSecret; + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return aesKw(alg.slice(-6), sharedSecret, encryptedKey); + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return rsaEs(alg, key, encryptedKey); + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.p2c !== 'number') + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`); + const p2cLimit = (options === null || options === void 0 ? void 0 : options.maxPBES2Count) || 10000; + if (joseHeader.p2c > p2cLimit) + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`); + if (typeof joseHeader.p2s !== 'string') + throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`); + return pbes2Kw(alg, key, encryptedKey, joseHeader.p2c, base64url(joseHeader.p2s)); + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return aesKw(alg, key, encryptedKey); + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.iv !== 'string') + throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`); + if (typeof joseHeader.tag !== 'string') + throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`); + const iv = base64url(joseHeader.iv); + const tag = base64url(joseHeader.tag); + return aesGcmKw(alg, key, encryptedKey, iv, tag); + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } +} +export default decryptKeyManagement; diff --git a/dist/browser/lib/encrypt_key_management.js b/dist/browser/lib/encrypt_key_management.js new file mode 100644 index 0000000000..eb9022c63e --- /dev/null +++ b/dist/browser/lib/encrypt_key_management.js @@ -0,0 +1,87 @@ +import { wrap as aesKw } from '../runtime/aeskw.js'; +import * as ECDH from '../runtime/ecdhes.js'; +import { encrypt as pbes2Kw } from '../runtime/pbes2kw.js'; +import { encrypt as rsaEs } from '../runtime/rsaes.js'; +import { encode as base64url } from '../runtime/base64url.js'; +import generateCek, { bitLength as cekLength } from '../lib/cek.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { exportJWK } from '../key/export.js'; +import checkKeyType from './check_key_type.js'; +import { wrap as aesGcmKw } from './aesgcmkw.js'; +async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) { + let encryptedKey; + let parameters; + let cek; + checkKeyType(alg, key, 'encrypt'); + switch (alg) { + case 'dir': { + cek = key; + break; + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!ECDH.ecdhAllowed(key)) { + throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + } + const { apu, apv } = providedParameters; + let { epk: ephemeralKey } = providedParameters; + ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey); + const { x, y, crv, kty } = await exportJWK(ephemeralKey); + const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv); + parameters = { epk: { x, crv, kty } }; + if (kty === 'EC') + parameters.epk.y = y; + if (apu) + parameters.apu = base64url(apu); + if (apv) + parameters.apv = base64url(apv); + if (alg === 'ECDH-ES') { + cek = sharedSecret; + break; + } + cek = providedCek || generateCek(enc); + const kwAlg = alg.slice(-6); + encryptedKey = await aesKw(kwAlg, sharedSecret, cek); + break; + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + cek = providedCek || generateCek(enc); + encryptedKey = await rsaEs(alg, key, cek); + break; + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + cek = providedCek || generateCek(enc); + const { p2c, p2s } = providedParameters; + ({ encryptedKey, ...parameters } = await pbes2Kw(alg, key, cek, p2c, p2s)); + break; + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + cek = providedCek || generateCek(enc); + encryptedKey = await aesKw(alg, key, cek); + break; + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + cek = providedCek || generateCek(enc); + const { iv } = providedParameters; + ({ encryptedKey, ...parameters } = await aesGcmKw(alg, key, cek, iv)); + break; + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + return { cek, encryptedKey, parameters }; +} +export default encryptKeyManagement; diff --git a/dist/browser/lib/epoch.js b/dist/browser/lib/epoch.js new file mode 100644 index 0000000000..e405e4b2df --- /dev/null +++ b/dist/browser/lib/epoch.js @@ -0,0 +1 @@ +export default (date) => Math.floor(date.getTime() / 1000); diff --git a/dist/browser/lib/format_pem.js b/dist/browser/lib/format_pem.js new file mode 100644 index 0000000000..81673f256f --- /dev/null +++ b/dist/browser/lib/format_pem.js @@ -0,0 +1,4 @@ +export default (b64, descriptor) => { + const newlined = (b64.match(/.{1,64}/g) || []).join('\n'); + return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`; +}; diff --git a/dist/browser/lib/invalid_key_input.js b/dist/browser/lib/invalid_key_input.js new file mode 100644 index 0000000000..049e66ece0 --- /dev/null +++ b/dist/browser/lib/invalid_key_input.js @@ -0,0 +1,30 @@ +function message(msg, actual, ...types) { + 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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}`; + } + } + return msg; +} +export default (actual, ...types) => { + return message('Key must be ', actual, ...types); +}; +export function withAlg(alg, actual, ...types) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types); +} diff --git a/dist/browser/lib/is_disjoint.js b/dist/browser/lib/is_disjoint.js new file mode 100644 index 0000000000..6f643502dc --- /dev/null +++ b/dist/browser/lib/is_disjoint.js @@ -0,0 +1,22 @@ +const isDisjoint = (...headers) => { + const sources = headers.filter(Boolean); + if (sources.length === 0 || sources.length === 1) { + return true; + } + let acc; + for (const header of sources) { + const parameters = Object.keys(header); + if (!acc || acc.size === 0) { + acc = new Set(parameters); + continue; + } + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false; + } + acc.add(parameter); + } + } + return true; +}; +export default isDisjoint; diff --git a/dist/browser/lib/is_object.js b/dist/browser/lib/is_object.js new file mode 100644 index 0000000000..4955e93225 --- /dev/null +++ b/dist/browser/lib/is_object.js @@ -0,0 +1,16 @@ +function isObjectLike(value) { + return typeof value === 'object' && value !== null; +} +export default function isObject(input) { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') { + return false; + } + if (Object.getPrototypeOf(input) === null) { + return true; + } + let proto = input; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(input) === proto; +} diff --git a/dist/browser/lib/iv.js b/dist/browser/lib/iv.js new file mode 100644 index 0000000000..cab2a12729 --- /dev/null +++ b/dist/browser/lib/iv.js @@ -0,0 +1,20 @@ +import { JOSENotSupported } from '../util/errors.js'; +import random from '../runtime/random.js'; +export 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}`); + } +} +export default (alg) => random(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/browser/lib/jwt_claims_set.js b/dist/browser/lib/jwt_claims_set.js new file mode 100644 index 0000000000..e9e2f1944f --- /dev/null +++ b/dist/browser/lib/jwt_claims_set.js @@ -0,0 +1,91 @@ +import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'; +import { decoder } from './buffer_utils.js'; +import epoch from './epoch.js'; +import secs from './secs.js'; +import isObject from './is_object.js'; +const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, ''); +const checkAudiencePresence = (audPayload, audOption) => { + if (typeof audPayload === 'string') { + return audOption.includes(audPayload); + } + if (Array.isArray(audPayload)) { + return audOption.some(Set.prototype.has.bind(new Set(audPayload))); + } + return false; +}; +export default (protectedHeader, encodedPayload, options = {}) => { + const { typ } = options; + if (typ && + (typeof protectedHeader.typ !== 'string' || + normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) { + throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed'); + } + let payload; + try { + payload = JSON.parse(decoder.decode(encodedPayload)); + } + catch (_a) { + } + if (!isObject(payload)) { + throw new JWTInvalid('JWT Claims Set must be a top-level JSON object'); + } + const { issuer } = options; + if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) { + throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed'); + } + const { subject } = options; + if (subject && payload.sub !== subject) { + throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed'); + } + const { audience } = options; + if (audience && + !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) { + throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed'); + } + let tolerance; + switch (typeof options.clockTolerance) { + case 'string': + tolerance = secs(options.clockTolerance); + break; + case 'number': + tolerance = options.clockTolerance; + break; + case 'undefined': + tolerance = 0; + break; + default: + throw new TypeError('Invalid clockTolerance option type'); + } + const { currentDate } = options; + const now = epoch(currentDate || new Date()); + if ((payload.iat !== undefined || options.maxTokenAge) && typeof payload.iat !== 'number') { + throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); + } + if (payload.nbf !== undefined) { + if (typeof payload.nbf !== 'number') { + throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); + } + if (payload.nbf > now + tolerance) { + throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed'); + } + } + if (payload.exp !== undefined) { + if (typeof payload.exp !== 'number') { + throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); + } + if (payload.exp <= now - tolerance) { + throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed'); + } + } + if (options.maxTokenAge) { + const age = now - payload.iat; + const max = typeof options.maxTokenAge === 'number' ? options.maxTokenAge : secs(options.maxTokenAge); + if (age - tolerance > max) { + throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed'); + } + if (age < 0 - tolerance) { + throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); + } + } + return payload; +}; diff --git a/dist/browser/lib/secs.js b/dist/browser/lib/secs.js new file mode 100644 index 0000000000..cf470ed8ad --- /dev/null +++ b/dist/browser/lib/secs.js @@ -0,0 +1,44 @@ +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const year = day * 365.25; +const REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i; +export default (str) => { + const matched = REGEX.exec(str); + if (!matched) { + throw new TypeError('Invalid time period format'); + } + const value = parseFloat(matched[1]); + const unit = matched[2].toLowerCase(); + switch (unit) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + case 's': + return Math.round(value); + case 'minute': + case 'minutes': + case 'min': + case 'mins': + case 'm': + return Math.round(value * minute); + case 'hour': + case 'hours': + case 'hr': + case 'hrs': + case 'h': + return Math.round(value * hour); + case 'day': + case 'days': + case 'd': + return Math.round(value * day); + case 'week': + case 'weeks': + case 'w': + return Math.round(value * week); + default: + return Math.round(value * year); + } +}; diff --git a/dist/browser/lib/validate_algorithms.js b/dist/browser/lib/validate_algorithms.js new file mode 100644 index 0000000000..a6a7918571 --- /dev/null +++ b/dist/browser/lib/validate_algorithms.js @@ -0,0 +1,11 @@ +const validateAlgorithms = (option, algorithms) => { + if (algorithms !== undefined && + (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) { + throw new TypeError(`"${option}" option must be an array of strings`); + } + if (!algorithms) { + return undefined; + } + return new Set(algorithms); +}; +export default validateAlgorithms; diff --git a/dist/browser/lib/validate_crit.js b/dist/browser/lib/validate_crit.js new file mode 100644 index 0000000000..68c69f18f5 --- /dev/null +++ b/dist/browser/lib/validate_crit.js @@ -0,0 +1,34 @@ +import { JOSENotSupported } from '../util/errors.js'; +function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { + if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); + } + if (!protectedHeader || protectedHeader.crit === undefined) { + return new Set(); + } + if (!Array.isArray(protectedHeader.crit) || + protectedHeader.crit.length === 0 || + protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) { + throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); + } + let recognized; + if (recognizedOption !== undefined) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); + } + else { + recognized = recognizedDefault; + } + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); + } + if (joseHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`); + } + else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); + } + } + return new Set(protectedHeader.crit); +} +export default validateCrit; diff --git a/dist/browser/package.json b/dist/browser/package.json new file mode 100644 index 0000000000..6990891ff3 --- /dev/null +++ b/dist/browser/package.json @@ -0,0 +1 @@ +{"type": "module"} diff --git a/dist/browser/runtime/aeskw.js b/dist/browser/runtime/aeskw.js new file mode 100644 index 0000000000..bb7dff8a5b --- /dev/null +++ b/dist/browser/runtime/aeskw.js @@ -0,0 +1,32 @@ +import bogusWebCrypto from './bogus.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +function checkKeySize(key, alg) { + if (key.algorithm.length !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`); + } +} +function getCryptoKey(key, alg, usage) { + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + return crypto.subtle.importKey('raw', key, 'AES-KW', true, [usage]); + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} +export const wrap = async (alg, key, cek) => { + const cryptoKey = await getCryptoKey(key, alg, 'wrapKey'); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, ...bogusWebCrypto); + return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW')); +}; +export const unwrap = async (alg, key, encryptedKey) => { + const cryptoKey = await getCryptoKey(key, alg, 'unwrapKey'); + checkKeySize(cryptoKey, alg); + const cryptoKeyCek = await crypto.subtle.unwrapKey('raw', encryptedKey, cryptoKey, 'AES-KW', ...bogusWebCrypto); + return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek)); +}; diff --git a/dist/browser/runtime/asn1.js b/dist/browser/runtime/asn1.js new file mode 100644 index 0000000000..ba617f649a --- /dev/null +++ b/dist/browser/runtime/asn1.js @@ -0,0 +1,214 @@ +import { isCloudflareWorkers } from './env.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { encodeBase64, decodeBase64 } from './base64url.js'; +import formatPEM from '../lib/format_pem.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { types } from './is_key_like.js'; +const genericExport = async (keyType, keyFormat, key) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable'); + } + if (key.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`); + } + return formatPEM(encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`); +}; +export const toSPKI = (key) => { + return genericExport('public', 'spki', key); +}; +export const toPKCS8 = (key) => { + return genericExport('private', 'pkcs8', key); +}; +const findOid = (keyData, oid, from = 0) => { + if (from === 0) { + oid.unshift(oid.length); + oid.unshift(0x06); + } + let i = keyData.indexOf(oid[0], from); + if (i === -1) + return false; + const sub = keyData.subarray(i, i + oid.length); + if (sub.length !== oid.length) + return false; + return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1); +}; +const getNamedCurve = (keyData) => { + switch (true) { + case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]): + return 'P-256'; + case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x22]): + return 'P-384'; + case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]): + return 'P-521'; + case findOid(keyData, [0x2b, 0x65, 0x6e]): + return 'X25519'; + case findOid(keyData, [0x2b, 0x65, 0x6f]): + return 'X448'; + case findOid(keyData, [0x2b, 0x65, 0x70]): + return 'Ed25519'; + case findOid(keyData, [0x2b, 0x65, 0x71]): + return 'Ed448'; + default: + throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type'); + } +}; +const genericImport = async (replace, keyFormat, pem, alg, options) => { + var _a, _b; + let algorithm; + let keyUsages; + const keyData = new Uint8Array(atob(pem.replace(replace, '')) + .split('') + .map((c) => c.charCodeAt(0))); + const isPublic = keyFormat === 'spki'; + switch (alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + }; + keyUsages = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey']; + break; + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + const namedCurve = getNamedCurve(keyData); + algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve }; + keyUsages = isPublic ? [] : ['deriveBits']; + break; + } + case 'EdDSA': + algorithm = { name: getNamedCurve(keyData) }; + keyUsages = isPublic ? ['verify'] : ['sign']; + break; + default: + throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value'); + } + try { + return await crypto.subtle.importKey(keyFormat, keyData, algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); + } + catch (err) { + if (algorithm.name === 'Ed25519' && + (err === null || err === void 0 ? void 0 : err.name) === 'NotSupportedError' && + isCloudflareWorkers()) { + algorithm = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' }; + return await crypto.subtle.importKey(keyFormat, keyData, algorithm, (_b = options === null || options === void 0 ? void 0 : options.extractable) !== null && _b !== void 0 ? _b : false, keyUsages); + } + throw err; + } +}; +export const fromPKCS8 = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options); +}; +export const fromSPKI = (pem, alg, options) => { + return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options); +}; +function getElement(seq) { + let result = []; + let next = 0; + while (next < seq.length) { + let nextPart = parseElement(seq.subarray(next)); + result.push(nextPart); + next += nextPart.byteLength; + } + return result; +} +function parseElement(bytes) { + let position = 0; + let tag = bytes[0] & 0x1f; + position++; + if (tag === 0x1f) { + tag = 0; + while (bytes[position] >= 0x80) { + tag = tag * 128 + bytes[position] - 0x80; + position++; + } + tag = tag * 128 + bytes[position] - 0x80; + position++; + } + let length = 0; + if (bytes[position] < 0x80) { + length = bytes[position]; + position++; + } + else if (length === 0x80) { + length = 0; + while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) { + if (length > bytes.byteLength) { + throw new TypeError('invalid indefinite form length'); + } + length++; + } + const byteLength = position + length + 2; + return { + byteLength, + contents: bytes.subarray(position, position + length), + raw: bytes.subarray(0, byteLength), + }; + } + else { + let numberOfDigits = bytes[position] & 0x7f; + position++; + length = 0; + for (let i = 0; i < numberOfDigits; i++) { + length = length * 256 + bytes[position]; + position++; + } + } + const byteLength = position + length; + return { + byteLength, + contents: bytes.subarray(position, byteLength), + raw: bytes.subarray(0, byteLength), + }; +} +function spkiFromX509(buf) { + const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents); + return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw); +} +function getSPKI(x509) { + const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ''); + const raw = decodeBase64(pem); + return formatPEM(spkiFromX509(raw), 'PUBLIC KEY'); +} +export const fromX509 = (pem, alg, options) => { + let spki; + try { + spki = getSPKI(pem); + } + catch (cause) { + throw new TypeError('failed to parse the X.509 certificate', { cause }); + } + return fromSPKI(spki, alg, options); +}; diff --git a/dist/browser/runtime/base64url.js b/dist/browser/runtime/base64url.js new file mode 100644 index 0000000000..32df82bfe0 --- /dev/null +++ b/dist/browser/runtime/base64url.js @@ -0,0 +1,37 @@ +import { encoder, decoder } from '../lib/buffer_utils.js'; +export const encodeBase64 = (input) => { + let unencoded = input; + if (typeof unencoded === 'string') { + unencoded = encoder.encode(unencoded); + } + const CHUNK_SIZE = 0x8000; + const arr = []; + for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) { + arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))); + } + return btoa(arr.join('')); +}; +export const encode = (input) => { + return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); +}; +export const decodeBase64 = (encoded) => { + const binary = atob(encoded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +}; +export const decode = (input) => { + let encoded = input; + if (encoded instanceof Uint8Array) { + encoded = decoder.decode(encoded); + } + encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''); + try { + return decodeBase64(encoded); + } + catch (_a) { + throw new TypeError('The input to be decoded is not correctly encoded.'); + } +}; diff --git a/dist/browser/runtime/bogus.js b/dist/browser/runtime/bogus.js new file mode 100644 index 0000000000..8fde604ebf --- /dev/null +++ b/dist/browser/runtime/bogus.js @@ -0,0 +1,6 @@ +const bogusWebCrypto = [ + { hash: 'SHA-256', name: 'HMAC' }, + true, + ['sign'], +]; +export default bogusWebCrypto; diff --git a/dist/browser/runtime/check_cek_length.js b/dist/browser/runtime/check_cek_length.js new file mode 100644 index 0000000000..ae89001215 --- /dev/null +++ b/dist/browser/runtime/check_cek_length.js @@ -0,0 +1,8 @@ +import { JWEInvalid } from '../util/errors.js'; +const checkCekLength = (cek, expected) => { + const actual = cek.byteLength << 3; + if (actual !== expected) { + throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } +}; +export default checkCekLength; diff --git a/dist/browser/runtime/check_key_length.js b/dist/browser/runtime/check_key_length.js new file mode 100644 index 0000000000..33970068fe --- /dev/null +++ b/dist/browser/runtime/check_key_length.js @@ -0,0 +1,8 @@ +export default (alg, key) => { + if (alg.startsWith('RS') || alg.startsWith('PS')) { + const { modulusLength } = key.algorithm; + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); + } + } +}; diff --git a/dist/browser/runtime/decrypt.js b/dist/browser/runtime/decrypt.js new file mode 100644 index 0000000000..68f9f7c3fe --- /dev/null +++ b/dist/browser/runtime/decrypt.js @@ -0,0 +1,85 @@ +import { concat, uint64be } from '../lib/buffer_utils.js'; +import checkIvLength from '../lib/check_iv_length.js'; +import checkCekLength from './check_cek_length.js'; +import timingSafeEqual from './timing_safe_equal.js'; +import { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, 'Uint8Array')); + } + const keySize = parseInt(enc.slice(1, 4), 10); + const encKey = await crypto.subtle.importKey('raw', cek.subarray(keySize >> 3), 'AES-CBC', false, ['decrypt']); + const macKey = await crypto.subtle.importKey('raw', cek.subarray(0, keySize >> 3), { + hash: `SHA-${keySize << 1}`, + name: 'HMAC', + }, false, ['sign']); + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const expectedTag = new Uint8Array((await crypto.subtle.sign('HMAC', macKey, macData)).slice(0, keySize >> 3)); + let macCheckPassed; + try { + macCheckPassed = timingSafeEqual(tag, expectedTag); + } + catch (_a) { + } + if (!macCheckPassed) { + throw new JWEDecryptionFailed(); + } + let plaintext; + try { + plaintext = new Uint8Array(await crypto.subtle.decrypt({ iv, name: 'AES-CBC' }, encKey, ciphertext)); + } + catch (_b) { + } + if (!plaintext) { + throw new JWEDecryptionFailed(); + } + return plaintext; +} +async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) { + let encKey; + if (cek instanceof Uint8Array) { + encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']); + } + else { + checkEncCryptoKey(cek, enc, 'decrypt'); + encKey = cek; + } + try { + return new Uint8Array(await crypto.subtle.decrypt({ + additionalData: aad, + iv, + name: 'AES-GCM', + tagLength: 128, + }, encKey, concat(ciphertext, tag))); + } + catch (_a) { + throw new JWEDecryptionFailed(); + } +} +const decrypt = async (enc, cek, ciphertext, iv, tag, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')); + } + checkIvLength(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + if (cek instanceof Uint8Array) + checkCekLength(cek, parseInt(enc.slice(-3), 10)); + return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + if (cek instanceof Uint8Array) + checkCekLength(cek, parseInt(enc.slice(1, 4), 10)); + return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad); + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +export default decrypt; diff --git a/dist/browser/runtime/digest.js b/dist/browser/runtime/digest.js new file mode 100644 index 0000000000..39099d3b1d --- /dev/null +++ b/dist/browser/runtime/digest.js @@ -0,0 +1,6 @@ +import crypto from './webcrypto.js'; +const digest = async (algorithm, data) => { + const subtleDigest = `SHA-${algorithm.slice(-3)}`; + return new Uint8Array(await crypto.subtle.digest(subtleDigest, data)); +}; +export default digest; diff --git a/dist/browser/runtime/ecdhes.js b/dist/browser/runtime/ecdhes.js new file mode 100644 index 0000000000..b4f515e312 --- /dev/null +++ b/dist/browser/runtime/ecdhes.js @@ -0,0 +1,46 @@ +import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { + if (!isCryptoKey(publicKey)) { + throw new TypeError(invalidKeyInput(publicKey, ...types)); + } + checkEncCryptoKey(publicKey, 'ECDH'); + if (!isCryptoKey(privateKey)) { + throw new TypeError(invalidKeyInput(privateKey, ...types)); + } + checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits'); + const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); + let length; + if (publicKey.algorithm.name === 'X25519') { + length = 256; + } + else if (publicKey.algorithm.name === 'X448') { + length = 448; + } + else { + length = + Math.ceil(parseInt(publicKey.algorithm.namedCurve.substr(-3), 10) / 8) << 3; + } + const sharedSecret = new Uint8Array(await crypto.subtle.deriveBits({ + name: publicKey.algorithm.name, + public: publicKey, + }, privateKey, length)); + return concatKdf(sharedSecret, keyLength, value); +} +export async function generateEpk(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + return crypto.subtle.generateKey(key.algorithm, true, ['deriveBits']); +} +export function ecdhAllowed(key) { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + return (['P-256', 'P-384', 'P-521'].includes(key.algorithm.namedCurve) || + key.algorithm.name === 'X25519' || + key.algorithm.name === 'X448'); +} diff --git a/dist/browser/runtime/encrypt.js b/dist/browser/runtime/encrypt.js new file mode 100644 index 0000000000..c2ec0ef91d --- /dev/null +++ b/dist/browser/runtime/encrypt.js @@ -0,0 +1,68 @@ +import { concat, uint64be } from '../lib/buffer_utils.js'; +import checkIvLength from '../lib/check_iv_length.js'; +import checkCekLength from './check_cek_length.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { types } from './is_key_like.js'; +async function cbcEncrypt(enc, plaintext, cek, iv, aad) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(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 }; +} +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 }; +} +const encrypt = async (enc, plaintext, cek, iv, aad) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')); + } + checkIvLength(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + if (cek instanceof Uint8Array) + checkCekLength(cek, parseInt(enc.slice(-3), 10)); + return cbcEncrypt(enc, plaintext, cek, iv, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + if (cek instanceof Uint8Array) + checkCekLength(cek, parseInt(enc.slice(1, 4), 10)); + return gcmEncrypt(enc, plaintext, cek, iv, aad); + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +export default encrypt; diff --git a/dist/browser/runtime/env.js b/dist/browser/runtime/env.js new file mode 100644 index 0000000000..740a197ec0 --- /dev/null +++ b/dist/browser/runtime/env.js @@ -0,0 +1,5 @@ +export function isCloudflareWorkers() { + return (typeof WebSocketPair !== 'undefined' || + (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') || + (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel')); +} diff --git a/dist/browser/runtime/fetch_jwks.js b/dist/browser/runtime/fetch_jwks.js new file mode 100644 index 0000000000..85d58b675d --- /dev/null +++ b/dist/browser/runtime/fetch_jwks.js @@ -0,0 +1,34 @@ +import { JOSEError, JWKSTimeout } from '../util/errors.js'; +const fetchJwks = async (url, timeout, options) => { + let controller; + let id; + let timedOut = false; + if (typeof AbortController === 'function') { + controller = new AbortController(); + id = setTimeout(() => { + timedOut = true; + controller.abort(); + }, timeout); + } + const response = await fetch(url.href, { + signal: controller ? controller.signal : undefined, + redirect: 'manual', + headers: options.headers, + }).catch((err) => { + if (timedOut) + throw new JWKSTimeout(); + throw err; + }); + if (id !== undefined) + clearTimeout(id); + if (response.status !== 200) { + throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response'); + } + try { + return await response.json(); + } + catch (_a) { + throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON'); + } +}; +export default fetchJwks; diff --git a/dist/browser/runtime/generate.js b/dist/browser/runtime/generate.js new file mode 100644 index 0000000000..8af84fa141 --- /dev/null +++ b/dist/browser/runtime/generate.js @@ -0,0 +1,153 @@ +import { isCloudflareWorkers } from './env.js'; +import crypto from './webcrypto.js'; +import { JOSENotSupported } from '../util/errors.js'; +import random from './random.js'; +export async function generateSecret(alg, options) { + var _a; + let length; + let algorithm; + let keyUsages; + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + length = parseInt(alg.slice(-3), 10); + algorithm = { name: 'HMAC', hash: `SHA-${length}`, length }; + keyUsages = ['sign', 'verify']; + break; + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + length = parseInt(alg.slice(-3), 10); + return random(new Uint8Array(length >> 3)); + case 'A128KW': + case 'A192KW': + case 'A256KW': + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: 'AES-KW', length }; + keyUsages = ['wrapKey', 'unwrapKey']; + break; + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + length = parseInt(alg.slice(1, 4), 10); + algorithm = { name: 'AES-GCM', length }; + keyUsages = ['encrypt', 'decrypt']; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + return crypto.subtle.generateKey(algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages); +} +function getModulusLengthOption(options) { + var _a; + const modulusLength = (_a = options === null || options === void 0 ? void 0 : options.modulusLength) !== null && _a !== void 0 ? _a : 2048; + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new JOSENotSupported('Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used'); + } + return modulusLength; +} +export async function generateKeyPair(alg, options) { + var _a, _b, _c, _d; + let algorithm; + let keyUsages; + switch (alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { + name: 'RSA-PSS', + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + }; + keyUsages = ['sign', 'verify']; + break; + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { + name: 'RSASSA-PKCS1-v1_5', + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + }; + keyUsages = ['sign', 'verify']; + break; + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + }; + keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey']; + break; + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' }; + keyUsages = ['sign', 'verify']; + break; + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' }; + keyUsages = ['sign', 'verify']; + break; + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' }; + keyUsages = ['sign', 'verify']; + break; + case 'EdDSA': + keyUsages = ['sign', 'verify']; + const crv = (_a = options === null || options === void 0 ? void 0 : options.crv) !== null && _a !== void 0 ? _a : 'Ed25519'; + switch (crv) { + case 'Ed25519': + case 'Ed448': + algorithm = { name: crv }; + break; + default: + throw new JOSENotSupported('Invalid or unsupported crv option provided'); + } + break; + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + keyUsages = ['deriveKey', 'deriveBits']; + const crv = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : 'P-256'; + switch (crv) { + case 'P-256': + case 'P-384': + case 'P-521': { + algorithm = { name: 'ECDH', namedCurve: crv }; + break; + } + case 'X25519': + case 'X448': + algorithm = { name: crv }; + break; + default: + throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448'); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + try { + return (await crypto.subtle.generateKey(algorithm, (_c = options === null || options === void 0 ? void 0 : options.extractable) !== null && _c !== void 0 ? _c : false, keyUsages)); + } + catch (err) { + if (algorithm.name === 'Ed25519' && + (err === null || err === void 0 ? void 0 : err.name) === 'NotSupportedError' && + isCloudflareWorkers()) { + algorithm = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' }; + return (await crypto.subtle.generateKey(algorithm, (_d = options === null || options === void 0 ? void 0 : options.extractable) !== null && _d !== void 0 ? _d : false, keyUsages)); + } + throw err; + } +} diff --git a/dist/browser/runtime/get_sign_verify_key.js b/dist/browser/runtime/get_sign_verify_key.js new file mode 100644 index 0000000000..db34ae8bbe --- /dev/null +++ b/dist/browser/runtime/get_sign_verify_key.js @@ -0,0 +1,17 @@ +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkSigCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +export default function getCryptoKey(alg, key, usage) { + if (isCryptoKey(key)) { + checkSigCryptoKey(key, alg, usage); + return key; + } + if (key instanceof Uint8Array) { + if (!alg.startsWith('HS')) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + return crypto.subtle.importKey('raw', key, { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, false, [usage]); + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} diff --git a/dist/browser/runtime/is_key_like.js b/dist/browser/runtime/is_key_like.js new file mode 100644 index 0000000000..8d35fdc84c --- /dev/null +++ b/dist/browser/runtime/is_key_like.js @@ -0,0 +1,5 @@ +import { isCryptoKey } from './webcrypto.js'; +export default (key) => { + return isCryptoKey(key); +}; +export const types = ['CryptoKey']; diff --git a/dist/browser/runtime/jwk_to_key.js b/dist/browser/runtime/jwk_to_key.js new file mode 100644 index 0000000000..d3e693d2de --- /dev/null +++ b/dist/browser/runtime/jwk_to_key.js @@ -0,0 +1,155 @@ +import { isCloudflareWorkers } from './env.js'; +import crypto from './webcrypto.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { decode as base64url } from './base64url.js'; +function subtleMapping(jwk) { + let algorithm; + let keyUsages; + switch (jwk.kty) { + case 'oct': { + switch (jwk.alg) { + case 'HS256': + case 'HS384': + case 'HS512': + algorithm = { name: 'HMAC', hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = ['sign', 'verify']; + break; + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + throw new JOSENotSupported(`${jwk.alg} keys cannot be imported as CryptoKey instances`); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + algorithm = { name: 'AES-GCM' }; + keyUsages = ['encrypt', 'decrypt']; + break; + case 'A128KW': + case 'A192KW': + case 'A256KW': + algorithm = { name: 'AES-KW' }; + keyUsages = ['wrapKey', 'unwrapKey']; + break; + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': + algorithm = { name: 'PBKDF2' }; + keyUsages = ['deriveBits']; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case 'RSA': { + switch (jwk.alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`, + }; + keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey']; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case 'EC': { + switch (jwk.alg) { + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + algorithm = { name: 'ECDH', namedCurve: jwk.crv }; + keyUsages = jwk.d ? ['deriveBits'] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + case 'OKP': { + switch (jwk.alg) { + case 'EdDSA': + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ['sign'] : ['verify']; + break; + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + algorithm = { name: jwk.crv }; + keyUsages = jwk.d ? ['deriveBits'] : []; + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + break; + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); + } + return { algorithm, keyUsages }; +} +const parse = async (jwk) => { + var _a, _b; + if (!jwk.alg) { + throw new TypeError('"alg" argument is required when "jwk.alg" is not present'); + } + const { algorithm, keyUsages } = subtleMapping(jwk); + const rest = [ + algorithm, + (_a = jwk.ext) !== null && _a !== void 0 ? _a : false, + (_b = jwk.key_ops) !== null && _b !== void 0 ? _b : keyUsages, + ]; + if (algorithm.name === 'PBKDF2') { + return crypto.subtle.importKey('raw', base64url(jwk.k), ...rest); + } + const keyData = { ...jwk }; + delete keyData.alg; + delete keyData.use; + try { + return await crypto.subtle.importKey('jwk', keyData, ...rest); + } + catch (err) { + if (algorithm.name === 'Ed25519' && + (err === null || err === void 0 ? void 0 : err.name) === 'NotSupportedError' && + isCloudflareWorkers()) { + rest[0] = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' }; + return await crypto.subtle.importKey('jwk', keyData, ...rest); + } + throw err; + } +}; +export default parse; diff --git a/dist/browser/runtime/key_to_jwk.js b/dist/browser/runtime/key_to_jwk.js new file mode 100644 index 0000000000..438edfedc3 --- /dev/null +++ b/dist/browser/runtime/key_to_jwk.js @@ -0,0 +1,21 @@ +import crypto, { isCryptoKey } from './webcrypto.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { encode as base64url } from './base64url.js'; +import { types } from './is_key_like.js'; +const keyToJWK = async (key) => { + if (key instanceof Uint8Array) { + return { + kty: 'oct', + k: base64url(key), + }; + } + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); + } + if (!key.extractable) { + throw new TypeError('non-extractable CryptoKey cannot be exported as a JWK'); + } + const { ext, key_ops, alg, use, ...jwk } = await crypto.subtle.exportKey('jwk', key); + return jwk; +}; +export default keyToJWK; diff --git a/dist/browser/runtime/pbes2kw.js b/dist/browser/runtime/pbes2kw.js new file mode 100644 index 0000000000..575304aa6b --- /dev/null +++ b/dist/browser/runtime/pbes2kw.js @@ -0,0 +1,51 @@ +import random from './random.js'; +import { p2s as concatSalt } from '../lib/buffer_utils.js'; +import { encode as base64url } from './base64url.js'; +import { wrap, unwrap } from './aeskw.js'; +import checkP2s from '../lib/check_p2s.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +function getCryptoKey(key, alg) { + if (key instanceof Uint8Array) { + return crypto.subtle.importKey('raw', key, 'PBKDF2', false, ['deriveBits']); + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, 'deriveBits', 'deriveKey'); + return key; + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} +async function deriveKey(p2s, alg, p2c, key) { + checkP2s(p2s); + const salt = concatSalt(alg, p2s); + const keylen = parseInt(alg.slice(13, 16), 10); + const subtleAlg = { + hash: `SHA-${alg.slice(8, 11)}`, + iterations: p2c, + name: 'PBKDF2', + salt, + }; + const wrapAlg = { + length: keylen, + name: 'AES-KW', + }; + const cryptoKey = await getCryptoKey(key, alg); + if (cryptoKey.usages.includes('deriveBits')) { + return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen)); + } + if (cryptoKey.usages.includes('deriveKey')) { + return crypto.subtle.deriveKey(subtleAlg, cryptoKey, wrapAlg, false, ['wrapKey', 'unwrapKey']); + } + throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"'); +} +export const encrypt = async (alg, key, cek, p2c = 2048, p2s = random(new Uint8Array(16))) => { + const derived = await deriveKey(p2s, alg, p2c, key); + const encryptedKey = await wrap(alg.slice(-6), derived, cek); + return { encryptedKey, p2c, p2s: base64url(p2s) }; +}; +export const decrypt = async (alg, key, encryptedKey, p2c, p2s) => { + const derived = await deriveKey(p2s, alg, p2c, key); + return unwrap(alg.slice(-6), derived, encryptedKey); +}; diff --git a/dist/browser/runtime/random.js b/dist/browser/runtime/random.js new file mode 100644 index 0000000000..e8e461124a --- /dev/null +++ b/dist/browser/runtime/random.js @@ -0,0 +1,2 @@ +import crypto from './webcrypto.js'; +export default crypto.getRandomValues.bind(crypto); diff --git a/dist/browser/runtime/rsaes.js b/dist/browser/runtime/rsaes.js new file mode 100644 index 0000000000..a1733769ec --- /dev/null +++ b/dist/browser/runtime/rsaes.js @@ -0,0 +1,37 @@ +import subtleAlgorithm from './subtle_rsaes.js'; +import bogusWebCrypto from './bogus.js'; +import crypto, { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import checkKeyLength from './check_key_length.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +export const encrypt = async (alg, key, cek) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + checkEncCryptoKey(key, alg, 'encrypt', 'wrapKey'); + checkKeyLength(alg, key); + if (key.usages.includes('encrypt')) { + return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek)); + } + if (key.usages.includes('wrapKey')) { + const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, ...bogusWebCrypto); + return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, key, subtleAlgorithm(alg))); + } + throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation'); +}; +export const decrypt = async (alg, key, encryptedKey) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + checkEncCryptoKey(key, alg, 'decrypt', 'unwrapKey'); + checkKeyLength(alg, key); + if (key.usages.includes('decrypt')) { + return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey)); + } + if (key.usages.includes('unwrapKey')) { + const cryptoKeyCek = await crypto.subtle.unwrapKey('raw', encryptedKey, key, subtleAlgorithm(alg), ...bogusWebCrypto); + return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek)); + } + throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation'); +}; diff --git a/dist/browser/runtime/sign.js b/dist/browser/runtime/sign.js new file mode 100644 index 0000000000..3ccb9e16c3 --- /dev/null +++ b/dist/browser/runtime/sign.js @@ -0,0 +1,11 @@ +import subtleAlgorithm from './subtle_dsa.js'; +import crypto from './webcrypto.js'; +import checkKeyLength from './check_key_length.js'; +import getSignKey from './get_sign_verify_key.js'; +const sign = async (alg, key, data) => { + const cryptoKey = await getSignKey(alg, key, 'sign'); + checkKeyLength(alg, cryptoKey); + const signature = await crypto.subtle.sign(subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data); + return new Uint8Array(signature); +}; +export default sign; diff --git a/dist/browser/runtime/subtle_dsa.js b/dist/browser/runtime/subtle_dsa.js new file mode 100644 index 0000000000..3df4514adf --- /dev/null +++ b/dist/browser/runtime/subtle_dsa.js @@ -0,0 +1,30 @@ +import { isCloudflareWorkers } from './env.js'; +import { JOSENotSupported } from '../util/errors.js'; +export default function subtleDsa(alg, algorithm) { + const hash = `SHA-${alg.slice(-3)}`; + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + return { hash, name: 'HMAC' }; + case 'PS256': + case 'PS384': + case 'PS512': + return { hash, name: 'RSA-PSS', saltLength: alg.slice(-3) >> 3 }; + case 'RS256': + case 'RS384': + case 'RS512': + return { hash, name: 'RSASSA-PKCS1-v1_5' }; + case 'ES256': + case 'ES384': + case 'ES512': + return { hash, name: 'ECDSA', namedCurve: algorithm.namedCurve }; + case 'EdDSA': + if (isCloudflareWorkers() && algorithm.name === 'NODE-ED25519') { + return { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' }; + } + return { name: algorithm.name }; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} diff --git a/dist/browser/runtime/subtle_rsaes.js b/dist/browser/runtime/subtle_rsaes.js new file mode 100644 index 0000000000..3a399bcfa7 --- /dev/null +++ b/dist/browser/runtime/subtle_rsaes.js @@ -0,0 +1,12 @@ +import { JOSENotSupported } from '../util/errors.js'; +export default function subtleRsaEs(alg) { + switch (alg) { + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + return 'RSA-OAEP'; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} diff --git a/dist/browser/runtime/timing_safe_equal.js b/dist/browser/runtime/timing_safe_equal.js new file mode 100644 index 0000000000..442cdccd20 --- /dev/null +++ b/dist/browser/runtime/timing_safe_equal.js @@ -0,0 +1,19 @@ +const timingSafeEqual = (a, b) => { + if (!(a instanceof Uint8Array)) { + throw new TypeError('First argument must be a buffer'); + } + if (!(b instanceof Uint8Array)) { + throw new TypeError('Second argument must be a buffer'); + } + if (a.length !== b.length) { + throw new TypeError('Input buffers must have the same length'); + } + const len = a.length; + let out = 0; + let i = -1; + while (++i < len) { + out |= a[i] ^ b[i]; + } + return out === 0; +}; +export default timingSafeEqual; diff --git a/dist/browser/runtime/verify.js b/dist/browser/runtime/verify.js new file mode 100644 index 0000000000..2ad4c7ffe2 --- /dev/null +++ b/dist/browser/runtime/verify.js @@ -0,0 +1,16 @@ +import subtleAlgorithm from './subtle_dsa.js'; +import crypto from './webcrypto.js'; +import checkKeyLength from './check_key_length.js'; +import getVerifyKey from './get_sign_verify_key.js'; +const verify = async (alg, key, signature, data) => { + const cryptoKey = await getVerifyKey(alg, key, 'verify'); + checkKeyLength(alg, cryptoKey); + const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm); + try { + return await crypto.subtle.verify(algorithm, cryptoKey, signature, data); + } + catch (_a) { + return false; + } +}; +export default verify; diff --git a/dist/browser/runtime/webcrypto.js b/dist/browser/runtime/webcrypto.js new file mode 100644 index 0000000000..f9e1e91539 --- /dev/null +++ b/dist/browser/runtime/webcrypto.js @@ -0,0 +1,2 @@ +export default crypto; +export const isCryptoKey = (key) => key instanceof CryptoKey; diff --git a/dist/browser/runtime/zlib.js b/dist/browser/runtime/zlib.js new file mode 100644 index 0000000000..e47cb532dc --- /dev/null +++ b/dist/browser/runtime/zlib.js @@ -0,0 +1,7 @@ +import { JOSENotSupported } from '../util/errors.js'; +export const inflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.'); +}; +export const deflate = async () => { + throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.'); +}; diff --git a/dist/browser/util/base64url.js b/dist/browser/util/base64url.js new file mode 100644 index 0000000000..88ce7556d6 --- /dev/null +++ b/dist/browser/util/base64url.js @@ -0,0 +1,3 @@ +import * as base64url from '../runtime/base64url.js'; +export const encode = base64url.encode; +export const decode = base64url.decode; diff --git a/dist/browser/util/decode_jwt.js b/dist/browser/util/decode_jwt.js new file mode 100644 index 0000000000..bea0ea9018 --- /dev/null +++ b/dist/browser/util/decode_jwt.js @@ -0,0 +1,32 @@ +import { decode as base64url } from './base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +import { JWTInvalid } from './errors.js'; +export function decodeJwt(jwt) { + if (typeof jwt !== 'string') + throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string'); + const { 1: payload, length } = jwt.split('.'); + if (length === 5) + throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded'); + if (length !== 3) + throw new JWTInvalid('Invalid JWT'); + if (!payload) + throw new JWTInvalid('JWTs must contain a payload'); + let decoded; + try { + decoded = base64url(payload); + } + catch (_a) { + throw new JWTInvalid('Failed to parse the base64url encoded payload'); + } + let result; + try { + result = JSON.parse(decoder.decode(decoded)); + } + catch (_b) { + throw new JWTInvalid('Failed to parse the decoded payload as JSON'); + } + if (!isObject(result)) + throw new JWTInvalid('Invalid JWT Claims Set'); + return result; +} diff --git a/dist/browser/util/decode_protected_header.js b/dist/browser/util/decode_protected_header.js new file mode 100644 index 0000000000..aecbfe2390 --- /dev/null +++ b/dist/browser/util/decode_protected_header.js @@ -0,0 +1,34 @@ +import { decode as base64url } from './base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +export function decodeProtectedHeader(token) { + let protectedB64u; + if (typeof token === 'string') { + const parts = token.split('.'); + if (parts.length === 3 || parts.length === 5) { + ; + [protectedB64u] = parts; + } + } + else if (typeof token === 'object' && token) { + if ('protected' in token) { + protectedB64u = token.protected; + } + else { + throw new TypeError('Token does not contain a Protected Header'); + } + } + try { + if (typeof protectedB64u !== 'string' || !protectedB64u) { + throw new Error(); + } + const result = JSON.parse(decoder.decode(base64url(protectedB64u))); + if (!isObject(result)) { + throw new Error(); + } + return result; + } + catch (_a) { + throw new TypeError('Invalid Token or Protected Header formatting'); + } +} diff --git a/dist/browser/util/errors.js b/dist/browser/util/errors.js new file mode 100644 index 0000000000..13a36f58fa --- /dev/null +++ b/dist/browser/util/errors.js @@ -0,0 +1,148 @@ +export class JOSEError extends Error { + static get code() { + return 'ERR_JOSE_GENERIC'; + } + constructor(message) { + var _a; + super(message); + this.code = 'ERR_JOSE_GENERIC'; + this.name = this.constructor.name; + (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); + } +} +export class JWTClaimValidationFailed extends JOSEError { + static get code() { + return 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + this.claim = claim; + this.reason = reason; + } +} +export class JWTExpired extends JOSEError { + static get code() { + return 'ERR_JWT_EXPIRED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_EXPIRED'; + this.claim = claim; + this.reason = reason; + } +} +export class JOSEAlgNotAllowed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_ALG_NOT_ALLOWED'; + } + static get code() { + return 'ERR_JOSE_ALG_NOT_ALLOWED'; + } +} +export class JOSENotSupported extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_NOT_SUPPORTED'; + } + static get code() { + return 'ERR_JOSE_NOT_SUPPORTED'; + } +} +export class JWEDecryptionFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_DECRYPTION_FAILED'; + this.message = 'decryption operation failed'; + } + static get code() { + return 'ERR_JWE_DECRYPTION_FAILED'; + } +} +export class JWEInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_INVALID'; + } + static get code() { + return 'ERR_JWE_INVALID'; + } +} +export class JWSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_INVALID'; + } + static get code() { + return 'ERR_JWS_INVALID'; + } +} +export class JWTInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWT_INVALID'; + } + static get code() { + return 'ERR_JWT_INVALID'; + } +} +export class JWKInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWK_INVALID'; + } + static get code() { + return 'ERR_JWK_INVALID'; + } +} +export class JWKSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_INVALID'; + } + static get code() { + return 'ERR_JWKS_INVALID'; + } +} +export class JWKSNoMatchingKey extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_NO_MATCHING_KEY'; + this.message = 'no applicable key found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_NO_MATCHING_KEY'; + } +} +export class JWKSMultipleMatchingKeys extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + this.message = 'multiple matching keys found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + } +} +Symbol.asyncIterator; +export class JWKSTimeout extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_TIMEOUT'; + this.message = 'request timed out'; + } + static get code() { + return 'ERR_JWKS_TIMEOUT'; + } +} +export class JWSSignatureVerificationFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + this.message = 'signature verification failed'; + } + static get code() { + return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + } +} diff --git a/dist/deno/README.md b/dist/deno/README.md new file mode 100644 index 0000000000..1f7257a1f5 --- /dev/null +++ b/dist/deno/README.md @@ -0,0 +1,82 @@ +# `jose` API Documentation + +`jose` is JavaScript module for JSON Object Signing and Encryption, providing support for JSON Web Tokens (JWT), JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Key Set (JWKS), and more. The module is designed to work across various Web-interoperable runtimes including Node.js, browsers, Cloudflare Workers, Deno, Bun, and others. + +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +## Available modules + +**`example`** Deno import +```js +import * as jose from 'https://deno.land/x/jose@v4.13.2/index.ts' +``` + +### JSON Web Tokens (JWT) + +The `jose` module supports JSON Web Tokens (JWT) and provides functionality for signing and verifying tokens, as well as their JWT Claims Set validation. + +- [Signing](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwt_sign.SignJWT.md) using the `SignJWT` class +- [Verification & JWT Claims Set Validation](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwt_verify.jwtVerify.md) using the `jwtVerify` function + - [Using a remote JWKS](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwks_remote.createRemoteJWKSet.md) + - [Using a local JWKS](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwks_local.createLocalJWKSet.md) +- Utility functions + - [Decoding Token's Protected Header](https://github.com/panva/jose/blob/v4.13.2/docs/functions/util_decode_protected_header.decodeProtectedHeader.md) + - [Decoding JWT Claims Set](https://github.com/panva/jose/blob/v4.13.2/docs/functions/util_decode_jwt.decodeJwt.md) prior to its validation + +### Encrypted JSON Web Tokens + +The `jose` module supports encrypted JSON Web Tokens and provides functionality for encrypting and decrypting tokens, as well as their JWT Claims Set validation. + +- [Encryption](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwt_encrypt.EncryptJWT.md) using the `EncryptJWT` class +- [Decryption & JWT Claims Set Validation](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwt_decrypt.jwtDecrypt.md) using the `jwtDecrypt` function +- Utility functions + - [Decoding Token's Protected Header](https://github.com/panva/jose/blob/v4.13.2/docs/functions/util_decode_protected_header.decodeProtectedHeader.md) + +### Key Utilities + +The `jose` module provides functionality for importing, exporting, and generating keys and secrets in various formats, including PEM formats like SPKI, X.509 certificate, and PKCS #8, as well as JSON Web Key (JWK). + +- Key Import Functions + - [JWK Import](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_import.importJWK.md) + - [Public Key Import (SPKI)](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_import.importSPKI.md) + - [Public Key Import (X.509 Certificate)](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_import.importX509.md) + - [Private Key Import (PKCS #8)](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_import.importPKCS8.md) +- Key and Secret Generation Functions + - [Asymmetric Key Pair Generation](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_generate_key_pair.generateKeyPair.md) + - [Symmetric Secret Generation](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_generate_secret.generateSecret.md) +- Key Export Functions + - [JWK Export](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_export.exportJWK.md) + - [Private Key Export](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_export.exportPKCS8.md) + - [Public Key Export](https://github.com/panva/jose/blob/v4.13.2/docs/functions/key_export.exportSPKI.md) + +### JSON Web Signature (JWS) + +The `jose` modules supports signing and verification of JWS messages with arbitrary payloads in Compact, Flattened JSON, and General JSON serialization syntaxes. + +- Signing - [Compact](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jws_compact_sign.CompactSign.md), [Flattened JSON](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jws_flattened_sign.FlattenedSign.md), [General JSON](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jws_general_sign.GeneralSign.md) +- Verification - [Compact](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jws_compact_verify.compactVerify.md), [Flattened JSON](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jws_flattened_verify.flattenedVerify.md), [General JSON](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jws_general_verify.generalVerify.md) + - [Verify using a remote JWKS](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwks_remote.createRemoteJWKSet.md) + - [Verify using a local JWKS](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwks_local.createLocalJWKSet.md) +- Utility functions + - [Decoding Token's Protected Header](https://github.com/panva/jose/blob/v4.13.2/docs/functions/util_decode_protected_header.decodeProtectedHeader.md) + +### JSON Web Encryption (JWE) + +The `jose` modules supports encryption and decryption of JWE messages with arbitrary plaintext in Compact, Flattened JSON, and General JSON serialization syntaxes. + +- Encryption - [Compact](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwe_compact_encrypt.CompactEncrypt.md), [Flattened JSON](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md), [General JSON](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwe_general_encrypt.GeneralEncrypt.md) +- Decryption - [Compact](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwe_compact_decrypt.compactDecrypt.md), [Flattened JSON](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md), [General JSON](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwe_general_decrypt.generalDecrypt.md) +- Utility functions + - [Decoding Token's Protected Header](https://github.com/panva/jose/blob/v4.13.2/docs/functions/util_decode_protected_header.decodeProtectedHeader.md) + +### Other + +The following are additional features and utilities provided by the `jose` module: + +- [Calculating JWK Thumbprint](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwk_thumbprint.calculateJwkThumbprint.md) +- [Calculating JWK Thumbprint URI](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md) +- [Verification using a JWK Embedded in a JWS Header](https://github.com/panva/jose/blob/v4.13.2/docs/functions/jwk_embedded.EmbeddedJWK.md) +- [Unsecured JWT](https://github.com/panva/jose/blob/v4.13.2/docs/classes/jwt_unsecured.UnsecuredJWT.md) +- [JOSE Errors](https://github.com/panva/jose/blob/v4.13.2/docs/modules/util_errors.md) diff --git a/dist/deno/index.ts b/dist/deno/index.ts new file mode 100644 index 0000000000..611b8e0d4c --- /dev/null +++ b/dist/deno/index.ts @@ -0,0 +1,98 @@ +export { compactDecrypt } from './jwe/compact/decrypt.ts' +export type { CompactDecryptGetKey } from './jwe/compact/decrypt.ts' +export { flattenedDecrypt } from './jwe/flattened/decrypt.ts' +export type { FlattenedDecryptGetKey } from './jwe/flattened/decrypt.ts' +export { generalDecrypt } from './jwe/general/decrypt.ts' +export type { GeneralDecryptGetKey } from './jwe/general/decrypt.ts' +export { GeneralEncrypt } from './jwe/general/encrypt.ts' +export type { Recipient } from './jwe/general/encrypt.ts' + +export { compactVerify } from './jws/compact/verify.ts' +export type { CompactVerifyGetKey } from './jws/compact/verify.ts' +export { flattenedVerify } from './jws/flattened/verify.ts' +export type { FlattenedVerifyGetKey } from './jws/flattened/verify.ts' +export { generalVerify } from './jws/general/verify.ts' +export type { GeneralVerifyGetKey } from './jws/general/verify.ts' + +export { jwtVerify } from './jwt/verify.ts' +export type { JWTVerifyOptions, JWTVerifyGetKey } from './jwt/verify.ts' +export { jwtDecrypt } from './jwt/decrypt.ts' +export type { JWTDecryptOptions, JWTDecryptGetKey } from './jwt/decrypt.ts' +export type { ProduceJWT } from './jwt/produce.ts' + +export { CompactEncrypt } from './jwe/compact/encrypt.ts' +export { FlattenedEncrypt } from './jwe/flattened/encrypt.ts' + +export { CompactSign } from './jws/compact/sign.ts' +export { FlattenedSign } from './jws/flattened/sign.ts' +export { GeneralSign } from './jws/general/sign.ts' +export type { Signature } from './jws/general/sign.ts' + +export { SignJWT } from './jwt/sign.ts' +export { EncryptJWT } from './jwt/encrypt.ts' + +export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.ts' +export { EmbeddedJWK } from './jwk/embedded.ts' + +export { createLocalJWKSet } from './jwks/local.ts' +export { createRemoteJWKSet } from './jwks/remote.ts' +export type { RemoteJWKSetOptions } from './jwks/remote.ts' + +export { UnsecuredJWT } from './jwt/unsecured.ts' +export type { UnsecuredResult } from './jwt/unsecured.ts' + +export { exportPKCS8, exportSPKI, exportJWK } from './key/export.ts' + +export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.ts' +export type { PEMImportOptions } from './key/import.ts' + +export { decodeProtectedHeader } from './util/decode_protected_header.ts' +export { decodeJwt } from './util/decode_jwt.ts' +export type { ProtectedHeaderParameters } from './util/decode_protected_header.ts' + +export * as errors from './util/errors.ts' + +export { generateKeyPair } from './key/generate_key_pair.ts' +export type { GenerateKeyPairResult, GenerateKeyPairOptions } from './key/generate_key_pair.ts' +export { generateSecret } from './key/generate_secret.ts' +export type { GenerateSecretOptions } from './key/generate_secret.ts' + +export * as base64url from './util/base64url.ts' + +export type { + KeyLike, + JWK, + FlattenedJWSInput, + GeneralJWSInput, + FlattenedJWS, + GeneralJWS, + JoseHeaderParameters, + JWSHeaderParameters, + JWEKeyManagementHeaderParameters, + FlattenedJWE, + GeneralJWE, + JWEHeaderParameters, + CritOption, + DeflateOption, + DecryptOptions, + EncryptOptions, + JWTClaimVerificationOptions, + VerifyOptions, + SignOptions, + JWTPayload, + DeflateFunction, + InflateFunction, + FlattenedDecryptResult, + GeneralDecryptResult, + CompactDecryptResult, + FlattenedVerifyResult, + GeneralVerifyResult, + CompactVerifyResult, + JWTVerifyResult, + JWTDecryptResult, + ResolvedKey, + CompactJWEHeaderParameters, + CompactJWSHeaderParameters, + JWTHeaderParameters, + JSONWebKeySet, +} from './types.d.ts' diff --git a/dist/deno/jwe/compact/decrypt.ts b/dist/deno/jwe/compact/decrypt.ts new file mode 100644 index 0000000000..a98d812083 --- /dev/null +++ b/dist/deno/jwe/compact/decrypt.ts @@ -0,0 +1,89 @@ +import { flattenedDecrypt } from '../flattened/decrypt.ts' +import { JWEInvalid } from '../../util/errors.ts' +import { decoder } from '../../lib/buffer_utils.ts' +import type { + KeyLike, + DecryptOptions, + CompactJWEHeaderParameters, + GetKeyFunction, + FlattenedJWE, + CompactDecryptResult, + ResolvedKey, +} from '../../types.d.ts' + +/** + * Interface for Compact JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface CompactDecryptGetKey + extends GetKeyFunction {} + +/** + * Decrypts a Compact JWE. + * + * @param jwe Compact JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export async function compactDecrypt( + jwe: string | Uint8Array, + key: KeyLike | Uint8Array, + options?: DecryptOptions, +): Promise +/** + * @param jwe Compact JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export async function compactDecrypt( + jwe: string | Uint8Array, + getKey: CompactDecryptGetKey, + options?: DecryptOptions, +): Promise> +export async function compactDecrypt( + jwe: string | Uint8Array, + key: KeyLike | Uint8Array | CompactDecryptGetKey, + options?: DecryptOptions, +) { + if (jwe instanceof Uint8Array) { + jwe = decoder.decode(jwe) + } + + if (typeof jwe !== 'string') { + throw new JWEInvalid('Compact JWE must be a string or Uint8Array') + } + const { + 0: protectedHeader, + 1: encryptedKey, + 2: iv, + 3: ciphertext, + 4: tag, + length, + } = jwe.split('.') + + if (length !== 5) { + throw new JWEInvalid('Invalid Compact JWE') + } + + const decrypted = await flattenedDecrypt( + { + ciphertext, + iv: (iv || undefined), + protected: protectedHeader || undefined, + tag: (tag || undefined), + encrypted_key: encryptedKey || undefined, + }, + [1]>key, + options, + ) + + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader! } + + if (typeof key === 'function') { + return { ...result, key: decrypted.key } + } + + return result +} diff --git a/dist/deno/jwe/compact/encrypt.ts b/dist/deno/jwe/compact/encrypt.ts new file mode 100644 index 0000000000..aa27001def --- /dev/null +++ b/dist/deno/jwe/compact/encrypt.ts @@ -0,0 +1,81 @@ +import { FlattenedEncrypt } from '../flattened/encrypt.ts' +import type { + KeyLike, + JWEKeyManagementHeaderParameters, + CompactJWEHeaderParameters, + EncryptOptions, +} from '../../types.d.ts' + +/** + * The CompactEncrypt class is used to build and encrypt Compact JWE strings. + * + */ +export class CompactEncrypt { + private _flattened: FlattenedEncrypt + + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array) { + this._flattened = new FlattenedEncrypt(plaintext) + } + + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array) { + this._flattened.setContentEncryptionKey(cek) + return this + } + + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array) { + this._flattened.setInitializationVector(iv) + return this + } + + /** + * Sets the JWE Protected Header on the CompactEncrypt object. + * + * @param protectedHeader JWE Protected Header object. + */ + setProtectedHeader(protectedHeader: CompactJWEHeaderParameters) { + this._flattened.setProtectedHeader(protectedHeader) + return this + } + + /** + * Sets the JWE Key Management parameters to be used when encrypting the Content Encryption Key. + * You do not need to invoke this method, it is only really intended for test and vector + * validation purposes. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters) { + this._flattened.setKeyManagementParameters(parameters) + return this + } + + /** + * Encrypts and resolves the value of the Compact JWE string. + * + * @param key Public Key or Secret to encrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + async encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions): Promise { + const jwe = await this._flattened.encrypt(key, options) + + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.') + } +} diff --git a/dist/deno/jwe/flattened/decrypt.ts b/dist/deno/jwe/flattened/decrypt.ts new file mode 100644 index 0000000000..d3858094a8 --- /dev/null +++ b/dist/deno/jwe/flattened/decrypt.ts @@ -0,0 +1,227 @@ +import { decode as base64url } from '../../runtime/base64url.ts' +import decrypt from '../../runtime/decrypt.ts' +import { inflate } from '../../runtime/zlib.ts' + +import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.ts' +import isDisjoint from '../../lib/is_disjoint.ts' +import isObject from '../../lib/is_object.ts' +import decryptKeyManagement from '../../lib/decrypt_key_management.ts' +import type { + FlattenedDecryptResult, + KeyLike, + FlattenedJWE, + JWEHeaderParameters, + DecryptOptions, + GetKeyFunction, + ResolvedKey, +} from '../../types.d.ts' +import { encoder, decoder, concat } from '../../lib/buffer_utils.ts' +import generateCek from '../../lib/cek.ts' +import validateCrit from '../../lib/validate_crit.ts' +import validateAlgorithms from '../../lib/validate_algorithms.ts' + +/** + * Interface for Flattened JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface FlattenedDecryptGetKey + extends GetKeyFunction {} + +/** + * Decrypts a Flattened JWE. + * + * @param jwe Flattened JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export function flattenedDecrypt( + jwe: FlattenedJWE, + key: KeyLike | Uint8Array, + options?: DecryptOptions, +): Promise +/** + * @param jwe Flattened JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export function flattenedDecrypt( + jwe: FlattenedJWE, + getKey: FlattenedDecryptGetKey, + options?: DecryptOptions, +): Promise> +export async function flattenedDecrypt( + jwe: FlattenedJWE, + key: KeyLike | Uint8Array | FlattenedDecryptGetKey, + options?: DecryptOptions, +) { + if (!isObject(jwe)) { + throw new JWEInvalid('Flattened JWE must be an object') + } + + if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { + throw new JWEInvalid('JOSE Header missing') + } + + if (typeof jwe.iv !== 'string') { + throw new JWEInvalid('JWE Initialization Vector missing or incorrect type') + } + + if (typeof jwe.ciphertext !== 'string') { + throw new JWEInvalid('JWE Ciphertext missing or incorrect type') + } + + if (typeof jwe.tag !== 'string') { + throw new JWEInvalid('JWE Authentication Tag missing or incorrect type') + } + + if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { + throw new JWEInvalid('JWE Protected Header incorrect type') + } + + if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { + throw new JWEInvalid('JWE Encrypted Key incorrect type') + } + + if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { + throw new JWEInvalid('JWE AAD incorrect type') + } + + if (jwe.header !== undefined && !isObject(jwe.header)) { + throw new JWEInvalid('JWE Shared Unprotected Header incorrect type') + } + + if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { + throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type') + } + + let parsedProt!: JWEHeaderParameters + if (jwe.protected) { + try { + const protectedHeader = base64url(jwe.protected) + parsedProt = JSON.parse(decoder.decode(protectedHeader)) + } catch { + throw new JWEInvalid('JWE Protected Header is invalid') + } + } + if (!isDisjoint(parsedProt, jwe.header, jwe.unprotected)) { + throw new JWEInvalid( + 'JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint', + ) + } + + const joseHeader: JWEHeaderParameters = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected, + } + + validateCrit(JWEInvalid, new Map(), options?.crit, parsedProt, joseHeader) + + if (joseHeader.zip !== undefined) { + if (!parsedProt || !parsedProt.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected') + } + + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported( + 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value', + ) + } + } + + const { alg, enc } = joseHeader + + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header') + } + + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header') + } + + const keyManagementAlgorithms = + options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms) + const contentEncryptionAlgorithms = + options && + validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms) + + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed') + } + + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed') + } + + let encryptedKey!: Uint8Array + if (jwe.encrypted_key !== undefined) { + encryptedKey = base64url(jwe.encrypted_key!) + } + + let resolvedKey = false + if (typeof key === 'function') { + key = await key(parsedProt, jwe) + resolvedKey = true + } + + let cek: KeyLike | Uint8Array + try { + cek = await decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) + } catch (err) { + if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { + throw err + } + // https://www.rfc-editor.org/rfc/rfc7516#section-11.5 + // To mitigate the attacks described in RFC 3218, the + // recipient MUST NOT distinguish between format, padding, and length + // errors of encrypted keys. It is strongly recommended, in the event + // of receiving an improperly formatted key, that the recipient + // substitute a randomly generated CEK and proceed to the next step, to + // mitigate timing attacks. + cek = generateCek(enc) + } + + const iv = base64url(jwe.iv) + const tag = base64url(jwe.tag) + + const protectedHeader: Uint8Array = encoder.encode(jwe.protected ?? '') + let additionalData: Uint8Array + + if (jwe.aad !== undefined) { + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)) + } else { + additionalData = protectedHeader + } + + let plaintext = await decrypt(enc, cek, base64url(jwe.ciphertext), iv, tag, additionalData) + + if (joseHeader.zip === 'DEF') { + plaintext = await (options?.inflateRaw || inflate)(plaintext) + } + + const result: FlattenedDecryptResult = { plaintext } + + if (jwe.protected !== undefined) { + result.protectedHeader = parsedProt + } + + if (jwe.aad !== undefined) { + result.additionalAuthenticatedData = base64url(jwe.aad!) + } + + if (jwe.unprotected !== undefined) { + result.sharedUnprotectedHeader = jwe.unprotected + } + + if (jwe.header !== undefined) { + result.unprotectedHeader = jwe.header + } + + if (resolvedKey) { + return { ...result, key } + } + + return result +} diff --git a/dist/deno/jwe/flattened/encrypt.ts b/dist/deno/jwe/flattened/encrypt.ts new file mode 100644 index 0000000000..c6a33fe95c --- /dev/null +++ b/dist/deno/jwe/flattened/encrypt.ts @@ -0,0 +1,298 @@ +import { encode as base64url } from '../../runtime/base64url.ts' +import encrypt from '../../runtime/encrypt.ts' +import { deflate } from '../../runtime/zlib.ts' + +import type { + KeyLike, + FlattenedJWE, + JWEHeaderParameters, + JWEKeyManagementHeaderParameters, + EncryptOptions, +} from '../../types.d.ts' +import generateIv from '../../lib/iv.ts' +import encryptKeyManagement from '../../lib/encrypt_key_management.ts' +import { JOSENotSupported, JWEInvalid } from '../../util/errors.ts' +import isDisjoint from '../../lib/is_disjoint.ts' +import { encoder, decoder, concat } from '../../lib/buffer_utils.ts' +import validateCrit from '../../lib/validate_crit.ts' + +/** @private */ +export const unprotected = Symbol() + +/** + * The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. + * + */ +export class FlattenedEncrypt { + private _plaintext: Uint8Array + + private _protectedHeader!: JWEHeaderParameters + + private _sharedUnprotectedHeader!: JWEHeaderParameters + + private _unprotectedHeader!: JWEHeaderParameters + + private _aad!: Uint8Array + + private _cek!: Uint8Array + + private _iv!: Uint8Array + + private _keyManagementParameters!: JWEKeyManagementHeaderParameters + + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError('plaintext must be an instance of Uint8Array') + } + this._plaintext = plaintext + } + + /** + * Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is + * really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or + * Agreement PartyVInfo parameters. Other parameters will always be randomly generated when needed + * and missing. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once') + } + this._keyManagementParameters = parameters + return this + } + + /** + * Sets the JWE Protected Header on the FlattenedEncrypt object. + * + * @param protectedHeader JWE Protected Header. + */ + setProtectedHeader(protectedHeader: JWEHeaderParameters) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once') + } + this._protectedHeader = protectedHeader + return this + } + + /** + * Sets the JWE Shared Unprotected Header on the FlattenedEncrypt object. + * + * @param sharedUnprotectedHeader JWE Shared Unprotected Header. + */ + setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters) { + if (this._sharedUnprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once') + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader + return this + } + + /** + * Sets the JWE Per-Recipient Unprotected Header on the FlattenedEncrypt object. + * + * @param unprotectedHeader JWE Per-Recipient Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once') + } + this._unprotectedHeader = unprotectedHeader + return this + } + + /** + * Sets the Additional Authenticated Data on the FlattenedEncrypt object. + * + * @param aad Additional Authenticated Data. + */ + setAdditionalAuthenticatedData(aad: Uint8Array) { + this._aad = aad + return this + } + + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once') + } + this._cek = cek + return this + } + + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once') + } + this._iv = iv + return this + } + + /** + * Encrypts and resolves the value of the Flattened JWE object. + * + * @param key Public Key or Secret to encrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + async encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new JWEInvalid( + 'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()', + ) + } + + if ( + !isDisjoint(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader) + ) { + throw new JWEInvalid( + 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', + ) + } + + const joseHeader: JWEHeaderParameters = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader, + } + + validateCrit(JWEInvalid, new Map(), options?.crit, this._protectedHeader, joseHeader) + + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected') + } + + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported( + 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value', + ) + } + } + + const { alg, enc } = joseHeader + + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid') + } + + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid') + } + + let encryptedKey: Uint8Array | undefined + + if (alg === 'dir') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption') + } + } else if (alg === 'ECDH-ES') { + if (this._cek) { + throw new TypeError( + 'setContentEncryptionKey cannot be called when using Direct Key Agreement', + ) + } + } + + let cek: KeyLike | Uint8Array + { + let parameters: { [propName: string]: unknown } | undefined + ;({ cek, encryptedKey, parameters } = await encryptKeyManagement( + alg, + enc, + key, + this._cek, + this._keyManagementParameters, + )) + + if (parameters) { + if (options && unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters) + } else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters } + } + } else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters) + } else { + this._protectedHeader = { ...this._protectedHeader, ...parameters } + } + } + } + } + + this._iv ||= generateIv(enc) + + let additionalData: Uint8Array + let protectedHeader: Uint8Array + let aadMember: string | undefined + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))) + } else { + protectedHeader = encoder.encode('') + } + + if (this._aad) { + aadMember = base64url(this._aad) + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(aadMember)) + } else { + additionalData = protectedHeader + } + + let ciphertext: Uint8Array + let tag: Uint8Array + + if (joseHeader.zip === 'DEF') { + const deflated = await (options?.deflateRaw || deflate)(this._plaintext) + ;({ ciphertext, tag } = await encrypt(enc, deflated, cek, this._iv, additionalData)) + } else { + ;({ ciphertext, tag } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData)) + } + + const jwe: FlattenedJWE = { + ciphertext: base64url(ciphertext), + iv: base64url(this._iv), + tag: base64url(tag), + } + + if (encryptedKey) { + jwe.encrypted_key = base64url(encryptedKey) + } + + if (aadMember) { + jwe.aad = aadMember + } + + if (this._protectedHeader) { + jwe.protected = decoder.decode(protectedHeader) + } + + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader + } + + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader + } + + return jwe + } +} diff --git a/dist/deno/jwe/general/decrypt.ts b/dist/deno/jwe/general/decrypt.ts new file mode 100644 index 0000000000..58bc4d8782 --- /dev/null +++ b/dist/deno/jwe/general/decrypt.ts @@ -0,0 +1,83 @@ +import { flattenedDecrypt } from '../flattened/decrypt.ts' +import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.ts' +import type { + KeyLike, + DecryptOptions, + JWEHeaderParameters, + GetKeyFunction, + FlattenedJWE, + GeneralJWE, + GeneralDecryptResult, + ResolvedKey, +} from '../../types.d.ts' +import isObject from '../../lib/is_object.ts' + +/** + * Interface for General JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface GeneralDecryptGetKey extends GetKeyFunction {} + +/** + * Decrypts a General JWE. + * + * @param jwe General JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export function generalDecrypt( + jwe: GeneralJWE, + key: KeyLike | Uint8Array, + options?: DecryptOptions, +): Promise +/** + * @param jwe General JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export function generalDecrypt( + jwe: GeneralJWE, + getKey: GeneralDecryptGetKey, + options?: DecryptOptions, +): Promise> +export async function generalDecrypt( + jwe: GeneralJWE, + key: KeyLike | Uint8Array | GeneralDecryptGetKey, + options?: DecryptOptions, +) { + if (!isObject(jwe)) { + throw new JWEInvalid('General JWE must be an object') + } + + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { + throw new JWEInvalid('JWE Recipients missing or incorrect type') + } + + if (!jwe.recipients.length) { + throw new JWEInvalid('JWE Recipients has no members') + } + + for (const recipient of jwe.recipients) { + try { + return await flattenedDecrypt( + { + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected, + }, + [1]>key, + options, + ) + } catch { + // + } + } + throw new JWEDecryptionFailed() +} diff --git a/dist/deno/jwe/general/encrypt.ts b/dist/deno/jwe/general/encrypt.ts new file mode 100644 index 0000000000..c4289d874f --- /dev/null +++ b/dist/deno/jwe/general/encrypt.ts @@ -0,0 +1,292 @@ +import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.ts' +import { JWEInvalid } from '../../util/errors.ts' +import generateCek from '../../lib/cek.ts' +import isDisjoint from '../../lib/is_disjoint.ts' +import encryptKeyManagement from '../../lib/encrypt_key_management.ts' +import { encode as base64url } from '../../runtime/base64url.ts' +import validateCrit from '../../lib/validate_crit.ts' + +import type { + KeyLike, + GeneralJWE, + JWEHeaderParameters, + CritOption, + DeflateOption, +} from '../../types.d.ts' + +export interface Recipient { + /** + * Sets the JWE Per-Recipient Unprotected Header on the Recipient object. + * + * @param unprotectedHeader JWE Per-Recipient Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): Recipient + + /** A shorthand for calling addRecipient() on the enclosing GeneralEncrypt instance */ + addRecipient(...args: Parameters): Recipient + + /** A shorthand for calling encrypt() on the enclosing GeneralEncrypt instance */ + encrypt(...args: Parameters): Promise + + /** Returns the enclosing GeneralEncrypt */ + done(): GeneralEncrypt +} + +class IndividualRecipient implements Recipient { + private parent: GeneralEncrypt + unprotectedHeader?: JWEHeaderParameters + key: KeyLike | Uint8Array + options: CritOption + + constructor(enc: GeneralEncrypt, key: KeyLike | Uint8Array, options: CritOption) { + this.parent = enc + this.key = key + this.options = options + } + + setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once') + } + this.unprotectedHeader = unprotectedHeader + return this + } + + addRecipient(...args: Parameters) { + return this.parent.addRecipient(...args) + } + + encrypt(...args: Parameters) { + return this.parent.encrypt(...args) + } + + done() { + return this.parent + } +} + +/** + * The GeneralEncrypt class is used to build and encrypt General JWE objects. + * + */ +export class GeneralEncrypt { + private _plaintext: Uint8Array + + private _recipients: IndividualRecipient[] = [] + + private _protectedHeader!: JWEHeaderParameters + + private _unprotectedHeader!: JWEHeaderParameters + + private _aad!: Uint8Array + + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array) { + this._plaintext = plaintext + } + + /** + * Adds an additional recipient for the General JWE object. + * + * @param key Public Key or Secret to encrypt the Content Encryption Key for the recipient with. + * See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + addRecipient(key: KeyLike | Uint8Array, options?: CritOption): Recipient { + const recipient = new IndividualRecipient(this, key, { crit: options?.crit }) + this._recipients.push(recipient) + return recipient + } + + /** + * Sets the JWE Protected Header on the GeneralEncrypt object. + * + * @param protectedHeader JWE Protected Header object. + */ + setProtectedHeader(protectedHeader: JWEHeaderParameters): this { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once') + } + this._protectedHeader = protectedHeader + return this + } + + /** + * Sets the JWE Shared Unprotected Header on the GeneralEncrypt object. + * + * @param sharedUnprotectedHeader JWE Shared Unprotected Header object. + */ + setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this { + if (this._unprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once') + } + this._unprotectedHeader = sharedUnprotectedHeader + return this + } + + /** + * Sets the Additional Authenticated Data on the GeneralEncrypt object. + * + * @param aad Additional Authenticated Data. + */ + setAdditionalAuthenticatedData(aad: Uint8Array) { + this._aad = aad + return this + } + + /** + * Encrypts and resolves the value of the General JWE object. + * + * @param options JWE Encryption options. + */ + async encrypt(options?: DeflateOption): Promise { + if (!this._recipients.length) { + throw new JWEInvalid('at least one recipient must be added') + } + + options = { deflateRaw: options?.deflateRaw } + + if (this._recipients.length === 1) { + const [recipient] = this._recipients + + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader!) + .encrypt(recipient.key, { ...recipient.options, ...options }) + + let jwe: GeneralJWE = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag, + } + + if (flattened.aad) jwe.aad = flattened.aad + if (flattened.protected) jwe.protected = flattened.protected + if (flattened.unprotected) jwe.unprotected = flattened.unprotected + if (flattened.encrypted_key) jwe.recipients![0].encrypted_key = flattened.encrypted_key + if (flattened.header) jwe.recipients![0].header = flattened.header + + return jwe + } + + let enc!: string + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i] + if ( + !isDisjoint(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader) + ) { + throw new JWEInvalid( + 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', + ) + } + + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + } + + const { alg } = joseHeader + + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid') + } + + if (alg === 'dir' || alg === 'ECDH-ES') { + throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient') + } + + if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid') + } + + if (!enc) { + enc = joseHeader.enc + } else if (enc !== joseHeader.enc) { + throw new JWEInvalid( + 'JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients', + ) + } + + validateCrit(JWEInvalid, new Map(), recipient.options.crit, this._protectedHeader, joseHeader) + + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid( + 'JWE "zip" (Compression Algorithm) Header MUST be integrity protected', + ) + } + } + } + + const cek = generateCek(enc) + + let jwe: GeneralJWE = { + ciphertext: '', + iv: '', + recipients: [], + tag: '', + } + + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i] + const target: Record = {} + jwe.recipients!.push(target) + + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + } + + const p2c = joseHeader.alg!.startsWith('PBES2') ? 2048 + i : undefined + + if (i === 0) { + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setContentEncryptionKey(cek) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader!) + .setKeyManagementParameters({ p2c }) + .encrypt(recipient.key, { + ...recipient.options, + ...options, + // @ts-expect-error + [unprotected]: true, + }) + + jwe.ciphertext = flattened.ciphertext + jwe.iv = flattened.iv + jwe.tag = flattened.tag + + if (flattened.aad) jwe.aad = flattened.aad + if (flattened.protected) jwe.protected = flattened.protected + if (flattened.unprotected) jwe.unprotected = flattened.unprotected + + target.encrypted_key = flattened.encrypted_key! + if (flattened.header) target.header = flattened.header + + continue + } + + const { encryptedKey, parameters } = await encryptKeyManagement( + recipient.unprotectedHeader?.alg! || + this._protectedHeader?.alg! || + this._unprotectedHeader?.alg!, + enc, + recipient.key, + cek, + { p2c }, + ) + target.encrypted_key = base64url(encryptedKey!) + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters } + } + + return jwe + } +} diff --git a/dist/deno/jwk/embedded.ts b/dist/deno/jwk/embedded.ts new file mode 100644 index 0000000000..7bce7316f3 --- /dev/null +++ b/dist/deno/jwk/embedded.ts @@ -0,0 +1,32 @@ +import type { KeyLike, FlattenedJWSInput, JWSHeaderParameters } from '../types.d.ts' +import { importJWK } from '../key/import.ts' +import isObject from '../lib/is_object.ts' +import { JWSInvalid } from '../util/errors.ts' + +/** + * EmbeddedJWK is an implementation of a GetKeyFunction intended to be used with the JWS/JWT verify + * operations whenever you need to opt-in to verify signatures with a public key embedded in the + * token's "jwk" (JSON Web Key) Header Parameter. It is recommended to combine this with the verify + * function's `algorithms` option to define accepted JWS "alg" (Algorithm) Header Parameter values. + * + */ +export async function EmbeddedJWK( + protectedHeader?: JWSHeaderParameters, + token?: FlattenedJWSInput, +): Promise { + const joseHeader = { + ...protectedHeader, + ...token?.header, + } + if (!isObject(joseHeader.jwk)) { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object') + } + + const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg!, true) + + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key') + } + + return key +} diff --git a/dist/deno/jwk/thumbprint.ts b/dist/deno/jwk/thumbprint.ts new file mode 100644 index 0000000000..1b158af94d --- /dev/null +++ b/dist/deno/jwk/thumbprint.ts @@ -0,0 +1,86 @@ +import digest from '../runtime/digest.ts' +import { encode as base64url } from '../runtime/base64url.ts' + +import { JOSENotSupported, JWKInvalid } from '../util/errors.ts' +import { encoder } from '../lib/buffer_utils.ts' +import type { JWK } from '../types.d.ts' +import isObject from '../lib/is_object.ts' + +const check = (value: unknown, description: string) => { + if (typeof value !== 'string' || !value) { + throw new JWKInvalid(`${description} missing or invalid`) + } +} + +/** + * Calculates a base64url-encoded JSON Web Key (JWK) Thumbprint + * + * @param jwk JSON Web Key. + * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is + * "sha256". + * @see {@link https://www.rfc-editor.org/rfc/rfc7638 RFC7638} + */ +export async function calculateJwkThumbprint( + jwk: JWK, + digestAlgorithm?: 'sha256' | 'sha384' | 'sha512', +): Promise { + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object') + } + + digestAlgorithm ??= 'sha256' + + if ( + digestAlgorithm !== 'sha256' && + digestAlgorithm !== 'sha384' && + digestAlgorithm !== 'sha512' + ) { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"') + } + + let components: JWK + switch (jwk.kty) { + case 'EC': + check(jwk.crv, '"crv" (Curve) Parameter') + check(jwk.x, '"x" (X Coordinate) Parameter') + check(jwk.y, '"y" (Y Coordinate) Parameter') + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y } + break + case 'OKP': + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter') + check(jwk.x, '"x" (Public Key) Parameter') + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x } + break + case 'RSA': + check(jwk.e, '"e" (Exponent) Parameter') + check(jwk.n, '"n" (Modulus) Parameter') + components = { e: jwk.e, kty: jwk.kty, n: jwk.n } + break + case 'oct': + check(jwk.k, '"k" (Key Value) Parameter') + components = { k: jwk.k, kty: jwk.kty } + break + default: + throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported') + } + + const data = encoder.encode(JSON.stringify(components)) + return base64url(await digest(digestAlgorithm, data)) +} + +/** + * Calculates a JSON Web Key (JWK) Thumbprint URI + * + * @param jwk JSON Web Key. + * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is + * "sha256". + * @see {@link https://www.rfc-editor.org/rfc/rfc9278 RFC9278} + */ +export async function calculateJwkThumbprintUri( + jwk: JWK, + digestAlgorithm?: 'sha256' | 'sha384' | 'sha512', +): Promise { + digestAlgorithm ??= 'sha256' + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm) + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}` +} diff --git a/dist/deno/jwks/local.ts b/dist/deno/jwks/local.ts new file mode 100644 index 0000000000..273a2fce92 --- /dev/null +++ b/dist/deno/jwks/local.ts @@ -0,0 +1,196 @@ +import type { + KeyLike, + JWSHeaderParameters, + JWK, + JSONWebKeySet, + FlattenedJWSInput, +} from '../types.d.ts' +import { importJWK } from '../key/import.ts' +import { + JWKSInvalid, + JOSENotSupported, + JWKSNoMatchingKey, + JWKSMultipleMatchingKeys, +} from '../util/errors.ts' +import isObject from '../lib/is_object.ts' + +function getKtyFromAlg(alg: unknown) { + switch (typeof alg === 'string' && alg.slice(0, 2)) { + case 'RS': + case 'PS': + return 'RSA' + case 'ES': + return 'EC' + case 'Ed': + return 'OKP' + default: + throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set') + } +} + +interface Cache { + [alg: string]: T +} + +/** @private */ +export function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet { + return ( + jwks && + typeof jwks === 'object' && + // @ts-expect-error + Array.isArray(jwks.keys) && + // @ts-expect-error + jwks.keys.every(isJWKLike) + ) +} + +function isJWKLike(key: unknown) { + return isObject(key) +} + +function clone(obj: T): T { + // @ts-ignore + if (typeof structuredClone === 'function') { + // @ts-ignore + return structuredClone(obj) + } + + return JSON.parse(JSON.stringify(obj)) +} + +/** @private */ +export class LocalJWKSet { + protected _jwks?: JSONWebKeySet + + private _cached: WeakMap> = new WeakMap() + + constructor(jwks: unknown) { + if (!isJWKSLike(jwks)) { + throw new JWKSInvalid('JSON Web Key Set malformed') + } + + this._jwks = clone(jwks) + } + + async getKey(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise { + const { alg, kid } = { ...protectedHeader, ...token?.header } + const kty = getKtyFromAlg(alg) + + const candidates = this._jwks!.keys.filter((jwk) => { + // filter keys based on the mapping of signature algorithms to Key Type + let candidate = kty === jwk.kty + + // filter keys based on the JWK Key ID in the header + if (candidate && typeof kid === 'string') { + candidate = kid === jwk.kid + } + + // filter keys based on the key's declared Algorithm + if (candidate && typeof jwk.alg === 'string') { + candidate = alg === jwk.alg + } + + // filter keys based on the key's declared Public Key Use + if (candidate && typeof jwk.use === 'string') { + candidate = jwk.use === 'sig' + } + + // filter keys based on the key's declared Key Operations + if (candidate && Array.isArray(jwk.key_ops)) { + candidate = jwk.key_ops.includes('verify') + } + + // filter out non-applicable OKP Sub Types + if (candidate && alg === 'EdDSA') { + candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448' + } + + // filter out non-applicable EC curves + if (candidate) { + switch (alg) { + case 'ES256': + candidate = jwk.crv === 'P-256' + break + case 'ES256K': + candidate = jwk.crv === 'secp256k1' + break + case 'ES384': + candidate = jwk.crv === 'P-384' + break + case 'ES512': + candidate = jwk.crv === 'P-521' + break + } + } + + return candidate + }) + + const { 0: jwk, length } = candidates + + if (length === 0) { + throw new JWKSNoMatchingKey() + } else if (length !== 1) { + const error = new JWKSMultipleMatchingKeys() + + const { _cached } = this + error[Symbol.asyncIterator] = async function* () { + for (const jwk of candidates) { + try { + yield await importWithAlgCache(_cached, jwk, alg!) + } catch { + continue + } + } + } + + throw error + } + + return importWithAlgCache(this._cached, jwk, alg!) + } +} + +async function importWithAlgCache( + cache: WeakMap>, + jwk: JWK, + alg: string, +) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk)! + if (cached[alg] === undefined) { + const key = await importJWK({ ...jwk, ext: true }, alg) + + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWKSInvalid('JSON Web Key Set members must be public keys') + } + + cached[alg] = key + } + + return cached[alg] +} + +/** + * Returns a function that resolves to a key object from a locally stored, or otherwise available, + * JSON Web Key Set. + * + * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), + * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if + * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key + * Operations) Parameters (if they are present on the JWK). + * + * Only a single public key must match the selection process. As shown in the example below when + * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt + * verification in an iterative manner. + * + * @param jwks JSON Web Key Set formatted object. + */ +export function createLocalJWKSet(jwks: JSONWebKeySet) { + const set = new LocalJWKSet(jwks) + return async function ( + protectedHeader?: JWSHeaderParameters, + token?: FlattenedJWSInput, + ): Promise { + return set.getKey(protectedHeader, token) + } +} diff --git a/dist/deno/jwks/remote.ts b/dist/deno/jwks/remote.ts new file mode 100644 index 0000000000..deb4a442e6 --- /dev/null +++ b/dist/deno/jwks/remote.ts @@ -0,0 +1,160 @@ +import fetchJwks from '../runtime/fetch_jwks.ts' +import { isCloudflareWorkers } from '../runtime/env.ts' + +import type { KeyLike, JWSHeaderParameters, FlattenedJWSInput } from '../types.d.ts' +import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.ts' + +import { isJWKSLike, LocalJWKSet } from './local.ts' + +/** Options for the remote JSON Web Key Set. */ +export interface RemoteJWKSetOptions { + /** + * Timeout (in milliseconds) for the HTTP request. When reached the request will be aborted and + * the verification will fail. Default is 5000 (5 seconds). + */ + timeoutDuration?: number + + /** + * Duration (in milliseconds) for which no more HTTP requests will be triggered after a previous + * successful fetch. Default is 30000 (30 seconds). + */ + cooldownDuration?: number + + /** + * Maximum time (in milliseconds) between successful HTTP requests. Default is 600000 (10 + * minutes). + */ + cacheMaxAge?: number | typeof Infinity + + /** + * An instance of {@link https://nodejs.org/api/http.html#class-httpagent http.Agent} or + * {@link https://nodejs.org/api/https.html#class-httpsagent https.Agent} to pass to the + * {@link https://nodejs.org/api/http.html#httpgetoptions-callback http.get} or + * {@link https://nodejs.org/api/https.html#httpsgetoptions-callback https.get} method's options. + * Use when behind an http(s) proxy. This is a Node.js runtime specific option, it is ignored when + * used outside of Node.js runtime. + */ + agent?: any + + /** Optional headers to be sent with the HTTP request. */ + headers?: Record +} + +class RemoteJWKSet extends LocalJWKSet { + private _url: URL + + private _timeoutDuration: number + + private _cooldownDuration: number + + private _cacheMaxAge: number + + private _jwksTimestamp?: number + + private _pendingFetch?: Promise + + private _options: Pick + + constructor(url: unknown, options?: RemoteJWKSetOptions) { + super({ keys: [] }) + + this._jwks = undefined + + if (!(url instanceof URL)) { + throw new TypeError('url must be an instance of URL') + } + this._url = new URL(url.href) + this._options = { agent: options?.agent, headers: options?.headers } + this._timeoutDuration = + typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000 + this._cooldownDuration = + typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000 + this._cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000 + } + + coolingDown() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cooldownDuration + : false + } + + fresh() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cacheMaxAge + : false + } + + async getKey(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise { + if (!this._jwks || !this.fresh()) { + await this.reload() + } + + try { + return await super.getKey(protectedHeader, token) + } catch (err) { + if (err instanceof JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload() + return super.getKey(protectedHeader, token) + } + } + throw err + } + } + + async reload() { + // Do not assume a fetch created in another request reliably resolves + // see https://github.com/panva/jose/issues/355 and https://github.com/panva/jose/issues/509 + if (this._pendingFetch && isCloudflareWorkers()) { + this._pendingFetch = undefined + } + + this._pendingFetch ||= fetchJwks(this._url, this._timeoutDuration, this._options) + .then((json) => { + if (!isJWKSLike(json)) { + throw new JWKSInvalid('JSON Web Key Set malformed') + } + + this._jwks = { keys: json.keys } + this._jwksTimestamp = Date.now() + this._pendingFetch = undefined + }) + .catch((err: Error) => { + this._pendingFetch = undefined + throw err + }) + + await this._pendingFetch + } +} + +/** + * Returns a function that resolves to a key object downloaded from a remote endpoint returning a + * JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC jwks_uri. The JSON Web Key Set is + * fetched when no key matches the selection process but only as frequently as the + * `cooldownDuration` option allows to prevent abuse. + * + * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), + * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if + * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key + * Operations) Parameters (if they are present on the JWK). + * + * Only a single public key must match the selection process. As shown in the example below when + * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt + * verification in an iterative manner. + * + * @param url URL to fetch the JSON Web Key Set from. + * @param options Options for the remote JSON Web Key Set. + */ +export function createRemoteJWKSet( + url: URL, + options?: RemoteJWKSetOptions, +) { + const set = new RemoteJWKSet(url, options) + return async function ( + protectedHeader?: JWSHeaderParameters, + token?: FlattenedJWSInput, + ): Promise { + return set.getKey(protectedHeader, token) + } +} diff --git a/dist/deno/jws/compact/sign.ts b/dist/deno/jws/compact/sign.ts new file mode 100644 index 0000000000..15ad33c145 --- /dev/null +++ b/dist/deno/jws/compact/sign.ts @@ -0,0 +1,42 @@ +import { FlattenedSign } from '../flattened/sign.ts' +import type { CompactJWSHeaderParameters, KeyLike, SignOptions } from '../../types.d.ts' + +/** + * The CompactSign class is used to build and sign Compact JWS strings. + * + */ +export class CompactSign { + private _flattened: FlattenedSign + + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array) { + this._flattened = new FlattenedSign(payload) + } + + /** + * Sets the JWS Protected Header on the Sign object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: CompactJWSHeaderParameters) { + this._flattened.setProtectedHeader(protectedHeader) + return this + } + + /** + * Signs and resolves the value of the Compact JWS string. + * + * @param key Private Key or Secret to sign the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + async sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise { + const jws = await this._flattened.sign(key, options) + + if (jws.payload === undefined) { + throw new TypeError('use the flattened module for creating JWS with b64: false') + } + + return `${jws.protected}.${jws.payload}.${jws.signature}` + } +} diff --git a/dist/deno/jws/compact/verify.ts b/dist/deno/jws/compact/verify.ts new file mode 100644 index 0000000000..c40abb05ee --- /dev/null +++ b/dist/deno/jws/compact/verify.ts @@ -0,0 +1,78 @@ +import { flattenedVerify } from '../flattened/verify.ts' +import { JWSInvalid } from '../../util/errors.ts' +import { decoder } from '../../lib/buffer_utils.ts' +import type { + CompactVerifyResult, + FlattenedJWSInput, + GetKeyFunction, + CompactJWSHeaderParameters, + KeyLike, + VerifyOptions, + ResolvedKey, +} from '../../types.d.ts' + +/** + * Interface for Compact JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface CompactVerifyGetKey + extends GetKeyFunction {} + +/** + * Verifies the signature and format of and afterwards decodes the Compact JWS. + * + * @param jws Compact JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function compactVerify( + jws: string | Uint8Array, + key: KeyLike | Uint8Array, + options?: VerifyOptions, +): Promise +/** + * @param jws Compact JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function compactVerify( + jws: string | Uint8Array, + getKey: CompactVerifyGetKey, + options?: VerifyOptions, +): Promise> +export async function compactVerify( + jws: string | Uint8Array, + key: KeyLike | Uint8Array | CompactVerifyGetKey, + options?: VerifyOptions, +) { + if (jws instanceof Uint8Array) { + jws = decoder.decode(jws) + } + + if (typeof jws !== 'string') { + throw new JWSInvalid('Compact JWS must be a string or Uint8Array') + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.') + + if (length !== 3) { + throw new JWSInvalid('Invalid Compact JWS') + } + + const verified = await flattenedVerify( + { payload, protected: protectedHeader, signature }, + [1]>key, + options, + ) + + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader! } + + if (typeof key === 'function') { + return { ...result, key: verified.key } + } + + return result +} diff --git a/dist/deno/jws/flattened/sign.ts b/dist/deno/jws/flattened/sign.ts new file mode 100644 index 0000000000..e84b8a3cda --- /dev/null +++ b/dist/deno/jws/flattened/sign.ts @@ -0,0 +1,142 @@ +import { encode as base64url } from '../../runtime/base64url.ts' +import sign from '../../runtime/sign.ts' + +import isDisjoint from '../../lib/is_disjoint.ts' +import { JWSInvalid } from '../../util/errors.ts' +import { encoder, decoder, concat } from '../../lib/buffer_utils.ts' +import type { KeyLike, FlattenedJWS, JWSHeaderParameters, SignOptions } from '../../types.d.ts' +import checkKeyType from '../../lib/check_key_type.ts' +import validateCrit from '../../lib/validate_crit.ts' + +/** + * The FlattenedSign class is used to build and sign Flattened JWS objects. + * + */ +export class FlattenedSign { + private _payload: Uint8Array + + private _protectedHeader!: JWSHeaderParameters + + private _unprotectedHeader!: JWSHeaderParameters + + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError('payload must be an instance of Uint8Array') + } + this._payload = payload + } + + /** + * Sets the JWS Protected Header on the FlattenedSign object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: JWSHeaderParameters) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once') + } + this._protectedHeader = protectedHeader + return this + } + + /** + * Sets the JWS Unprotected Header on the FlattenedSign object. + * + * @param unprotectedHeader JWS Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once') + } + this._unprotectedHeader = unprotectedHeader + return this + } + + /** + * Signs and resolves the value of the Flattened JWS object. + * + * @param key Private Key or Secret to sign the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + async sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new JWSInvalid( + 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()', + ) + } + + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader)) { + throw new JWSInvalid( + 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', + ) + } + + const joseHeader: JWSHeaderParameters = { + ...this._protectedHeader, + ...this._unprotectedHeader, + } + + const extensions = validateCrit( + JWSInvalid, + new Map([['b64', true]]), + options?.crit, + this._protectedHeader, + joseHeader, + ) + + let b64: boolean = true + if (extensions.has('b64')) { + b64 = this._protectedHeader.b64! + if (typeof b64 !== 'boolean') { + throw new JWSInvalid( + 'The "b64" (base64url-encode payload) Header Parameter must be a boolean', + ) + } + } + + const { alg } = joseHeader + + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') + } + + checkKeyType(alg, key, 'sign') + + let payload = this._payload + if (b64) { + payload = encoder.encode(base64url(payload)) + } + + let protectedHeader: Uint8Array + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))) + } else { + protectedHeader = encoder.encode('') + } + + const data = concat(protectedHeader, encoder.encode('.'), payload) + + const signature = await sign(alg, key, data) + + const jws: FlattenedJWS = { + signature: base64url(signature), + payload: '', + } + + if (b64) { + jws.payload = decoder.decode(payload) + } + + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader + } + + if (this._protectedHeader) { + jws.protected = decoder.decode(protectedHeader) + } + + return jws + } +} diff --git a/dist/deno/jws/flattened/verify.ts b/dist/deno/jws/flattened/verify.ts new file mode 100644 index 0000000000..5a40fb8a28 --- /dev/null +++ b/dist/deno/jws/flattened/verify.ts @@ -0,0 +1,186 @@ +import { decode as base64url } from '../../runtime/base64url.ts' +import verify from '../../runtime/verify.ts' + +import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.ts' +import { concat, encoder, decoder } from '../../lib/buffer_utils.ts' +import isDisjoint from '../../lib/is_disjoint.ts' +import isObject from '../../lib/is_object.ts' +import checkKeyType from '../../lib/check_key_type.ts' +import validateCrit from '../../lib/validate_crit.ts' +import validateAlgorithms from '../../lib/validate_algorithms.ts' + +import type { + FlattenedVerifyResult, + KeyLike, + FlattenedJWSInput, + JWSHeaderParameters, + VerifyOptions, + GetKeyFunction, + ResolvedKey, +} from '../../types.d.ts' + +/** + * Interface for Flattened JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface FlattenedVerifyGetKey + extends GetKeyFunction {} + +/** + * Verifies the signature and format of and afterwards decodes the Flattened JWS. + * + * @param jws Flattened JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function flattenedVerify( + jws: FlattenedJWSInput, + key: KeyLike | Uint8Array, + options?: VerifyOptions, +): Promise +/** + * @param jws Flattened JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function flattenedVerify( + jws: FlattenedJWSInput, + getKey: FlattenedVerifyGetKey, + options?: VerifyOptions, +): Promise> +export async function flattenedVerify( + jws: FlattenedJWSInput, + key: KeyLike | Uint8Array | FlattenedVerifyGetKey, + options?: VerifyOptions, +) { + if (!isObject(jws)) { + throw new JWSInvalid('Flattened JWS must be an object') + } + + if (jws.protected === undefined && jws.header === undefined) { + throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members') + } + + if (jws.protected !== undefined && typeof jws.protected !== 'string') { + throw new JWSInvalid('JWS Protected Header incorrect type') + } + + if (jws.payload === undefined) { + throw new JWSInvalid('JWS Payload missing') + } + + if (typeof jws.signature !== 'string') { + throw new JWSInvalid('JWS Signature missing or incorrect type') + } + + if (jws.header !== undefined && !isObject(jws.header)) { + throw new JWSInvalid('JWS Unprotected Header incorrect type') + } + + let parsedProt: JWSHeaderParameters = {} + if (jws.protected) { + try { + const protectedHeader = base64url(jws.protected) + parsedProt = JSON.parse(decoder.decode(protectedHeader)) + } catch { + throw new JWSInvalid('JWS Protected Header is invalid') + } + } + if (!isDisjoint(parsedProt, jws.header)) { + throw new JWSInvalid( + 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', + ) + } + + const joseHeader: JWSHeaderParameters = { + ...parsedProt, + ...jws.header, + } + + const extensions = validateCrit( + JWSInvalid, + new Map([['b64', true]]), + options?.crit, + parsedProt, + joseHeader, + ) + + let b64: boolean = true + if (extensions.has('b64')) { + b64 = parsedProt.b64! + if (typeof b64 !== 'boolean') { + throw new JWSInvalid( + 'The "b64" (base64url-encode payload) Header Parameter must be a boolean', + ) + } + } + + const { alg } = joseHeader + + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') + } + + const algorithms = options && validateAlgorithms('algorithms', options.algorithms) + + if (algorithms && !algorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed') + } + + if (b64) { + if (typeof jws.payload !== 'string') { + throw new JWSInvalid('JWS Payload must be a string') + } + } else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) { + throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance') + } + + let resolvedKey = false + if (typeof key === 'function') { + key = await key(parsedProt, jws) + resolvedKey = true + } + + checkKeyType(alg, key, 'verify') + + const data = concat( + encoder.encode(jws.protected ?? ''), + encoder.encode('.'), + typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload, + ) + const signature = base64url(jws.signature) + const verified = await verify(alg, key, signature, data) + + if (!verified) { + throw new JWSSignatureVerificationFailed() + } + + let payload: Uint8Array + if (b64) { + payload = base64url(jws.payload) + } else if (typeof jws.payload === 'string') { + payload = encoder.encode(jws.payload) + } else { + payload = jws.payload + } + + const result: FlattenedVerifyResult = { payload } + + if (jws.protected !== undefined) { + result.protectedHeader = parsedProt + } + + if (jws.header !== undefined) { + result.unprotectedHeader = jws.header + } + + if (resolvedKey) { + return { ...result, key } + } + + return result +} diff --git a/dist/deno/jws/general/sign.ts b/dist/deno/jws/general/sign.ts new file mode 100644 index 0000000000..c47d8b78d1 --- /dev/null +++ b/dist/deno/jws/general/sign.ts @@ -0,0 +1,130 @@ +import { FlattenedSign } from '../flattened/sign.ts' +import { JWSInvalid } from '../../util/errors.ts' + +import type { KeyLike, GeneralJWS, JWSHeaderParameters, SignOptions } from '../../types.d.ts' + +export interface Signature { + /** + * Sets the JWS Protected Header on the Signature object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: JWSHeaderParameters): Signature + + /** + * Sets the JWS Unprotected Header on the Signature object. + * + * @param unprotectedHeader JWS Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters): Signature + + /** A shorthand for calling addSignature() on the enclosing GeneralSign instance */ + addSignature(...args: Parameters): Signature + + /** A shorthand for calling encrypt() on the enclosing GeneralSign instance */ + sign(...args: Parameters): Promise + + /** Returns the enclosing GeneralSign */ + done(): GeneralSign +} + +class IndividualSignature implements Signature { + private parent: GeneralSign + + protectedHeader?: JWSHeaderParameters + unprotectedHeader?: JWSHeaderParameters + options?: SignOptions + key: KeyLike | Uint8Array + + constructor(sig: GeneralSign, key: KeyLike | Uint8Array, options?: SignOptions) { + this.parent = sig + this.key = key + this.options = options + } + + setProtectedHeader(protectedHeader: JWSHeaderParameters) { + if (this.protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once') + } + this.protectedHeader = protectedHeader + return this + } + + setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once') + } + this.unprotectedHeader = unprotectedHeader + return this + } + + addSignature(...args: Parameters) { + return this.parent.addSignature(...args) + } + + sign(...args: Parameters) { + return this.parent.sign(...args) + } + + done() { + return this.parent + } +} + +/** + * The GeneralSign class is used to build and sign General JWS objects. + * + */ +export class GeneralSign { + private _payload: Uint8Array + + private _signatures: IndividualSignature[] = [] + + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array) { + this._payload = payload + } + + /** + * Adds an additional signature for the General JWS object. + * + * @param key Private Key or Secret to sign the individual JWS signature with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + addSignature(key: KeyLike | Uint8Array, options?: SignOptions): Signature { + const signature = new IndividualSignature(this, key, options) + this._signatures.push(signature) + return signature + } + + /** Signs and resolves the value of the General JWS object. */ + async sign(): Promise { + if (!this._signatures.length) { + throw new JWSInvalid('at least one signature must be added') + } + + const jws: GeneralJWS = { + signatures: [], + payload: '', + } + + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i] + const flattened = new FlattenedSign(this._payload) + + flattened.setProtectedHeader(signature.protectedHeader!) + flattened.setUnprotectedHeader(signature.unprotectedHeader!) + + const { payload, ...rest } = await flattened.sign(signature.key, signature.options) + if (i === 0) { + jws.payload = payload + } else if (jws.payload !== payload) { + throw new JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)') + } + jws.signatures.push(rest) + } + + return jws + } +} diff --git a/dist/deno/jws/general/verify.ts b/dist/deno/jws/general/verify.ts new file mode 100644 index 0000000000..26695046f7 --- /dev/null +++ b/dist/deno/jws/general/verify.ts @@ -0,0 +1,78 @@ +import { flattenedVerify } from '../flattened/verify.ts' +import type { + GeneralJWSInput, + GeneralVerifyResult, + FlattenedJWSInput, + GetKeyFunction, + JWSHeaderParameters, + KeyLike, + VerifyOptions, + ResolvedKey, +} from '../../types.d.ts' +import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.ts' +import isObject from '../../lib/is_object.ts' + +/** + * Interface for General JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface GeneralVerifyGetKey + extends GetKeyFunction {} + +/** + * Verifies the signature and format of and afterwards decodes the General JWS. + * + * @param jws General JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function generalVerify( + jws: GeneralJWSInput, + key: KeyLike | Uint8Array, + options?: VerifyOptions, +): Promise +/** + * @param jws General JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export function generalVerify( + jws: GeneralJWSInput, + getKey: GeneralVerifyGetKey, + options?: VerifyOptions, +): Promise> +export async function generalVerify( + jws: GeneralJWSInput, + key: KeyLike | Uint8Array | GeneralVerifyGetKey, + options?: VerifyOptions, +) { + if (!isObject(jws)) { + throw new JWSInvalid('General JWS must be an object') + } + + if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { + throw new JWSInvalid('JWS Signatures missing or incorrect type') + } + + for (const signature of jws.signatures) { + try { + return await flattenedVerify( + { + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature, + }, + [1]>key, + options, + ) + } catch { + // + } + } + throw new JWSSignatureVerificationFailed() +} diff --git a/dist/deno/jwt/decrypt.ts b/dist/deno/jwt/decrypt.ts new file mode 100644 index 0000000000..8acce1690d --- /dev/null +++ b/dist/deno/jwt/decrypt.ts @@ -0,0 +1,94 @@ +import { compactDecrypt } from '../jwe/compact/decrypt.ts' +import type { + KeyLike, + DecryptOptions, + JWTClaimVerificationOptions, + GetKeyFunction, + CompactJWEHeaderParameters, + FlattenedJWE, + JWTDecryptResult, + ResolvedKey, +} from '../types.d.ts' +import jwtPayload from '../lib/jwt_claims_set.ts' +import { JWTClaimValidationFailed } from '../util/errors.ts' + +/** Combination of JWE Decryption options and JWT Claims Set verification options. */ +export interface JWTDecryptOptions extends DecryptOptions, JWTClaimVerificationOptions {} + +/** + * Interface for JWT Decryption dynamic key resolution. No token components have been verified at + * the time of this function call. + */ +export interface JWTDecryptGetKey + extends GetKeyFunction {} + +/** + * Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT + * Claims Set. + * + * @param jwt JSON Web Token value (encoded as JWE). + * @param key Private Key or Secret to decrypt and verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export async function jwtDecrypt( + jwt: string | Uint8Array, + key: KeyLike | Uint8Array, + options?: JWTDecryptOptions, +): Promise +/** + * @param jwt JSON Web Token value (encoded as JWE). + * @param getKey Function resolving Private Key or Secret to decrypt and verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export async function jwtDecrypt( + jwt: string | Uint8Array, + getKey: JWTDecryptGetKey, + options?: JWTDecryptOptions, +): Promise> +export async function jwtDecrypt( + jwt: string | Uint8Array, + key: KeyLike | Uint8Array | JWTDecryptGetKey, + options?: JWTDecryptOptions, +) { + const decrypted = await compactDecrypt(jwt, [1]>key, options) + const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options) + + const { protectedHeader } = decrypted + + if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { + throw new JWTClaimValidationFailed( + 'replicated "iss" claim header parameter mismatch', + 'iss', + 'mismatch', + ) + } + + if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { + throw new JWTClaimValidationFailed( + 'replicated "sub" claim header parameter mismatch', + 'sub', + 'mismatch', + ) + } + + if ( + protectedHeader.aud !== undefined && + JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud) + ) { + throw new JWTClaimValidationFailed( + 'replicated "aud" claim header parameter mismatch', + 'aud', + 'mismatch', + ) + } + + const result = { payload, protectedHeader } + + if (typeof key === 'function') { + return { ...result, key: decrypted.key } + } + + return result +} diff --git a/dist/deno/jwt/encrypt.ts b/dist/deno/jwt/encrypt.ts new file mode 100644 index 0000000000..d4efdef580 --- /dev/null +++ b/dist/deno/jwt/encrypt.ts @@ -0,0 +1,152 @@ +import { CompactEncrypt } from '../jwe/compact/encrypt.ts' +import type { + EncryptOptions, + CompactJWEHeaderParameters, + JWEKeyManagementHeaderParameters, + KeyLike, +} from '../types.d.ts' +import { encoder } from '../lib/buffer_utils.ts' +import { ProduceJWT } from './produce.ts' + +/** + * The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. + * + */ +export class EncryptJWT extends ProduceJWT { + private _cek!: Uint8Array + + private _iv!: Uint8Array + + private _keyManagementParameters!: JWEKeyManagementHeaderParameters + + private _protectedHeader!: CompactJWEHeaderParameters + + private _replicateIssuerAsHeader!: boolean + + private _replicateSubjectAsHeader!: boolean + + private _replicateAudienceAsHeader!: boolean + + /** + * Sets the JWE Protected Header on the EncryptJWT object. + * + * @param protectedHeader JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc" + * (JWE Encryption Algorithm) properties. + */ + setProtectedHeader(protectedHeader: CompactJWEHeaderParameters) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once') + } + this._protectedHeader = protectedHeader + return this + } + + /** + * Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is + * really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or + * Agreement PartyVInfo parameters. Other parameters will always be randomly generated when needed + * and missing. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once') + } + this._keyManagementParameters = parameters + return this + } + + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once') + } + this._cek = cek + return this + } + + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once') + } + this._iv = iv + return this + } + + /** + * Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true + return this + } + + /** + * Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true + return this + } + + /** + * Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true + return this + } + + /** + * Encrypts and returns the JWT. + * + * @param key Public Key or Secret to encrypt the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + async encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions): Promise { + const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload))) + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss } + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub } + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud } + } + enc.setProtectedHeader(this._protectedHeader) + if (this._iv) { + enc.setInitializationVector(this._iv) + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek) + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters) + } + return enc.encrypt(key, options) + } +} diff --git a/dist/deno/jwt/produce.ts b/dist/deno/jwt/produce.ts new file mode 100644 index 0000000000..5fd7defddf --- /dev/null +++ b/dist/deno/jwt/produce.ts @@ -0,0 +1,104 @@ +import type { JWTPayload } from '../types.d.ts' +import epoch from '../lib/epoch.ts' +import isObject from '../lib/is_object.ts' +import secs from '../lib/secs.ts' + +/** Generic class for JWT producing. */ +export class ProduceJWT { + protected _payload!: JWTPayload + + /** @param payload The JWT Claims Set object. */ + constructor(payload: JWTPayload) { + if (!isObject(payload)) { + throw new TypeError('JWT Claims Set MUST be an object') + } + this._payload = payload + } + + /** + * Set "iss" (Issuer) Claim. + * + * @param issuer "Issuer" Claim value to set on the JWT Claims Set. + */ + setIssuer(issuer: string) { + this._payload = { ...this._payload, iss: issuer } + return this + } + + /** + * Set "sub" (Subject) Claim. + * + * @param subject "sub" (Subject) Claim value to set on the JWT Claims Set. + */ + setSubject(subject: string) { + this._payload = { ...this._payload, sub: subject } + return this + } + + /** + * Set "aud" (Audience) Claim. + * + * @param audience "aud" (Audience) Claim value to set on the JWT Claims Set. + */ + setAudience(audience: string | string[]) { + this._payload = { ...this._payload, aud: audience } + return this + } + + /** + * Set "jti" (JWT ID) Claim. + * + * @param jwtId "jti" (JWT ID) Claim value to set on the JWT Claims Set. + */ + setJti(jwtId: string) { + this._payload = { ...this._payload, jti: jwtId } + return this + } + + /** + * Set "nbf" (Not Before) Claim. + * + * @param input "nbf" (Not Before) Claim value to set on the JWT Claims Set. When number is passed + * that is used as a value, when string is passed it is resolved to a time span and added to the + * current timestamp. + */ + setNotBefore(input: number | string) { + if (typeof input === 'number') { + this._payload = { ...this._payload, nbf: input } + } else { + this._payload = { ...this._payload, nbf: epoch(new Date()) + secs(input) } + } + return this + } + + /** + * Set "exp" (Expiration Time) Claim. + * + * @param input "exp" (Expiration Time) Claim value to set on the JWT Claims Set. When number is + * passed that is used as a value, when string is passed it is resolved to a time span and added + * to the current timestamp. + */ + setExpirationTime(input: number | string) { + if (typeof input === 'number') { + this._payload = { ...this._payload, exp: input } + } else { + this._payload = { ...this._payload, exp: epoch(new Date()) + secs(input) } + } + return this + } + + /** + * Set "iat" (Issued At) Claim. + * + * @param input "iat" (Issued At) Claim value to set on the JWT Claims Set. Default is current + * timestamp. + */ + setIssuedAt(input?: number) { + if (typeof input === 'undefined') { + this._payload = { ...this._payload, iat: epoch(new Date()) } + } else { + this._payload = { ...this._payload, iat: input } + } + return this + } +} diff --git a/dist/deno/jwt/sign.ts b/dist/deno/jwt/sign.ts new file mode 100644 index 0000000000..a9ed77571f --- /dev/null +++ b/dist/deno/jwt/sign.ts @@ -0,0 +1,44 @@ +import { CompactSign } from '../jws/compact/sign.ts' +import { JWTInvalid } from '../util/errors.ts' +import type { JWTHeaderParameters, KeyLike, SignOptions } from '../types.d.ts' +import { encoder } from '../lib/buffer_utils.ts' +import { ProduceJWT } from './produce.ts' + +/** + * The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. + * + */ +export class SignJWT extends ProduceJWT { + private _protectedHeader!: JWTHeaderParameters + + /** + * Sets the JWS Protected Header on the SignJWT object. + * + * @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property. + */ + setProtectedHeader(protectedHeader: JWTHeaderParameters) { + this._protectedHeader = protectedHeader + return this + } + + /** + * Signs and returns the JWT. + * + * @param key Private Key or Secret to sign the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Sign options. + */ + async sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise { + const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload))) + sig.setProtectedHeader(this._protectedHeader) + if ( + Array.isArray(this._protectedHeader?.crit) && + this._protectedHeader.crit.includes('b64') && + // @ts-expect-error + this._protectedHeader.b64 === false + ) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload') + } + return sig.sign(key, options) + } +} diff --git a/dist/deno/jwt/unsecured.ts b/dist/deno/jwt/unsecured.ts new file mode 100644 index 0000000000..c616d20d6c --- /dev/null +++ b/dist/deno/jwt/unsecured.ts @@ -0,0 +1,55 @@ +import * as base64url from '../runtime/base64url.ts' + +import type { JWSHeaderParameters, JWTClaimVerificationOptions, JWTPayload } from '../types.d.ts' +import { decoder } from '../lib/buffer_utils.ts' +import { JWTInvalid } from '../util/errors.ts' +import jwtPayload from '../lib/jwt_claims_set.ts' +import { ProduceJWT } from './produce.ts' + +export interface UnsecuredResult { + payload: JWTPayload + header: JWSHeaderParameters +} + +/** + * The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. + * + */ +export class UnsecuredJWT extends ProduceJWT { + /** Encodes the Unsecured JWT. */ + encode(): string { + const header = base64url.encode(JSON.stringify({ alg: 'none' })) + const payload = base64url.encode(JSON.stringify(this._payload)) + + return `${header}.${payload}.` + } + + /** + * Decodes an unsecured JWT. + * + * @param jwt Unsecured JWT to decode the payload of. + * @param options JWT Claims Set validation options. + */ + static decode(jwt: string, options?: JWTClaimVerificationOptions): UnsecuredResult { + if (typeof jwt !== 'string') { + throw new JWTInvalid('Unsecured JWT must be a string') + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.') + + if (length !== 3 || signature !== '') { + throw new JWTInvalid('Invalid Unsecured JWT') + } + + let header: JWSHeaderParameters + try { + header = JSON.parse(decoder.decode(base64url.decode(encodedHeader))) + if (header.alg !== 'none') throw new Error() + } catch { + throw new JWTInvalid('Invalid Unsecured JWT') + } + + const payload = jwtPayload(header, base64url.decode(encodedPayload), options) + + return { payload, header } + } +} diff --git a/dist/deno/jwt/verify.ts b/dist/deno/jwt/verify.ts new file mode 100644 index 0000000000..0a565b64c2 --- /dev/null +++ b/dist/deno/jwt/verify.ts @@ -0,0 +1,68 @@ +import { compactVerify } from '../jws/compact/verify.ts' +import type { + KeyLike, + VerifyOptions, + JWTClaimVerificationOptions, + JWTHeaderParameters, + GetKeyFunction, + FlattenedJWSInput, + JWTVerifyResult, + ResolvedKey, +} from '../types.d.ts' +import jwtPayload from '../lib/jwt_claims_set.ts' +import { JWTInvalid } from '../util/errors.ts' + +/** Combination of JWS Verification options and JWT Claims Set verification options. */ +export interface JWTVerifyOptions extends VerifyOptions, JWTClaimVerificationOptions {} + +/** + * Interface for JWT Verification dynamic key resolution. No token components have been verified at + * the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface JWTVerifyGetKey extends GetKeyFunction {} + +/** + * Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the + * JWT Claims Set. + * + * @param jwt JSON Web Token value (encoded as JWS). + * @param key Key to verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export async function jwtVerify( + jwt: string | Uint8Array, + key: KeyLike | Uint8Array, + options?: JWTVerifyOptions, +): Promise + +/** + * @param jwt JSON Web Token value (encoded as JWS). + * @param getKey Function resolving a key to verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export async function jwtVerify( + jwt: string | Uint8Array, + getKey: JWTVerifyGetKey, + options?: JWTVerifyOptions, +): Promise> + +export async function jwtVerify( + jwt: string | Uint8Array, + key: KeyLike | Uint8Array | JWTVerifyGetKey, + options?: JWTVerifyOptions, +) { + const verified = await compactVerify(jwt, [1]>key, options) + if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload') + } + const payload = jwtPayload(verified.protectedHeader, verified.payload, options) + const result = { payload, protectedHeader: verified.protectedHeader } + if (typeof key === 'function') { + return { ...result, key: verified.key } + } + return result +} diff --git a/dist/deno/key/export.ts b/dist/deno/key/export.ts new file mode 100644 index 0000000000..072c0c7161 --- /dev/null +++ b/dist/deno/key/export.ts @@ -0,0 +1,34 @@ +import { toSPKI as exportPublic } from '../runtime/asn1.ts' +import { toPKCS8 as exportPrivate } from '../runtime/asn1.ts' +import keyToJWK from '../runtime/key_to_jwk.ts' + +import type { JWK, KeyLike } from '../types.d.ts' + +/** + * Exports a runtime-specific public key representation (KeyObject or CryptoKey) to a PEM-encoded + * SPKI string format. + * + * @param key Key representation to transform to a PEM-encoded SPKI string format. + */ +export async function exportSPKI(key: KeyLike): Promise { + return exportPublic(key) +} + +/** + * Exports a runtime-specific private key representation (KeyObject or CryptoKey) to a PEM-encoded + * PKCS8 string format. + * + * @param key Key representation to transform to a PEM-encoded PKCS8 string format. + */ +export async function exportPKCS8(key: KeyLike): Promise { + return exportPrivate(key) +} + +/** + * Exports a runtime-specific key representation (KeyLike) to a JWK. + * + * @param key Key representation to export as JWK. + */ +export async function exportJWK(key: KeyLike | Uint8Array): Promise { + return keyToJWK(key) +} diff --git a/dist/deno/key/generate_key_pair.ts b/dist/deno/key/generate_key_pair.ts new file mode 100644 index 0000000000..2bd21d7edb --- /dev/null +++ b/dist/deno/key/generate_key_pair.ts @@ -0,0 +1,50 @@ +import { generateKeyPair as generate } from '../runtime/generate.ts' + +import type { KeyLike } from '../types.d.ts' + +export interface GenerateKeyPairResult { + /** The generated Private Key. */ + privateKey: T + + /** Public Key corresponding to the generated Private Key. */ + publicKey: T +} + +export interface GenerateKeyPairOptions { + /** + * The EC "crv" (Curve) or OKP "crv" (Subtype of Key Pair) value to generate. The curve must be + * both supported on the runtime as well as applicable for the given JWA algorithm identifier. + */ + crv?: string + + /** + * A hint for RSA algorithms to generate an RSA key of a given `modulusLength` (Key size in bits). + * JOSE requires 2048 bits or larger. Default is 2048. + */ + modulusLength?: number + + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey SubtleCrypto.generateKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean +} + +/** + * Generates a private and a public key for a given JWA algorithm identifier. This can only generate + * asymmetric key pairs. For symmetric secrets use the `generateSecret` function. + * + * Note: Under Web Crypto API runtime the `privateKey` is generated with `extractable` set to + * `false` by default. + * + * @param alg JWA Algorithm Identifier to be used with the generated key pair. + * @param options Additional options passed down to the key pair generation. + */ +export async function generateKeyPair( + alg: string, + options?: GenerateKeyPairOptions, +): Promise> { + // @ts-ignore + return generate(alg, options) +} diff --git a/dist/deno/key/generate_secret.ts b/dist/deno/key/generate_secret.ts new file mode 100644 index 0000000000..8154152d50 --- /dev/null +++ b/dist/deno/key/generate_secret.ts @@ -0,0 +1,29 @@ +import { generateSecret as generate } from '../runtime/generate.ts' + +import type { KeyLike } from '../types.d.ts' + +export interface GenerateSecretOptions { + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey SubtleCrypto.generateKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean +} + +/** + * Generates a symmetric secret key for a given JWA algorithm identifier. + * + * Note: Under Web Crypto API runtime the secret key is generated with `extractable` set to `false` + * by default. + * + * @param alg JWA Algorithm Identifier to be used with the generated secret. + * @param options Additional options passed down to the secret generation. + */ +export async function generateSecret( + alg: string, + options?: GenerateSecretOptions, +): Promise { + // @ts-ignore + return generate(alg, options) +} diff --git a/dist/deno/key/import.ts b/dist/deno/key/import.ts new file mode 100644 index 0000000000..1ccb170757 --- /dev/null +++ b/dist/deno/key/import.ts @@ -0,0 +1,133 @@ +import { decode as decodeBase64URL } from '../runtime/base64url.ts' +import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.ts' +import asKeyObject from '../runtime/jwk_to_key.ts' + +import { JOSENotSupported } from '../util/errors.ts' +import isObject from '../lib/is_object.ts' +import type { JWK, KeyLike } from '../types.d.ts' + +export interface PEMImportOptions { + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey SubtleCrypto.importKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean +} + +/** + * Imports a PEM-encoded SPKI string as a runtime-specific public key representation (KeyObject or + * CryptoKey). + * + * @param pem PEM-encoded SPKI string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export async function importSPKI( + spki: string, + alg: string, + options?: PEMImportOptions, +): Promise { + if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) { + throw new TypeError('"spki" must be SPKI formatted string') + } + // @ts-ignore + return fromSPKI(spki, alg, options) +} + +/** + * Imports the SPKI from an X.509 string certificate as a runtime-specific public key representation + * (KeyObject or CryptoKey). + * + * @param pem X.509 certificate string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export async function importX509( + x509: string, + alg: string, + options?: PEMImportOptions, +): Promise { + if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) { + throw new TypeError('"x509" must be X.509 formatted string') + } + // @ts-ignore + return fromX509(x509, alg, options) +} + +/** + * Imports a PEM-encoded PKCS#8 string as a runtime-specific private key representation (KeyObject + * or CryptoKey). + * + * @param pem PEM-encoded PKCS#8 string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export async function importPKCS8( + pkcs8: string, + alg: string, + options?: PEMImportOptions, +): Promise { + if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string') + } + // @ts-ignore + return fromPKCS8(pkcs8, alg, options) +} + +/** + * Imports a JWK to a runtime-specific key representation (KeyLike). Either JWK "alg" (Algorithm) + * Parameter must be present or the optional "alg" argument. When running on a runtime using + * {@link https://www.w3.org/TR/WebCryptoAPI/ Web Cryptography API} the jwk parameters "use", + * "key_ops", and "ext" are also used in the resulting `CryptoKey`. + * + * @param jwk JSON Web Key. + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key. Default is the "alg" property on the JWK, its presence is only enforced + * in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + * @param octAsKeyObject Forces a symmetric key to be imported to a KeyObject or CryptoKey. Default + * is true unless JWK "ext" (Extractable) is true. + */ +export async function importJWK( + jwk: JWK, + alg?: string, + octAsKeyObject?: boolean, +): Promise { + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object') + } + + alg ||= jwk.alg + + switch (jwk.kty) { + case 'oct': + if (typeof jwk.k !== 'string' || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value') + } + + octAsKeyObject ??= jwk.ext !== true + + if (octAsKeyObject) { + // @ts-ignore + return asKeyObject({ ...jwk, alg, ext: jwk.ext ?? false }) + } + + return decodeBase64URL(jwk.k) + case 'RSA': + if (jwk.oth !== undefined) { + throw new JOSENotSupported( + 'RSA JWK "oth" (Other Primes Info) Parameter value is not supported', + ) + } + case 'EC': + case 'OKP': + // @ts-ignore + return asKeyObject({ ...jwk, alg }) + default: + throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value') + } +} diff --git a/dist/deno/lib/aesgcmkw.ts b/dist/deno/lib/aesgcmkw.ts new file mode 100644 index 0000000000..87d7013728 --- /dev/null +++ b/dist/deno/lib/aesgcmkw.ts @@ -0,0 +1,30 @@ +import encrypt from '../runtime/encrypt.ts' +import decrypt from '../runtime/decrypt.ts' +import generateIv from './iv.ts' +import { encode as base64url } from '../runtime/base64url.ts' + +export async function wrap(alg: string, key: unknown, cek: Uint8Array, iv?: Uint8Array) { + const jweAlgorithm = alg.slice(0, 7) + iv ||= generateIv(jweAlgorithm) + + const { ciphertext: encryptedKey, tag } = await encrypt( + jweAlgorithm, + cek, + key, + iv, + new Uint8Array(0), + ) + + return { encryptedKey, iv: base64url(iv), tag: base64url(tag) } +} + +export async function unwrap( + alg: string, + key: unknown, + encryptedKey: Uint8Array, + iv: Uint8Array, + tag: Uint8Array, +) { + const jweAlgorithm = alg.slice(0, 7) + return decrypt(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)) +} diff --git a/dist/deno/lib/buffer_utils.ts b/dist/deno/lib/buffer_utils.ts new file mode 100644 index 0000000000..2c910c61cb --- /dev/null +++ b/dist/deno/lib/buffer_utils.ts @@ -0,0 +1,60 @@ +import digest from '../runtime/digest.ts' + +export const encoder = new TextEncoder() +export const decoder = new TextDecoder() + +const MAX_INT32 = 2 ** 32 + +export function concat(...buffers: Uint8Array[]): Uint8Array { + const size = buffers.reduce((acc, { length }) => acc + length, 0) + const buf = new Uint8Array(size) + let i = 0 + buffers.forEach((buffer) => { + buf.set(buffer, i) + i += buffer.length + }) + return buf +} + +export function p2s(alg: string, p2sInput: Uint8Array) { + return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput) +} + +function writeUInt32BE(buf: Uint8Array, value: number, offset?: number) { + 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 & 0xff], offset) +} + +export function uint64be(value: number) { + 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 +} + +export function uint32be(value: number) { + const buf = new Uint8Array(4) + writeUInt32BE(buf, value) + return buf +} + +export function lengthAndInput(input: Uint8Array) { + return concat(uint32be(input.length), input) +} + +export async function concatKdf(secret: Uint8Array, bits: number, value: Uint8Array) { + const iterations = Math.ceil((bits >> 3) / 32) + const res = new Uint8Array(iterations * 32) + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length) + buf.set(uint32be(iter + 1)) + buf.set(secret, 4) + buf.set(value, 4 + secret.length) + res.set(await digest('sha256', buf), iter * 32) + } + return res.slice(0, bits >> 3) +} diff --git a/dist/deno/lib/cek.ts b/dist/deno/lib/cek.ts new file mode 100644 index 0000000000..22cb0bb1a7 --- /dev/null +++ b/dist/deno/lib/cek.ts @@ -0,0 +1,21 @@ +import { JOSENotSupported } from '../util/errors.ts' +import random from '../runtime/random.ts' + +export function bitLength(alg: string) { + switch (alg) { + case 'A128GCM': + return 128 + case 'A192GCM': + return 192 + case 'A256GCM': + case 'A128CBC-HS256': + return 256 + case 'A192CBC-HS384': + return 384 + case 'A256CBC-HS512': + return 512 + default: + throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`) + } +} +export default (alg: string): Uint8Array => random(new Uint8Array(bitLength(alg) >> 3)) diff --git a/dist/deno/lib/check_iv_length.ts b/dist/deno/lib/check_iv_length.ts new file mode 100644 index 0000000000..d63a2b280a --- /dev/null +++ b/dist/deno/lib/check_iv_length.ts @@ -0,0 +1,10 @@ +import { JWEInvalid } from '../util/errors.ts' +import { bitLength } from './iv.ts' + +const checkIvLength = (enc: string, iv: Uint8Array) => { + if (iv.length << 3 !== bitLength(enc)) { + throw new JWEInvalid('Invalid Initialization Vector length') + } +} + +export default checkIvLength diff --git a/dist/deno/lib/check_key_type.ts b/dist/deno/lib/check_key_type.ts new file mode 100644 index 0000000000..cc85e608dc --- /dev/null +++ b/dist/deno/lib/check_key_type.ts @@ -0,0 +1,74 @@ +import { withAlg as invalidKeyInput } from './invalid_key_input.ts' +import isKeyLike, { types } from '../runtime/is_key_like.ts' + +const symmetricTypeCheck = (alg: string, key: unknown) => { + if (key instanceof Uint8Array) return + + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array')) + } + + if (key.type !== 'secret') { + throw new TypeError( + `${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`, + ) + } +} + +const asymmetricTypeCheck = (alg: string, key: unknown, usage: string) => { + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types)) + } + + if (key.type === 'secret') { + throw new TypeError( + `${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`, + ) + } + + if (usage === 'sign' && key.type === 'public') { + throw new TypeError( + `${types.join(' or ')} instances for asymmetric algorithm signing must be of type "private"`, + ) + } + + if (usage === 'decrypt' && key.type === 'public') { + throw new TypeError( + `${types.join( + ' or ', + )} instances for asymmetric algorithm decryption must be of type "private"`, + ) + } + + // KeyObject allows this but CryptoKey does not. + if ((key).algorithm && usage === 'verify' && key.type === 'private') { + throw new TypeError( + `${types.join(' or ')} instances for asymmetric algorithm verifying must be of type "public"`, + ) + } + + // KeyObject allows this but CryptoKey does not. + if ((key).algorithm && usage === 'encrypt' && key.type === 'private') { + throw new TypeError( + `${types.join( + ' or ', + )} instances for asymmetric algorithm encryption must be of type "public"`, + ) + } +} + +const checkKeyType = (alg: string, key: unknown, usage: string): void => { + const symmetric = + alg.startsWith('HS') || + alg === 'dir' || + alg.startsWith('PBES2') || + /^A\d{3}(?:GCM)?KW$/.test(alg) + + if (symmetric) { + symmetricTypeCheck(alg, key) + } else { + asymmetricTypeCheck(alg, key, usage) + } +} + +export default checkKeyType diff --git a/dist/deno/lib/check_p2s.ts b/dist/deno/lib/check_p2s.ts new file mode 100644 index 0000000000..53c6d9c85f --- /dev/null +++ b/dist/deno/lib/check_p2s.ts @@ -0,0 +1,7 @@ +import { JWEInvalid } from '../util/errors.ts' + +export default function checkP2s(p2s: Uint8Array) { + if (!(p2s instanceof Uint8Array) || p2s.length < 8) { + throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets') + } +} diff --git a/dist/deno/lib/crypto_key.ts b/dist/deno/lib/crypto_key.ts new file mode 100644 index 0000000000..2c9eb3fdae --- /dev/null +++ b/dist/deno/lib/crypto_key.ts @@ -0,0 +1,151 @@ +import { isCloudflareWorkers } from '../runtime/env.ts' + +function unusable(name: string | number, prop = 'algorithm.name') { + return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`) +} + +function isAlgorithm(algorithm: any, name: string): algorithm is T { + return algorithm.name === name +} + +function getHashLength(hash: KeyAlgorithm) { + return parseInt(hash.name.slice(4), 10) +} + +function getNamedCurve(alg: string) { + switch (alg) { + case 'ES256': + return 'P-256' + case 'ES384': + return 'P-384' + case 'ES512': + return 'P-521' + default: + throw new Error('unreachable') + } +} + +function checkUsage(key: CryptoKey, usages: KeyUsage[]) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = 'CryptoKey does not support this operation, its usages must include ' + if (usages.length > 2) { + const last = usages.pop() + msg += `one of ${usages.join(', ')}, or ${last}.` + } else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.` + } else { + msg += `${usages[0]}.` + } + + throw new TypeError(msg) + } +} + +export function checkSigCryptoKey(key: CryptoKey, alg: string, ...usages: KeyUsage[]) { + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': { + if (!isAlgorithm(key.algorithm, 'HMAC')) throw unusable('HMAC') + const expected = parseInt(alg.slice(2), 10) + const actual = getHashLength(key.algorithm.hash) + if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash') + break + } + case 'RS256': + case 'RS384': + case 'RS512': { + if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5')) + throw unusable('RSASSA-PKCS1-v1_5') + const expected = parseInt(alg.slice(2), 10) + const actual = getHashLength(key.algorithm.hash) + if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash') + break + } + case 'PS256': + case 'PS384': + case 'PS512': { + if (!isAlgorithm(key.algorithm, 'RSA-PSS')) throw unusable('RSA-PSS') + const expected = parseInt(alg.slice(2), 10) + const actual = getHashLength(key.algorithm.hash) + if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash') + break + } + case 'EdDSA': { + if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') { + if (isCloudflareWorkers()) { + if (isAlgorithm(key.algorithm, 'NODE-ED25519')) break + throw unusable('Ed25519, Ed448, or NODE-ED25519') + } + throw unusable('Ed25519 or Ed448') + } + break + } + case 'ES256': + case 'ES384': + case 'ES512': { + if (!isAlgorithm(key.algorithm, 'ECDSA')) throw unusable('ECDSA') + const expected = getNamedCurve(alg) + const actual = key.algorithm.namedCurve + if (actual !== expected) throw unusable(expected, 'algorithm.namedCurve') + break + } + default: + throw new TypeError('CryptoKey does not support this operation') + } + + checkUsage(key, usages) +} + +export function checkEncCryptoKey(key: CryptoKey, alg: string, ...usages: KeyUsage[]) { + 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': + case 'X448': + break + default: + throw unusable('ECDH, X25519, or X448') + } + 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, usages) +} diff --git a/dist/deno/lib/decrypt_key_management.ts b/dist/deno/lib/decrypt_key_management.ts new file mode 100644 index 0000000000..0960882982 --- /dev/null +++ b/dist/deno/lib/decrypt_key_management.ts @@ -0,0 +1,141 @@ +import { unwrap as aesKw } from '../runtime/aeskw.ts' +import * as ECDH from '../runtime/ecdhes.ts' +import { decrypt as pbes2Kw } from '../runtime/pbes2kw.ts' +import { decrypt as rsaEs } from '../runtime/rsaes.ts' +import { decode as base64url } from '../runtime/base64url.ts' + +import type { DecryptOptions, JWEHeaderParameters, KeyLike, JWK } from '../types.d.ts' +import { JOSENotSupported, JWEInvalid } from '../util/errors.ts' +import { bitLength as cekLength } from '../lib/cek.ts' +import { importJWK } from '../key/import.ts' +import checkKeyType from './check_key_type.ts' +import isObject from './is_object.ts' +import { unwrap as aesGcmKw } from './aesgcmkw.ts' + +async function decryptKeyManagement( + alg: string, + key: KeyLike | Uint8Array, + encryptedKey: Uint8Array | undefined, + joseHeader: JWEHeaderParameters, + options?: DecryptOptions, +): Promise { + checkKeyType(alg, key, 'decrypt') + + switch (alg) { + case 'dir': { + // Direct Encryption + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key') + + return key + } + case 'ECDH-ES': + // Direct Key Agreement + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key') + + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + // Direct Key Agreement + if (!isObject(joseHeader.epk)) + throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`) + + if (!ECDH.ecdhAllowed(key)) + throw new JOSENotSupported( + 'ECDH with the provided key is not allowed or not supported by your javascript runtime', + ) + + const epk = await importJWK(joseHeader.epk, alg) + let partyUInfo!: Uint8Array + let partyVInfo!: Uint8Array + + if (joseHeader.apu !== undefined) { + if (typeof joseHeader.apu !== 'string') + throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`) + partyUInfo = base64url(joseHeader.apu) + } + + if (joseHeader.apv !== undefined) { + if (typeof joseHeader.apv !== 'string') + throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`) + partyVInfo = base64url(joseHeader.apv) + } + + const sharedSecret = await ECDH.deriveKey( + epk, + key, + alg === 'ECDH-ES' ? joseHeader.enc! : alg, + alg === 'ECDH-ES' ? cekLength(joseHeader.enc!) : parseInt(alg.slice(-5, -2), 10), + partyUInfo, + partyVInfo, + ) + + if (alg === 'ECDH-ES') return sharedSecret + + // Key Agreement with Key Wrapping + if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') + + return aesKw(alg.slice(-6), sharedSecret, encryptedKey) + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + // Key Encryption (RSA) + if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') + + return rsaEs(alg, key, encryptedKey) + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + // Key Encryption (PBES2) + if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') + + if (typeof joseHeader.p2c !== 'number') + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`) + + const p2cLimit = options?.maxPBES2Count || 10_000 + + if (joseHeader.p2c > p2cLimit) + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`) + + if (typeof joseHeader.p2s !== 'string') + throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`) + + return pbes2Kw(alg, key, encryptedKey, joseHeader.p2c, base64url(joseHeader.p2s)) + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + // Key Wrapping (AES KW) + if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') + + return aesKw(alg, key, encryptedKey) + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + // Key Wrapping (AES GCM KW) + if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') + + if (typeof joseHeader.iv !== 'string') + throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`) + + if (typeof joseHeader.tag !== 'string') + throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`) + + const iv = base64url(joseHeader.iv) + const tag = base64url(joseHeader.tag) + + return aesGcmKw(alg, key, encryptedKey, iv, tag) + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value') + } + } +} + +export default decryptKeyManagement diff --git a/dist/deno/lib/encrypt_key_management.ts b/dist/deno/lib/encrypt_key_management.ts new file mode 100644 index 0000000000..4ce03a4b8d --- /dev/null +++ b/dist/deno/lib/encrypt_key_management.ts @@ -0,0 +1,124 @@ +import { wrap as aesKw } from '../runtime/aeskw.ts' +import * as ECDH from '../runtime/ecdhes.ts' +import { encrypt as pbes2Kw } from '../runtime/pbes2kw.ts' +import { encrypt as rsaEs } from '../runtime/rsaes.ts' +import { encode as base64url } from '../runtime/base64url.ts' + +import type { + KeyLike, + JWEKeyManagementHeaderParameters, + JWEHeaderParameters, + JWK, +} from '../types.d.ts' +import generateCek, { bitLength as cekLength } from '../lib/cek.ts' +import { JOSENotSupported } from '../util/errors.ts' +import { exportJWK } from '../key/export.ts' +import checkKeyType from './check_key_type.ts' +import { wrap as aesGcmKw } from './aesgcmkw.ts' + +async function encryptKeyManagement( + alg: string, + enc: string, + key: KeyLike | Uint8Array, + providedCek?: Uint8Array, + providedParameters: JWEKeyManagementHeaderParameters = {}, +): Promise<{ + cek: KeyLike | Uint8Array + encryptedKey?: Uint8Array + parameters?: JWEHeaderParameters +}> { + let encryptedKey: Uint8Array | undefined + let parameters: (JWEHeaderParameters & { epk?: JWK }) | undefined + let cek: KeyLike | Uint8Array + + checkKeyType(alg, key, 'encrypt') + + switch (alg) { + case 'dir': { + // Direct Encryption + cek = key + break + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + // Direct Key Agreement + if (!ECDH.ecdhAllowed(key)) { + throw new JOSENotSupported( + 'ECDH with the provided key is not allowed or not supported by your javascript runtime', + ) + } + const { apu, apv } = providedParameters + let { epk: ephemeralKey } = providedParameters + ephemeralKey ||= (await ECDH.generateEpk(key)).privateKey + const { x, y, crv, kty } = await exportJWK(ephemeralKey!) + const sharedSecret = await ECDH.deriveKey( + key, + ephemeralKey, + alg === 'ECDH-ES' ? enc : alg, + alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), + apu, + apv, + ) + parameters = { epk: { x, crv, kty } } + if (kty === 'EC') parameters.epk!.y = y + if (apu) parameters.apu = base64url(apu) + if (apv) parameters.apv = base64url(apv) + + if (alg === 'ECDH-ES') { + cek = sharedSecret + break + } + + // Key Agreement with Key Wrapping + cek = providedCek || generateCek(enc) + const kwAlg = alg.slice(-6) + encryptedKey = await aesKw(kwAlg, sharedSecret, cek) + break + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + // Key Encryption (RSA) + cek = providedCek || generateCek(enc) + encryptedKey = await rsaEs(alg, key, cek) + break + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + // Key Encryption (PBES2) + cek = providedCek || generateCek(enc) + const { p2c, p2s } = providedParameters + ;({ encryptedKey, ...parameters } = await pbes2Kw(alg, key, cek, p2c, p2s)) + break + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + // Key Wrapping (AES KW) + cek = providedCek || generateCek(enc) + encryptedKey = await aesKw(alg, key, cek) + break + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + // Key Wrapping (AES GCM KW) + cek = providedCek || generateCek(enc) + const { iv } = providedParameters + ;({ encryptedKey, ...parameters } = await aesGcmKw(alg, key, cek, iv)) + break + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value') + } + } + + return { cek, encryptedKey, parameters } +} + +export default encryptKeyManagement diff --git a/dist/deno/lib/epoch.ts b/dist/deno/lib/epoch.ts new file mode 100644 index 0000000000..616795a52e --- /dev/null +++ b/dist/deno/lib/epoch.ts @@ -0,0 +1 @@ +export default (date: Date) => Math.floor(date.getTime() / 1000) diff --git a/dist/deno/lib/format_pem.ts b/dist/deno/lib/format_pem.ts new file mode 100644 index 0000000000..ff54c795dd --- /dev/null +++ b/dist/deno/lib/format_pem.ts @@ -0,0 +1,4 @@ +export default (b64: string, descriptor: string) => { + const newlined = (b64.match(/.{1,64}/g) || []).join('\n') + return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----` +} diff --git a/dist/deno/lib/invalid_key_input.ts b/dist/deno/lib/invalid_key_input.ts new file mode 100644 index 0000000000..2b99dd440c --- /dev/null +++ b/dist/deno/lib/invalid_key_input.ts @@ -0,0 +1,30 @@ +function message(msg: string, actual: unknown, ...types: string[]) { + 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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}` + } + } + + return msg +} + +export default (actual: unknown, ...types: string[]) => { + return message('Key must be ', actual, ...types) +} + +export function withAlg(alg: string, actual: unknown, ...types: string[]) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types) +} diff --git a/dist/deno/lib/is_disjoint.ts b/dist/deno/lib/is_disjoint.ts new file mode 100644 index 0000000000..c260352e76 --- /dev/null +++ b/dist/deno/lib/is_disjoint.ts @@ -0,0 +1,26 @@ +const isDisjoint = (...headers: Array) => { + const sources = headers.filter(Boolean) + + if (sources.length === 0 || sources.length === 1) { + return true + } + + let acc!: Set + for (const header of sources) { + const parameters = Object.keys(header) + if (!acc || acc.size === 0) { + acc = new Set(parameters) + continue + } + + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false + } + acc.add(parameter) + } + } + + return true +} +export default isDisjoint diff --git a/dist/deno/lib/is_object.ts b/dist/deno/lib/is_object.ts new file mode 100644 index 0000000000..50aa4f6a5c --- /dev/null +++ b/dist/deno/lib/is_object.ts @@ -0,0 +1,17 @@ +function isObjectLike(value: unknown) { + return typeof value === 'object' && value !== null +} + +export default function isObject(input: unknown): input is T { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') { + return false + } + if (Object.getPrototypeOf(input) === null) { + return true + } + let proto = input + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto) + } + return Object.getPrototypeOf(input) === proto +} diff --git a/dist/deno/lib/iv.ts b/dist/deno/lib/iv.ts new file mode 100644 index 0000000000..93eb2ca8d8 --- /dev/null +++ b/dist/deno/lib/iv.ts @@ -0,0 +1,21 @@ +import { JOSENotSupported } from '../util/errors.ts' +import random from '../runtime/random.ts' + +export function bitLength(alg: string) { + 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}`) + } +} +export default (alg: string): Uint8Array => random(new Uint8Array(bitLength(alg) >> 3)) diff --git a/dist/deno/lib/jwt_claims_set.ts b/dist/deno/lib/jwt_claims_set.ts new file mode 100644 index 0000000000..5ebb4c0872 --- /dev/null +++ b/dist/deno/lib/jwt_claims_set.ts @@ -0,0 +1,139 @@ +import type { + JWTPayload, + JWTClaimVerificationOptions, + JWEHeaderParameters, + JWSHeaderParameters, +} from '../types.d.ts' +import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.ts' +import { decoder } from './buffer_utils.ts' +import epoch from './epoch.ts' +import secs from './secs.ts' +import isObject from './is_object.ts' + +const normalizeTyp = (value: string) => value.toLowerCase().replace(/^application\//, '') + +const checkAudiencePresence = (audPayload: unknown, audOption: unknown[]) => { + if (typeof audPayload === 'string') { + return audOption.includes(audPayload) + } + + if (Array.isArray(audPayload)) { + // Each principal intended to process the JWT MUST + // identify itself with a value in the audience claim + return audOption.some(Set.prototype.has.bind(new Set(audPayload))) + } + + return false +} + +export default ( + protectedHeader: JWEHeaderParameters | JWSHeaderParameters, + encodedPayload: Uint8Array, + options: JWTClaimVerificationOptions = {}, +) => { + const { typ } = options + if ( + typ && + (typeof protectedHeader!.typ !== 'string' || + normalizeTyp(protectedHeader!.typ) !== normalizeTyp(typ)) + ) { + throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed') + } + + let payload!: { [propName: string]: unknown } + try { + payload = JSON.parse(decoder.decode(encodedPayload)) + } catch { + // + } + + if (!isObject(payload)) { + throw new JWTInvalid('JWT Claims Set must be a top-level JSON object') + } + + const { issuer } = options + if (issuer && !((Array.isArray(issuer) ? issuer : [issuer])).includes(payload.iss!)) { + throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed') + } + + const { subject } = options + if (subject && payload.sub !== subject) { + throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed') + } + + const { audience } = options + if ( + audience && + !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience) + ) { + throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed') + } + + let tolerance: number + switch (typeof options.clockTolerance) { + case 'string': + tolerance = secs(options.clockTolerance) + break + case 'number': + tolerance = options.clockTolerance + break + case 'undefined': + tolerance = 0 + break + default: + throw new TypeError('Invalid clockTolerance option type') + } + + const { currentDate } = options + const now = epoch(currentDate || new Date()) + + if ((payload.iat !== undefined || options.maxTokenAge) && typeof payload.iat !== 'number') { + throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid') + } + + if (payload.nbf !== undefined) { + if (typeof payload.nbf !== 'number') { + throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid') + } + if (payload.nbf > now + tolerance) { + throw new JWTClaimValidationFailed( + '"nbf" claim timestamp check failed', + 'nbf', + 'check_failed', + ) + } + } + + if (payload.exp !== undefined) { + if (typeof payload.exp !== 'number') { + throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid') + } + if (payload.exp <= now - tolerance) { + throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed') + } + } + + if (options.maxTokenAge) { + const age = now - payload.iat! + const max = + typeof options.maxTokenAge === 'number' ? options.maxTokenAge : secs(options.maxTokenAge) + + if (age - tolerance > max) { + throw new JWTExpired( + '"iat" claim timestamp check failed (too far in the past)', + 'iat', + 'check_failed', + ) + } + + if (age < 0 - tolerance) { + throw new JWTClaimValidationFailed( + '"iat" claim timestamp check failed (it should be in the past)', + 'iat', + 'check_failed', + ) + } + } + + return payload +} diff --git a/dist/deno/lib/secs.ts b/dist/deno/lib/secs.ts new file mode 100644 index 0000000000..8d9f07f7de --- /dev/null +++ b/dist/deno/lib/secs.ts @@ -0,0 +1,51 @@ +const minute = 60 +const hour = minute * 60 +const day = hour * 24 +const week = day * 7 +const year = day * 365.25 + +const REGEX = + /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i + +export default (str: string): number => { + const matched = REGEX.exec(str) + + if (!matched) { + throw new TypeError('Invalid time period format') + } + + const value = parseFloat(matched[1]) + const unit = matched[2].toLowerCase() + + switch (unit) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + case 's': + return Math.round(value) + case 'minute': + case 'minutes': + case 'min': + case 'mins': + case 'm': + return Math.round(value * minute) + case 'hour': + case 'hours': + case 'hr': + case 'hrs': + case 'h': + return Math.round(value * hour) + case 'day': + case 'days': + case 'd': + return Math.round(value * day) + case 'week': + case 'weeks': + case 'w': + return Math.round(value * week) + // years matched + default: + return Math.round(value * year) + } +} diff --git a/dist/deno/lib/validate_algorithms.ts b/dist/deno/lib/validate_algorithms.ts new file mode 100644 index 0000000000..870efaa6ef --- /dev/null +++ b/dist/deno/lib/validate_algorithms.ts @@ -0,0 +1,16 @@ +const validateAlgorithms = (option: string, algorithms?: string[]) => { + if ( + algorithms !== undefined && + (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string')) + ) { + throw new TypeError(`"${option}" option must be an array of strings`) + } + + if (!algorithms) { + return undefined + } + + return new Set(algorithms) +} + +export default validateAlgorithms diff --git a/dist/deno/lib/validate_crit.ts b/dist/deno/lib/validate_crit.ts new file mode 100644 index 0000000000..d5ea76dc55 --- /dev/null +++ b/dist/deno/lib/validate_crit.ts @@ -0,0 +1,56 @@ +import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.ts' + +interface CritCheckHeader { + b64?: boolean + crit?: string[] + [propName: string]: unknown +} + +function validateCrit( + Err: typeof JWEInvalid | typeof JWSInvalid, + recognizedDefault: Map, + recognizedOption: { [propName: string]: boolean } | undefined, + protectedHeader: CritCheckHeader, + joseHeader: CritCheckHeader, +) { + if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected') + } + + if (!protectedHeader || protectedHeader.crit === undefined) { + return new Set() + } + + if ( + !Array.isArray(protectedHeader.crit) || + protectedHeader.crit.length === 0 || + protectedHeader.crit.some((input: string) => typeof input !== 'string' || input.length === 0) + ) { + throw new Err( + '"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present', + ) + } + + let recognized: Map + if (recognizedOption !== undefined) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]) + } else { + recognized = recognizedDefault + } + + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`) + } + + if (joseHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`) + } else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`) + } + } + + return new Set(protectedHeader.crit) +} + +export default validateCrit diff --git a/dist/deno/runtime/aeskw.ts b/dist/deno/runtime/aeskw.ts new file mode 100644 index 0000000000..72da0ef4a7 --- /dev/null +++ b/dist/deno/runtime/aeskw.ts @@ -0,0 +1,56 @@ +import type { AesKwUnwrapFunction, AesKwWrapFunction } from './interfaces.d.ts' +import bogusWebCrypto from './bogus.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +function checkKeySize(key: CryptoKey, alg: string) { + if ((key.algorithm).length !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`) + } +} + +function getCryptoKey(key: unknown, alg: string, usage: KeyUsage) { + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, usage) + return key + } + + if (key instanceof Uint8Array) { + return crypto.subtle.importKey('raw', key, 'AES-KW', true, [usage]) + } + + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')) +} + +export const wrap: AesKwWrapFunction = async (alg: string, key: unknown, cek: Uint8Array) => { + const cryptoKey = await getCryptoKey(key, alg, 'wrapKey') + + checkKeySize(cryptoKey, alg) + + // we're importing the cek to end up with CryptoKey instance that can be wrapped, the algorithm used is irrelevant + const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, ...bogusWebCrypto) + + return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW')) +} + +export const unwrap: AesKwUnwrapFunction = async ( + alg: string, + key: unknown, + encryptedKey: Uint8Array, +) => { + const cryptoKey = await getCryptoKey(key, alg, 'unwrapKey') + + checkKeySize(cryptoKey, alg) + + const cryptoKeyCek = await crypto.subtle.unwrapKey( + 'raw', + encryptedKey, + cryptoKey, + 'AES-KW', + ...bogusWebCrypto, + ) + + return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek)) +} diff --git a/dist/deno/runtime/asn1.ts b/dist/deno/runtime/asn1.ts new file mode 100644 index 0000000000..7e4db5886e --- /dev/null +++ b/dist/deno/runtime/asn1.ts @@ -0,0 +1,268 @@ +import { isCloudflareWorkers } from './env.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import type { PEMExportFunction, PEMImportFunction } from './interfaces.d.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { encodeBase64, decodeBase64 } from './base64url.ts' +import formatPEM from '../lib/format_pem.ts' +import { JOSENotSupported } from '../util/errors.ts' +import { types } from './is_key_like.ts' + +import type { PEMImportOptions } from '../key/import.ts' + +const genericExport = async ( + keyType: 'private' | 'public', + keyFormat: 'spki' | 'pkcs8', + key: unknown, +) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable') + } + + if (key.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`) + } + + return formatPEM( + encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), + `${keyType.toUpperCase()} KEY`, + ) +} + +export const toSPKI: PEMExportFunction = (key) => { + return genericExport('public', 'spki', key) +} + +export const toPKCS8: PEMExportFunction = (key) => { + return genericExport('private', 'pkcs8', key) +} + +const findOid = (keyData: Uint8Array, oid: number[], from = 0): boolean => { + if (from === 0) { + oid.unshift(oid.length) + oid.unshift(0x06) + } + let i = keyData.indexOf(oid[0], from) + if (i === -1) return false + const sub = keyData.subarray(i, i + oid.length) + if (sub.length !== oid.length) return false + return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1) +} + +const getNamedCurve = (keyData: Uint8Array): string => { + switch (true) { + case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]): + return 'P-256' + case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x22]): + return 'P-384' + case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]): + return 'P-521' + case findOid(keyData, [0x2b, 0x65, 0x6e]): + return 'X25519' + case findOid(keyData, [0x2b, 0x65, 0x6f]): + return 'X448' + case findOid(keyData, [0x2b, 0x65, 0x70]): + return 'Ed25519' + case findOid(keyData, [0x2b, 0x65, 0x71]): + return 'Ed448' + default: + throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type') + } +} + +const genericImport = async ( + replace: RegExp, + keyFormat: 'spki' | 'pkcs8', + pem: string, + alg: string, + options?: PEMImportOptions, +) => { + let algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm + let keyUsages: KeyUsage[] + + const keyData = new Uint8Array( + atob(pem.replace(replace, '')) + .split('') + .map((c) => c.charCodeAt(0)), + ) + + const isPublic = keyFormat === 'spki' + + switch (alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + } + keyUsages = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'] + break + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + const namedCurve = getNamedCurve(keyData) + algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve } + keyUsages = isPublic ? [] : ['deriveBits'] + break + } + case 'EdDSA': + algorithm = { name: getNamedCurve(keyData) } + keyUsages = isPublic ? ['verify'] : ['sign'] + break + default: + throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value') + } + + try { + return await crypto.subtle.importKey( + keyFormat, + keyData, + algorithm, + options?.extractable ?? false, + keyUsages, + ) + } catch (err) { + if ( + algorithm.name === 'Ed25519' && + (err)?.name === 'NotSupportedError' && + isCloudflareWorkers() + ) { + algorithm = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' } + return await crypto.subtle.importKey( + keyFormat, + keyData, + algorithm, + options?.extractable ?? false, + keyUsages, + ) + } + throw err + } +} + +export const fromPKCS8: PEMImportFunction = (pem, alg, options?) => { + return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options) +} + +export const fromSPKI: PEMImportFunction = (pem, alg, options?) => { + return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options) +} + +function getElement(seq: Uint8Array) { + let result = [] + let next = 0 + + while (next < seq.length) { + let nextPart = parseElement(seq.subarray(next)) + result.push(nextPart) + next += nextPart.byteLength + } + return result +} + +function parseElement(bytes: Uint8Array) { + let position = 0 + + // tag + let tag = bytes[0] & 0x1f + position++ + if (tag === 0x1f) { + tag = 0 + while (bytes[position] >= 0x80) { + tag = tag * 128 + bytes[position] - 0x80 + position++ + } + tag = tag * 128 + bytes[position] - 0x80 + position++ + } + + // length + let length = 0 + if (bytes[position] < 0x80) { + length = bytes[position] + position++ + } else if (length === 0x80) { + length = 0 + + while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) { + if (length > bytes.byteLength) { + throw new TypeError('invalid indefinite form length') + } + length++ + } + + const byteLength = position + length + 2 + return { + byteLength, + contents: bytes.subarray(position, position + length), + raw: bytes.subarray(0, byteLength), + } + } else { + let numberOfDigits = bytes[position] & 0x7f + position++ + length = 0 + for (let i = 0; i < numberOfDigits; i++) { + length = length * 256 + bytes[position] + position++ + } + } + + const byteLength = position + length + return { + byteLength, + contents: bytes.subarray(position, byteLength), + raw: bytes.subarray(0, byteLength), + } +} + +function spkiFromX509(buf: Uint8Array) { + const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents) + return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw) +} + +function getSPKI(x509: string): string { + const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '') + const raw = decodeBase64(pem) + return formatPEM(spkiFromX509(raw), 'PUBLIC KEY') +} + +export const fromX509: PEMImportFunction = (pem, alg, options?) => { + let spki: string + try { + spki = getSPKI(pem) + } catch (cause) { + // @ts-ignore + throw new TypeError('failed to parse the X.509 certificate', { cause }) + } + return fromSPKI(spki, alg, options) +} diff --git a/dist/deno/runtime/base64url.ts b/dist/deno/runtime/base64url.ts new file mode 100644 index 0000000000..45dff570c3 --- /dev/null +++ b/dist/deno/runtime/base64url.ts @@ -0,0 +1,41 @@ +import { encoder, decoder } from '../lib/buffer_utils.ts' + +export const encodeBase64 = (input: Uint8Array | string) => { + let unencoded = input + if (typeof unencoded === 'string') { + unencoded = encoder.encode(unencoded) + } + const CHUNK_SIZE = 0x8000 + const arr = [] + for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) { + // @ts-expect-error + arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE))) + } + return btoa(arr.join('')) +} + +export const encode = (input: Uint8Array | string) => { + return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_') +} + +export const decodeBase64 = (encoded: string): Uint8Array => { + const binary = atob(encoded) + const bytes = new Uint8Array(binary.length) + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i) + } + return bytes +} + +export const decode = (input: Uint8Array | string) => { + let encoded = input + if (encoded instanceof Uint8Array) { + encoded = decoder.decode(encoded) + } + encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '') + try { + return decodeBase64(encoded) + } catch { + throw new TypeError('The input to be decoded is not correctly encoded.') + } +} diff --git a/dist/deno/runtime/bogus.ts b/dist/deno/runtime/bogus.ts new file mode 100644 index 0000000000..3f7bcfdf66 --- /dev/null +++ b/dist/deno/runtime/bogus.ts @@ -0,0 +1,7 @@ +const bogusWebCrypto: [HmacImportParams, boolean, KeyUsage[]] = [ + { hash: 'SHA-256', name: 'HMAC' }, + true, + ['sign'], +] + +export default bogusWebCrypto diff --git a/dist/deno/runtime/check_cek_length.ts b/dist/deno/runtime/check_cek_length.ts new file mode 100644 index 0000000000..44aeda9684 --- /dev/null +++ b/dist/deno/runtime/check_cek_length.ts @@ -0,0 +1,12 @@ +import { JWEInvalid } from '../util/errors.ts' + +const checkCekLength = (cek: Uint8Array, expected: number) => { + const actual = cek.byteLength << 3 + if (actual !== expected) { + throw new JWEInvalid( + `Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`, + ) + } +} + +export default checkCekLength diff --git a/dist/deno/runtime/check_key_length.ts b/dist/deno/runtime/check_key_length.ts new file mode 100644 index 0000000000..fac5e16f32 --- /dev/null +++ b/dist/deno/runtime/check_key_length.ts @@ -0,0 +1,8 @@ +export default (alg: string, key: CryptoKey) => { + if (alg.startsWith('RS') || alg.startsWith('PS')) { + const { modulusLength } = key.algorithm + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`) + } + } +} diff --git a/dist/deno/runtime/decrypt.ts b/dist/deno/runtime/decrypt.ts new file mode 100644 index 0000000000..5210491dea --- /dev/null +++ b/dist/deno/runtime/decrypt.ts @@ -0,0 +1,137 @@ +import { concat, uint64be } from '../lib/buffer_utils.ts' + +import type { DecryptFunction } from './interfaces.d.ts' +import checkIvLength from '../lib/check_iv_length.ts' +import checkCekLength from './check_cek_length.ts' +import timingSafeEqual from './timing_safe_equal.ts' +import { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +async function cbcDecrypt( + enc: string, + cek: Uint8Array | CryptoKey, + ciphertext: Uint8Array, + iv: Uint8Array, + tag: Uint8Array, + aad: Uint8Array, +) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, 'Uint8Array')) + } + const keySize = parseInt(enc.slice(1, 4), 10) + const encKey = await crypto.subtle.importKey( + 'raw', + cek.subarray(keySize >> 3), + 'AES-CBC', + false, + ['decrypt'], + ) + const macKey = await crypto.subtle.importKey( + 'raw', + cek.subarray(0, keySize >> 3), + { + hash: `SHA-${keySize << 1}`, + name: 'HMAC', + }, + false, + ['sign'], + ) + + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)) + const expectedTag = new Uint8Array( + (await crypto.subtle.sign('HMAC', macKey, macData)).slice(0, keySize >> 3), + ) + + let macCheckPassed!: boolean + try { + macCheckPassed = timingSafeEqual(tag, expectedTag) + } catch { + // + } + if (!macCheckPassed) { + throw new JWEDecryptionFailed() + } + + let plaintext!: Uint8Array + try { + plaintext = new Uint8Array( + await crypto.subtle.decrypt({ iv, name: 'AES-CBC' }, encKey, ciphertext), + ) + } catch { + // + } + if (!plaintext) { + throw new JWEDecryptionFailed() + } + + return plaintext +} + +async function gcmDecrypt( + enc: string, + cek: Uint8Array | CryptoKey, + ciphertext: Uint8Array, + iv: Uint8Array, + tag: Uint8Array, + aad: Uint8Array, +) { + let encKey: CryptoKey + if (cek instanceof Uint8Array) { + encKey = await crypto.subtle.importKey('raw', cek, 'AES-GCM', false, ['decrypt']) + } else { + checkEncCryptoKey(cek, enc, 'decrypt') + encKey = cek + } + + try { + return new Uint8Array( + await crypto.subtle.decrypt( + { + additionalData: aad, + iv, + name: 'AES-GCM', + tagLength: 128, + }, + encKey, + concat(ciphertext, tag), + ), + ) + } catch { + throw new JWEDecryptionFailed() + } +} + +const decrypt: DecryptFunction = async ( + enc: string, + cek: unknown, + ciphertext: Uint8Array, + iv: Uint8Array, + tag: Uint8Array, + aad: Uint8Array, +) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')) + } + + checkIvLength(enc, iv) + + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(-3), 10)) + return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(1, 4), 10)) + return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm') + } +} + +export default decrypt diff --git a/dist/deno/runtime/digest.ts b/dist/deno/runtime/digest.ts new file mode 100644 index 0000000000..58a6e302a1 --- /dev/null +++ b/dist/deno/runtime/digest.ts @@ -0,0 +1,11 @@ +import crypto from './webcrypto.ts' +import type { DigestFunction } from './interfaces.d.ts' + +const digest: DigestFunction = async ( + algorithm: 'sha256' | 'sha384' | 'sha512', + data: Uint8Array, +): Promise => { + const subtleDigest = `SHA-${algorithm.slice(-3)}` + return new Uint8Array(await crypto.subtle.digest(subtleDigest, data)) +} +export default digest diff --git a/dist/deno/runtime/ecdhes.ts b/dist/deno/runtime/ecdhes.ts new file mode 100644 index 0000000000..d5f67827fb --- /dev/null +++ b/dist/deno/runtime/ecdhes.ts @@ -0,0 +1,72 @@ +import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +export async function deriveKey( + publicKey: unknown, + privateKey: unknown, + algorithm: string, + keyLength: number, + apu: Uint8Array = new Uint8Array(0), + apv: Uint8Array = new Uint8Array(0), +) { + if (!isCryptoKey(publicKey)) { + throw new TypeError(invalidKeyInput(publicKey, ...types)) + } + checkEncCryptoKey(publicKey, 'ECDH') + if (!isCryptoKey(privateKey)) { + throw new TypeError(invalidKeyInput(privateKey, ...types)) + } + checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits') + + const value = concat( + lengthAndInput(encoder.encode(algorithm)), + lengthAndInput(apu), + lengthAndInput(apv), + uint32be(keyLength), + ) + + let length: number + if (publicKey.algorithm.name === 'X25519') { + length = 256 + } else if (publicKey.algorithm.name === 'X448') { + length = 448 + } else { + length = + Math.ceil(parseInt((publicKey.algorithm).namedCurve.substr(-3), 10) / 8) << 3 + } + + const sharedSecret = new Uint8Array( + await crypto.subtle.deriveBits( + { + name: publicKey.algorithm.name, + public: publicKey, + }, + privateKey, + length, + ), + ) + + return concatKdf(sharedSecret, keyLength, value) +} + +export async function generateEpk(key: unknown) { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + + return crypto.subtle.generateKey(key.algorithm, true, ['deriveBits']) +} + +export function ecdhAllowed(key: unknown) { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + return ( + ['P-256', 'P-384', 'P-521'].includes((key.algorithm).namedCurve) || + key.algorithm.name === 'X25519' || + key.algorithm.name === 'X448' + ) +} diff --git a/dist/deno/runtime/encrypt.ts b/dist/deno/runtime/encrypt.ts new file mode 100644 index 0000000000..f74773c6bb --- /dev/null +++ b/dist/deno/runtime/encrypt.ts @@ -0,0 +1,122 @@ +import { concat, uint64be } from '../lib/buffer_utils.ts' +import type { EncryptFunction } from './interfaces.d.ts' +import checkIvLength from '../lib/check_iv_length.ts' +import checkCekLength from './check_cek_length.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { JOSENotSupported } from '../util/errors.ts' +import { types } from './is_key_like.ts' + +async function cbcEncrypt( + enc: string, + plaintext: Uint8Array, + cek: Uint8Array | CryptoKey, + iv: Uint8Array, + aad: Uint8Array, +) { + if (!(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(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 } +} + +async function gcmEncrypt( + enc: string, + plaintext: Uint8Array, + cek: Uint8Array | CryptoKey, + iv: Uint8Array, + aad: Uint8Array, +) { + let encKey: CryptoKey + 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 } +} + +const encrypt: EncryptFunction = async ( + enc: string, + plaintext: Uint8Array, + cek: unknown, + iv: Uint8Array, + aad: Uint8Array, +) => { + if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')) + } + + checkIvLength(enc, iv) + + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(-3), 10)) + return cbcEncrypt(enc, plaintext, cek, iv, aad) + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(1, 4), 10)) + return gcmEncrypt(enc, plaintext, cek, iv, aad) + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm') + } +} + +export default encrypt diff --git a/dist/deno/runtime/env.ts b/dist/deno/runtime/env.ts new file mode 100644 index 0000000000..c756022f1b --- /dev/null +++ b/dist/deno/runtime/env.ts @@ -0,0 +1,10 @@ +export function isCloudflareWorkers() { + return ( + // @ts-ignore + typeof WebSocketPair !== 'undefined' || + // @ts-ignore + (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') || + // @ts-ignore + (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel') + ) +} diff --git a/dist/deno/runtime/fetch_jwks.ts b/dist/deno/runtime/fetch_jwks.ts new file mode 100644 index 0000000000..6b7aad808f --- /dev/null +++ b/dist/deno/runtime/fetch_jwks.ts @@ -0,0 +1,43 @@ +import type { FetchFunction } from './interfaces.d.ts' +import { JOSEError, JWKSTimeout } from '../util/errors.ts' + +type AcceptedRequestOptions = Pick + +const fetchJwks: FetchFunction = async ( + url: URL, + timeout: number, + options: AcceptedRequestOptions, +) => { + let controller!: AbortController + let id!: ReturnType + let timedOut = false + if (typeof AbortController === 'function') { + controller = new AbortController() + id = setTimeout(() => { + timedOut = true + controller.abort() + }, timeout) + } + + const response = await fetch(url.href, { + signal: controller ? controller.signal : undefined, + redirect: 'manual', + headers: options.headers, + }).catch((err) => { + if (timedOut) throw new JWKSTimeout() + throw err + }) + + if (id !== undefined) clearTimeout(id) + + if (response.status !== 200) { + throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response') + } + + try { + return await response.json() + } catch { + throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON') + } +} +export default fetchJwks diff --git a/dist/deno/runtime/generate.ts b/dist/deno/runtime/generate.ts new file mode 100644 index 0000000000..b9672cf2fb --- /dev/null +++ b/dist/deno/runtime/generate.ts @@ -0,0 +1,169 @@ +import { isCloudflareWorkers } from './env.ts' +import crypto from './webcrypto.ts' +import { JOSENotSupported } from '../util/errors.ts' +import random from './random.ts' +import type { GenerateKeyPairOptions } from '../key/generate_key_pair.ts' +import type { GenerateSecretOptions } from '../key/generate_secret.ts' + +export async function generateSecret(alg: string, options?: GenerateSecretOptions) { + let length: number + let algorithm: AesKeyGenParams | HmacKeyGenParams + let keyUsages: KeyUsage[] + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + length = parseInt(alg.slice(-3), 10) + algorithm = { name: 'HMAC', hash: `SHA-${length}`, length } + keyUsages = ['sign', 'verify'] + break + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + length = parseInt(alg.slice(-3), 10) + return random(new Uint8Array(length >> 3)) + case 'A128KW': + case 'A192KW': + case 'A256KW': + length = parseInt(alg.slice(1, 4), 10) + algorithm = { name: 'AES-KW', length } + keyUsages = ['wrapKey', 'unwrapKey'] + break + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + length = parseInt(alg.slice(1, 4), 10) + algorithm = { name: 'AES-GCM', length } + keyUsages = ['encrypt', 'decrypt'] + break + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + + return >( + (crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages)) + ) +} + +function getModulusLengthOption(options?: GenerateKeyPairOptions) { + const modulusLength = options?.modulusLength ?? 2048 + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new JOSENotSupported( + 'Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used', + ) + } + return modulusLength +} + +export async function generateKeyPair(alg: string, options?: GenerateKeyPairOptions) { + let algorithm: RsaHashedKeyGenParams | EcKeyGenParams | KeyAlgorithm + let keyUsages: KeyUsage[] + + switch (alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { + name: 'RSA-PSS', + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + } + keyUsages = ['sign', 'verify'] + break + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { + name: 'RSASSA-PKCS1-v1_5', + hash: `SHA-${alg.slice(-3)}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + } + keyUsages = ['sign', 'verify'] + break + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + modulusLength: getModulusLengthOption(options), + } + keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey'] + break + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' } + keyUsages = ['sign', 'verify'] + break + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' } + keyUsages = ['sign', 'verify'] + break + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' } + keyUsages = ['sign', 'verify'] + break + case 'EdDSA': + keyUsages = ['sign', 'verify'] + const crv = options?.crv ?? 'Ed25519' + switch (crv) { + case 'Ed25519': + case 'Ed448': + algorithm = { name: crv } + break + default: + throw new JOSENotSupported('Invalid or unsupported crv option provided') + } + break + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + keyUsages = ['deriveKey', 'deriveBits'] + const crv = options?.crv ?? 'P-256' + switch (crv) { + case 'P-256': + case 'P-384': + case 'P-521': { + algorithm = { name: 'ECDH', namedCurve: crv } + break + } + case 'X25519': + case 'X448': + algorithm = { name: crv } + break + default: + throw new JOSENotSupported( + 'Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448', + ) + } + break + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + + try { + return <{ publicKey: CryptoKey; privateKey: CryptoKey }>( + await crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages) + ) + } catch (err) { + if ( + algorithm.name === 'Ed25519' && + (err)?.name === 'NotSupportedError' && + isCloudflareWorkers() + ) { + algorithm = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' } + return <{ publicKey: CryptoKey; privateKey: CryptoKey }>( + await crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages) + ) + } + throw err + } +} diff --git a/dist/deno/runtime/get_sign_verify_key.ts b/dist/deno/runtime/get_sign_verify_key.ts new file mode 100644 index 0000000000..9a813f8e3c --- /dev/null +++ b/dist/deno/runtime/get_sign_verify_key.ts @@ -0,0 +1,26 @@ +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkSigCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +export default function getCryptoKey(alg: string, key: unknown, usage: KeyUsage) { + if (isCryptoKey(key)) { + checkSigCryptoKey(key, alg, usage) + return key + } + + if (key instanceof Uint8Array) { + if (!alg.startsWith('HS')) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + return crypto.subtle.importKey( + 'raw', + key, + { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, + false, + [usage], + ) + } + + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')) +} diff --git a/dist/deno/runtime/interfaces.d.ts b/dist/deno/runtime/interfaces.d.ts new file mode 100644 index 0000000000..242c79603f --- /dev/null +++ b/dist/deno/runtime/interfaces.d.ts @@ -0,0 +1,75 @@ +import type { JWK, KeyLike } from '../types.d.ts' +import type { PEMImportOptions } from '../key/import.ts' + +type AsyncOrSync = Promise | T + +export interface TimingSafeEqual { + (a: Uint8Array, b: Uint8Array): boolean +} +export interface SignFunction { + (alg: string, key: unknown, data: Uint8Array): Promise +} +export interface VerifyFunction { + (alg: string, key: unknown, signature: Uint8Array, data: Uint8Array): Promise +} +export interface AesKwWrapFunction { + (alg: string, key: unknown, cek: Uint8Array): AsyncOrSync +} +export interface AesKwUnwrapFunction { + (alg: string, key: unknown, encryptedKey: Uint8Array): AsyncOrSync +} +export interface RsaEsEncryptFunction { + (alg: string, key: unknown, cek: Uint8Array): AsyncOrSync +} +export interface RsaEsDecryptFunction { + (alg: string, key: unknown, encryptedKey: Uint8Array): AsyncOrSync +} +export interface Pbes2KWEncryptFunction { + (alg: string, key: unknown, cek: Uint8Array, p2c?: number, p2s?: Uint8Array): Promise<{ + encryptedKey: Uint8Array + p2c: number + p2s: string + }> +} +export interface Pbes2KWDecryptFunction { + ( + alg: string, + key: unknown, + encryptedKey: Uint8Array, + p2c: number, + p2s: Uint8Array, + ): Promise +} +export interface EncryptFunction { + (enc: string, plaintext: Uint8Array, cek: unknown, iv: Uint8Array, aad: Uint8Array): AsyncOrSync<{ + ciphertext: Uint8Array + tag: Uint8Array + }> +} +export interface DecryptFunction { + ( + enc: string, + cek: unknown, + ciphertext: Uint8Array, + iv: Uint8Array, + tag: Uint8Array, + additionalData: Uint8Array, + ): AsyncOrSync +} +export interface FetchFunction { + (url: URL, timeout: number, options?: any): Promise<{ [propName: string]: unknown }> +} +export interface DigestFunction { + (digest: 'sha256' | 'sha384' | 'sha512', data: Uint8Array): AsyncOrSync +} +export interface JWKImportFunction { + (jwk: JWK): AsyncOrSync +} +export interface PEMImportFunction { + (pem: string, alg: string, options?: PEMImportOptions): AsyncOrSync +} +interface ExportFunction { + (key: unknown): AsyncOrSync +} +export type JWKExportFunction = ExportFunction +export type PEMExportFunction = ExportFunction diff --git a/dist/deno/runtime/is_key_like.ts b/dist/deno/runtime/is_key_like.ts new file mode 100644 index 0000000000..7023da56fc --- /dev/null +++ b/dist/deno/runtime/is_key_like.ts @@ -0,0 +1,8 @@ +import type { KeyLike } from '../types.d.ts' +import { isCryptoKey } from './webcrypto.ts' + +export default (key: unknown): key is KeyLike => { + return isCryptoKey(key) +} + +export const types = ['CryptoKey'] diff --git a/dist/deno/runtime/jwk_to_key.ts b/dist/deno/runtime/jwk_to_key.ts new file mode 100644 index 0000000000..8367979fff --- /dev/null +++ b/dist/deno/runtime/jwk_to_key.ts @@ -0,0 +1,167 @@ +import { isCloudflareWorkers } from './env.ts' +import crypto from './webcrypto.ts' +import type { JWKImportFunction } from './interfaces.d.ts' +import { JOSENotSupported } from '../util/errors.ts' +import type { JWK } from '../types.d.ts' +import { decode as base64url } from './base64url.ts' + +function subtleMapping(jwk: JWK): { + algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm + keyUsages: KeyUsage[] +} { + let algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm + let keyUsages: KeyUsage[] + + switch (jwk.kty) { + case 'oct': { + switch (jwk.alg) { + case 'HS256': + case 'HS384': + case 'HS512': + algorithm = { name: 'HMAC', hash: `SHA-${jwk.alg.slice(-3)}` } + keyUsages = ['sign', 'verify'] + break + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + throw new JOSENotSupported(`${jwk.alg} keys cannot be imported as CryptoKey instances`) + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + algorithm = { name: 'AES-GCM' } + keyUsages = ['encrypt', 'decrypt'] + break + case 'A128KW': + case 'A192KW': + case 'A256KW': + algorithm = { name: 'AES-KW' } + keyUsages = ['wrapKey', 'unwrapKey'] + break + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': + algorithm = { name: 'PBKDF2' } + keyUsages = ['deriveBits'] + break + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + break + } + case 'RSA': { + switch (jwk.alg) { + case 'PS256': + case 'PS384': + case 'PS512': + algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'RS256': + case 'RS384': + case 'RS512': + algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + algorithm = { + name: 'RSA-OAEP', + hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`, + } + keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'] + break + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + break + } + case 'EC': { + switch (jwk.alg) { + case 'ES256': + algorithm = { name: 'ECDSA', namedCurve: 'P-256' } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'ES384': + algorithm = { name: 'ECDSA', namedCurve: 'P-384' } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'ES512': + algorithm = { name: 'ECDSA', namedCurve: 'P-521' } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + algorithm = { name: 'ECDH', namedCurve: jwk.crv! } + keyUsages = jwk.d ? ['deriveBits'] : [] + break + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + break + } + case 'OKP': { + switch (jwk.alg) { + case 'EdDSA': + algorithm = { name: jwk.crv! } + keyUsages = jwk.d ? ['sign'] : ['verify'] + break + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + algorithm = { name: jwk.crv! } + keyUsages = jwk.d ? ['deriveBits'] : [] + break + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') + } + break + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value') + } + + return { algorithm, keyUsages } +} + +const parse: JWKImportFunction = async (jwk: JWK): Promise => { + if (!jwk.alg) { + throw new TypeError('"alg" argument is required when "jwk.alg" is not present') + } + + const { algorithm, keyUsages } = subtleMapping(jwk) + const rest: [RsaHashedImportParams | EcKeyAlgorithm | Algorithm, boolean, KeyUsage[]] = [ + algorithm, + jwk.ext ?? false, + jwk.key_ops ?? keyUsages, + ] + + if (algorithm.name === 'PBKDF2') { + return crypto.subtle.importKey('raw', base64url(jwk.k!), ...rest) + } + + const keyData: JWK = { ...jwk } + delete keyData.alg + delete keyData.use + try { + return await crypto.subtle.importKey('jwk', keyData, ...rest) + } catch (err) { + if ( + algorithm.name === 'Ed25519' && + (err)?.name === 'NotSupportedError' && + isCloudflareWorkers() + ) { + rest[0] = { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' } + return await crypto.subtle.importKey('jwk', keyData, ...rest) + } + throw err + } +} +export default parse diff --git a/dist/deno/runtime/key_to_jwk.ts b/dist/deno/runtime/key_to_jwk.ts new file mode 100644 index 0000000000..c279b618cc --- /dev/null +++ b/dist/deno/runtime/key_to_jwk.ts @@ -0,0 +1,25 @@ +import crypto, { isCryptoKey } from './webcrypto.ts' +import type { JWKExportFunction } from './interfaces.d.ts' +import type { JWK } from '../types.d.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { encode as base64url } from './base64url.ts' +import { types } from './is_key_like.ts' + +const keyToJWK: JWKExportFunction = async (key: unknown): Promise => { + if (key instanceof Uint8Array) { + return { + kty: 'oct', + k: base64url(key), + } + } + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')) + } + if (!key.extractable) { + throw new TypeError('non-extractable CryptoKey cannot be exported as a JWK') + } + const { ext, key_ops, alg, use, ...jwk } = await crypto.subtle.exportKey('jwk', key) + + return jwk +} +export default keyToJWK diff --git a/dist/deno/runtime/pbes2kw.ts b/dist/deno/runtime/pbes2kw.ts new file mode 100644 index 0000000000..03976bdcc7 --- /dev/null +++ b/dist/deno/runtime/pbes2kw.ts @@ -0,0 +1,78 @@ +import type { Pbes2KWDecryptFunction, Pbes2KWEncryptFunction } from './interfaces.d.ts' +import random from './random.ts' +import { p2s as concatSalt } from '../lib/buffer_utils.ts' +import { encode as base64url } from './base64url.ts' +import { wrap, unwrap } from './aeskw.ts' +import checkP2s from '../lib/check_p2s.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +function getCryptoKey(key: unknown, alg: string) { + if (key instanceof Uint8Array) { + return crypto.subtle.importKey('raw', key, 'PBKDF2', false, ['deriveBits']) + } + + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, 'deriveBits', 'deriveKey') + return key + } + + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')) +} + +async function deriveKey(p2s: Uint8Array, alg: string, p2c: number, key: unknown) { + checkP2s(p2s) + + const salt = concatSalt(alg, p2s) + const keylen = parseInt(alg.slice(13, 16), 10) + const subtleAlg = { + hash: `SHA-${alg.slice(8, 11)}`, + iterations: p2c, + name: 'PBKDF2', + salt, + } + const wrapAlg = { + length: keylen, + name: 'AES-KW', + } + + const cryptoKey = await getCryptoKey(key, alg) + + if (cryptoKey.usages.includes('deriveBits')) { + return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen)) + } + + if (cryptoKey.usages.includes('deriveKey')) { + return crypto.subtle.deriveKey(subtleAlg, cryptoKey, wrapAlg, false, ['wrapKey', 'unwrapKey']) + } + + throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"') +} + +export const encrypt: Pbes2KWEncryptFunction = async ( + alg: string, + key: unknown, + cek: Uint8Array, + p2c: number = 2048, + p2s: Uint8Array = random(new Uint8Array(16)), +) => { + const derived = await deriveKey(p2s, alg, p2c, key) + + const encryptedKey = await wrap(alg.slice(-6), derived, cek) + + return { encryptedKey, p2c, p2s: base64url(p2s) } +} + +export const decrypt: Pbes2KWDecryptFunction = async ( + alg: string, + key: unknown, + encryptedKey: Uint8Array, + p2c: number, + p2s: Uint8Array, +) => { + const derived = await deriveKey(p2s, alg, p2c, key) + + return unwrap(alg.slice(-6), derived, encryptedKey) +} diff --git a/dist/deno/runtime/random.ts b/dist/deno/runtime/random.ts new file mode 100644 index 0000000000..b885796ffb --- /dev/null +++ b/dist/deno/runtime/random.ts @@ -0,0 +1,3 @@ +import crypto from './webcrypto.ts' + +export default crypto.getRandomValues.bind(crypto) diff --git a/dist/deno/runtime/rsaes.ts b/dist/deno/runtime/rsaes.ts new file mode 100644 index 0000000000..e5e4bd514e --- /dev/null +++ b/dist/deno/runtime/rsaes.ts @@ -0,0 +1,64 @@ +import type { RsaEsDecryptFunction, RsaEsEncryptFunction } from './interfaces.d.ts' +import subtleAlgorithm from './subtle_rsaes.ts' +import bogusWebCrypto from './bogus.ts' +import crypto, { isCryptoKey } from './webcrypto.ts' +import { checkEncCryptoKey } from '../lib/crypto_key.ts' +import checkKeyLength from './check_key_length.ts' +import invalidKeyInput from '../lib/invalid_key_input.ts' +import { types } from './is_key_like.ts' + +export const encrypt: RsaEsEncryptFunction = async (alg: string, key: unknown, cek: Uint8Array) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + checkEncCryptoKey(key, alg, 'encrypt', 'wrapKey') + checkKeyLength(alg, key) + + if (key.usages.includes('encrypt')) { + return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek)) + } + + if (key.usages.includes('wrapKey')) { + // we're importing the cek to end up with CryptoKey instance that can be wrapped, the algorithm used is irrelevant + const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, ...bogusWebCrypto) + return new Uint8Array( + await crypto.subtle.wrapKey('raw', cryptoKeyCek, key, subtleAlgorithm(alg)), + ) + } + + throw new TypeError( + 'RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation', + ) +} + +export const decrypt: RsaEsDecryptFunction = async ( + alg: string, + key: unknown, + encryptedKey: Uint8Array, +) => { + if (!isCryptoKey(key)) { + throw new TypeError(invalidKeyInput(key, ...types)) + } + checkEncCryptoKey(key, alg, 'decrypt', 'unwrapKey') + checkKeyLength(alg, key) + + if (key.usages.includes('decrypt')) { + return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey)) + } + + if (key.usages.includes('unwrapKey')) { + const cryptoKeyCek = await crypto.subtle.unwrapKey( + 'raw', + encryptedKey, + key, + subtleAlgorithm(alg), + ...bogusWebCrypto, + ) + + return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek)) + } + + throw new TypeError( + 'RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation', + ) +} diff --git a/dist/deno/runtime/sign.ts b/dist/deno/runtime/sign.ts new file mode 100644 index 0000000000..8a0c5af1b6 --- /dev/null +++ b/dist/deno/runtime/sign.ts @@ -0,0 +1,18 @@ +import type { SignFunction } from './interfaces.d.ts' +import subtleAlgorithm from './subtle_dsa.ts' +import crypto from './webcrypto.ts' +import checkKeyLength from './check_key_length.ts' +import getSignKey from './get_sign_verify_key.ts' + +const sign: SignFunction = async (alg, key: unknown, data) => { + const cryptoKey = await getSignKey(alg, key, 'sign') + checkKeyLength(alg, cryptoKey) + const signature = await crypto.subtle.sign( + subtleAlgorithm(alg, cryptoKey.algorithm), + cryptoKey, + data, + ) + return new Uint8Array(signature) +} + +export default sign diff --git a/dist/deno/runtime/subtle_dsa.ts b/dist/deno/runtime/subtle_dsa.ts new file mode 100644 index 0000000000..db40dc693e --- /dev/null +++ b/dist/deno/runtime/subtle_dsa.ts @@ -0,0 +1,35 @@ +import { isCloudflareWorkers } from './env.ts' +import { JOSENotSupported } from '../util/errors.ts' + +export default function subtleDsa(alg: string, algorithm: KeyAlgorithm | EcKeyAlgorithm) { + const hash = `SHA-${alg.slice(-3)}` + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + return { hash, name: 'HMAC' } + case 'PS256': + case 'PS384': + case 'PS512': + // @ts-expect-error + return { hash, name: 'RSA-PSS', saltLength: alg.slice(-3) >> 3 } + case 'RS256': + case 'RS384': + case 'RS512': + return { hash, name: 'RSASSA-PKCS1-v1_5' } + case 'ES256': + case 'ES384': + case 'ES512': + return { hash, name: 'ECDSA', namedCurve: (algorithm).namedCurve } + case 'EdDSA': + if (isCloudflareWorkers() && algorithm.name === 'NODE-ED25519') { + return { name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' } + } + + return { name: algorithm.name } + default: + throw new JOSENotSupported( + `alg ${alg} is not supported either by JOSE or your javascript runtime`, + ) + } +} diff --git a/dist/deno/runtime/subtle_rsaes.ts b/dist/deno/runtime/subtle_rsaes.ts new file mode 100644 index 0000000000..16fb3d3c59 --- /dev/null +++ b/dist/deno/runtime/subtle_rsaes.ts @@ -0,0 +1,15 @@ +import { JOSENotSupported } from '../util/errors.ts' + +export default function subtleRsaEs(alg: string) { + switch (alg) { + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + return 'RSA-OAEP' + default: + throw new JOSENotSupported( + `alg ${alg} is not supported either by JOSE or your javascript runtime`, + ) + } +} diff --git a/dist/deno/runtime/timing_safe_equal.ts b/dist/deno/runtime/timing_safe_equal.ts new file mode 100644 index 0000000000..53bc719686 --- /dev/null +++ b/dist/deno/runtime/timing_safe_equal.ts @@ -0,0 +1,23 @@ +import type { TimingSafeEqual } from './interfaces.d.ts' + +const timingSafeEqual: TimingSafeEqual = (a, b) => { + if (!(a instanceof Uint8Array)) { + throw new TypeError('First argument must be a buffer') + } + if (!(b instanceof Uint8Array)) { + throw new TypeError('Second argument must be a buffer') + } + if (a.length !== b.length) { + throw new TypeError('Input buffers must have the same length') + } + + const len = a.length + let out = 0 + let i = -1 + while (++i < len) { + out |= a[i] ^ b[i] + } + return out === 0 +} + +export default timingSafeEqual diff --git a/dist/deno/runtime/verify.ts b/dist/deno/runtime/verify.ts new file mode 100644 index 0000000000..c904d9422e --- /dev/null +++ b/dist/deno/runtime/verify.ts @@ -0,0 +1,18 @@ +import type { VerifyFunction } from './interfaces.d.ts' +import subtleAlgorithm from './subtle_dsa.ts' +import crypto from './webcrypto.ts' +import checkKeyLength from './check_key_length.ts' +import getVerifyKey from './get_sign_verify_key.ts' + +const verify: VerifyFunction = async (alg, key: unknown, signature, data) => { + const cryptoKey = await getVerifyKey(alg, key, 'verify') + checkKeyLength(alg, cryptoKey) + const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm) + try { + return await crypto.subtle.verify(algorithm, cryptoKey, signature, data) + } catch { + return false + } +} + +export default verify diff --git a/dist/deno/runtime/webcrypto.ts b/dist/deno/runtime/webcrypto.ts new file mode 100644 index 0000000000..fd524e1fe4 --- /dev/null +++ b/dist/deno/runtime/webcrypto.ts @@ -0,0 +1,3 @@ +export default crypto + +export const isCryptoKey = (key: unknown): key is CryptoKey => key instanceof CryptoKey diff --git a/dist/deno/runtime/zlib.ts b/dist/deno/runtime/zlib.ts new file mode 100644 index 0000000000..653d1b419f --- /dev/null +++ b/dist/deno/runtime/zlib.ts @@ -0,0 +1,13 @@ +import { JOSENotSupported } from '../util/errors.ts' +import type { InflateFunction, DeflateFunction } from '../types.d.ts' + +export const inflate: InflateFunction = async () => { + throw new JOSENotSupported( + 'JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `inflateRaw` decrypt option to provide Inflate Raw implementation.', + ) +} +export const deflate: DeflateFunction = async () => { + throw new JOSENotSupported( + 'JWE "zip" (Compression Algorithm) Header Parameter is not supported by your javascript runtime. You need to use the `deflateRaw` encrypt option to provide Deflate Raw implementation.', + ) +} diff --git a/dist/deno/types.d.ts b/dist/deno/types.d.ts new file mode 100644 index 0000000000..f5b6820701 --- /dev/null +++ b/dist/deno/types.d.ts @@ -0,0 +1,572 @@ +/** + * KeyLike are runtime-specific classes representing asymmetric keys or symmetric secrets. These are + * instances of {@link https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey CryptoKey} and + * additionally {@link https://nodejs.org/api/crypto.html#class-keyobject KeyObject} in Node.js + * runtime. + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array Uint8Array} + * instances are also accepted as symmetric secret representation only. + * + * [Key Import Functions](../modules/key_import.md) can be used to import PEM, or JWK formatted + * asymmetric keys and certificates to these runtime-specific representations. + * + * In Node.js the {@link https://nodejs.org/api/buffer.html#buffer Buffer} class is a subclass of + * Uint8Array and so Buffer can be provided for symmetric secrets as well. + * + * {@link https://nodejs.org/api/crypto.html#class-keyobject KeyObject} is a representation of a + * key/secret available in the Node.js runtime. In addition to the import functions of this library + * you may use the runtime APIs + * {@link https://nodejs.org/api/crypto.html#cryptocreatepublickeykey crypto.createPublicKey}, + * {@link https://nodejs.org/api/crypto.html#cryptocreateprivatekeykey crypto.createPrivateKey}, and + * {@link https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding crypto.createSecretKey} + * to obtain a `KeyObject` from your existing key material. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey CryptoKey} is a representation + * of a key/secret available in the Browser and Web-interoperable runtimes. In addition to the + * import functions of this library you may use the + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey SubtleCrypto.importKey} + * API to obtain a CryptoKey from your existing key material. + * + */ +export type KeyLike = { type: string } + +/** + * JSON Web Key ({@link https://www.rfc-editor.org/rfc/rfc7517 JWK}). "RSA", "EC", "OKP", and "oct" + * key types are supported. + */ +export interface JWK { + /** JWK "alg" (Algorithm) Parameter. */ + alg?: string + crv?: string + d?: string + dp?: string + dq?: string + e?: string + /** JWK "ext" (Extractable) Parameter. */ + ext?: boolean + k?: string + /** JWK "key_ops" (Key Operations) Parameter. */ + key_ops?: string[] + /** JWK "kid" (Key ID) Parameter. */ + kid?: string + /** JWK "kty" (Key Type) Parameter. */ + kty?: string + n?: string + oth?: Array<{ + d?: string + r?: string + t?: string + }> + p?: string + q?: string + qi?: string + /** JWK "use" (Public Key Use) Parameter. */ + use?: string + x?: string + y?: string + /** JWK "x5c" (X.509 Certificate Chain) Parameter. */ + x5c?: string[] + /** JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter. */ + x5t?: string + /** "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter. */ + 'x5t#S256'?: string + /** JWK "x5u" (X.509 URL) Parameter. */ + x5u?: string + + [propName: string]: unknown +} + +/** + * Generic Interface for consuming operations dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * If you cannot match a key suitable for the token, throw an error instead. + * + * @param protectedHeader JWE or JWS Protected Header. + * @param token The consumed JWE or JWS token. + */ +export interface GetKeyFunction { + (protectedHeader: T, token: T2): Promise | KeyLike | Uint8Array +} + +/** + * Flattened JWS definition for verify function inputs, allows payload as Uint8Array for detached + * signature validation. + */ +export interface FlattenedJWSInput { + /** + * The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS + * Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented + * as an unencoded JSON object, rather than as a string. These Header Parameter values are not + * integrity protected. + */ + header?: JWSHeaderParameters + + /** + * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 + * "b64": false is used the value passed may also be a Uint8Array. + */ + payload: string | Uint8Array + + /** + * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected + * Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These + * Header Parameter values are integrity protected. + */ + protected?: string + + /** The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). */ + signature: string +} + +/** + * General JWS definition for verify function inputs, allows payload as Uint8Array for detached + * signature validation. + */ +export interface GeneralJWSInput { + /** + * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when + * JWS Unencoded Payload ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) "b64": false is + * used the value passed may also be a Uint8Array. + */ + payload: string | Uint8Array + + /** + * The "signatures" member value MUST be an array of JSON objects. Each object represents a + * signature or MAC over the JWS Payload and the JWS Protected Header. + */ + signatures: Omit[] +} + +/** + * Flattened JWS definition. Payload is returned as an empty string when JWS Unencoded Payload + * ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. + */ +export interface FlattenedJWS extends Partial { + payload: string + signature: string +} + +/** + * General JWS definition. Payload is returned as an empty string when JWS Unencoded Payload + * ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. + */ +export interface GeneralJWS { + payload: string + signatures: Omit[] +} + +export interface JoseHeaderParameters { + /** "kid" (Key ID) Header Parameter. */ + kid?: string + + /** "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter. */ + x5t?: string + + /** "x5c" (X.509 Certificate Chain) Header Parameter. */ + x5c?: string[] + + /** "x5u" (X.509 URL) Header Parameter. */ + x5u?: string + + /** "jku" (JWK Set URL) Header Parameter. */ + jku?: string + + /** "jwk" (JSON Web Key) Header Parameter. */ + jwk?: Pick + + /** "typ" (Type) Header Parameter. */ + typ?: string + + /** "cty" (Content Type) Header Parameter. */ + cty?: string +} + +/** Recognized JWS Header Parameters, any other Header Members may also be present. */ +export interface JWSHeaderParameters extends JoseHeaderParameters { + /** JWS "alg" (Algorithm) Header Parameter. */ + alg?: string + + /** + * This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing + * Input computation as per {@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}. + */ + b64?: boolean + + /** JWS "crit" (Critical) Header Parameter. */ + crit?: string[] + + /** Any other JWS Header member. */ + [propName: string]: unknown +} + +/** Recognized JWE Key Management-related Header Parameters. */ +export interface JWEKeyManagementHeaderParameters { + apu?: Uint8Array + apv?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + p2c?: number + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + p2s?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + iv?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + epk?: KeyLike +} + +/** Flattened JWE definition. */ +export interface FlattenedJWE { + /** + * The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD + * value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a + * base64url-encoded value to be integrity protected but not encrypted. + */ + aad?: string + + /** The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). */ + ciphertext: string + + /** + * The "encrypted_key" member MUST be present and contain the value BASE64URL(JWE Encrypted Key) + * when the JWE Encrypted Key value is non-empty; otherwise, it MUST be absent. + */ + encrypted_key?: string + + /** + * The "header" member MUST be present and contain the value JWE Per- Recipient Unprotected Header + * when the JWE Per-Recipient Unprotected Header value is non-empty; otherwise, it MUST be absent. + * This value is represented as an unencoded JSON object, rather than as a string. These Header + * Parameter values are not integrity protected. + */ + header?: JWEHeaderParameters + + /** + * The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when + * the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent. + */ + iv: string + + /** + * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected + * Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These + * Header Parameter values are integrity protected. + */ + protected?: string + + /** + * The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when + * the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent. + */ + tag: string + + /** + * The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header + * when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This + * value is represented as an unencoded JSON object, rather than as a string. These Header + * Parameter values are not integrity protected. + */ + unprotected?: JWEHeaderParameters +} + +export interface GeneralJWE extends Omit { + recipients: Pick[] +} + +/** Recognized JWE Header Parameters, any other Header members may also be present. */ +export interface JWEHeaderParameters extends JoseHeaderParameters { + /** JWE "alg" (Algorithm) Header Parameter. */ + alg?: string + + /** JWE "enc" (Encryption Algorithm) Header Parameter. */ + enc?: string + + /** JWE "crit" (Critical) Header Parameter. */ + crit?: string[] + + /** JWE "zip" (Compression Algorithm) Header Parameter. */ + zip?: string + + /** Any other JWE Header member. */ + [propName: string]: unknown +} + +/** Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. */ +export interface CritOption { + /** + * An object with keys representing recognized "crit" (Critical) Header Parameter names. The value + * for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity + * protected, `false` when it's irrelevant. + * + * This makes the "Extension Header Parameter "..." is not recognized" error go away. + * + * Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" + * (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically + * correct when provided and that it is optionally integrity protected. It will not process the + * Header Parameter in any way or reject the operation if it is missing. You MUST still verify the + * Header Parameter was present and process it according to the profile's validation steps after + * the operation succeeds. + * + * The JWS extension Header Parameter `b64` is always recognized and processed properly. No other + * registered Header Parameters that need this kind of default built-in treatment are currently + * available. + */ + crit?: { + [propName: string]: boolean + } +} + +/** JWE Decryption options. */ +export interface DecryptOptions extends CritOption { + /** A list of accepted JWE "alg" (Algorithm) Header Parameter values. */ + keyManagementAlgorithms?: string[] + + /** + * A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all + * "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed. + */ + contentEncryptionAlgorithms?: string[] + + /** + * In a browser runtime you have to provide an implementation for Inflate Raw when you expect JWEs + * with compressed plaintext. + */ + inflateRaw?: InflateFunction + + /** + * (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter + * value. The PBKDF2 iteration count defines the algorithm's computational expense. By default + * this value is set to 10000. + */ + maxPBES2Count?: number +} + +/** JWE Deflate option. */ +export interface DeflateOption { + /** + * In a browser runtime you have to provide an implementation for Deflate Raw when you will be + * producing JWEs with compressed plaintext. + */ + deflateRaw?: DeflateFunction +} + +/** JWE Encryption options. */ +export interface EncryptOptions extends CritOption, DeflateOption {} + +/** JWT Claims Set verification options. */ +export interface JWTClaimVerificationOptions { + /** Expected JWT "aud" (Audience) Claim value(s). */ + audience?: string | string[] + + /** + * Expected clock tolerance + * + * - In seconds when number (e.g. 5) + * - Parsed as seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). + */ + clockTolerance?: string | number + + /** Expected JWT "iss" (Issuer) Claim value(s). */ + issuer?: string | string[] + + /** + * Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. + * + * - In seconds when number (e.g. 5) + * - Parsed as seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). + */ + maxTokenAge?: string | number + + /** Expected JWT "sub" (Subject) Claim value. */ + subject?: string + + /** Expected JWT "typ" (Type) Header Parameter value. */ + typ?: string + + /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */ + currentDate?: Date +} + +/** JWS Verification options. */ +export interface VerifyOptions extends CritOption { + /** + * A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg" + * (Algorithm) values applicable for the used key/secret are allowed. Note: "none" is never + * accepted. + */ + algorithms?: string[] +} + +/** JWS Signing options. */ +export interface SignOptions extends CritOption {} + +/** Recognized JWT Claims Set members, any other members may also be present. */ +export interface JWTPayload { + /** + * JWT Issuer + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1 RFC7519#section-4.1.1} + */ + iss?: string + + /** + * JWT Subject + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2 RFC7519#section-4.1.2} + */ + sub?: string + + /** + * JWT Audience + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3 RFC7519#section-4.1.3} + */ + aud?: string | string[] + + /** + * JWT ID + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7 RFC7519#section-4.1.7} + */ + jti?: string + + /** + * JWT Not Before + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5 RFC7519#section-4.1.5} + */ + nbf?: number + + /** + * JWT Expiration Time + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4 RFC7519#section-4.1.4} + */ + exp?: number + + /** + * JWT Issued At + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 RFC7519#section-4.1.6} + */ + iat?: number + + /** Any other JWT Claim Set member. */ + [propName: string]: unknown +} + +/** + * Deflate Raw implementation, e.g. promisified + * {@link https://nodejs.org/api/zlib.html#zlibdeflaterawbuffer-options-callback zlib.deflateRaw}. + */ +export interface DeflateFunction { + (input: Uint8Array): Promise +} + +/** + * Inflate Raw implementation, e.g. promisified + * {@link https://nodejs.org/api/zlib.html#zlibinflaterawbuffer-options-callback zlib.inflateRaw}. + */ +export interface InflateFunction { + (input: Uint8Array): Promise +} + +export interface FlattenedDecryptResult { + /** JWE AAD. */ + additionalAuthenticatedData?: Uint8Array + + /** Plaintext. */ + plaintext: Uint8Array + + /** JWE Protected Header. */ + protectedHeader?: JWEHeaderParameters + + /** JWE Shared Unprotected Header. */ + sharedUnprotectedHeader?: JWEHeaderParameters + + /** JWE Per-Recipient Unprotected Header. */ + unprotectedHeader?: JWEHeaderParameters +} + +export interface GeneralDecryptResult extends FlattenedDecryptResult {} + +export interface CompactDecryptResult { + /** Plaintext. */ + plaintext: Uint8Array + + /** JWE Protected Header. */ + protectedHeader: CompactJWEHeaderParameters +} + +export interface FlattenedVerifyResult { + /** JWS Payload. */ + payload: Uint8Array + + /** JWS Protected Header. */ + protectedHeader?: JWSHeaderParameters + + /** JWS Unprotected Header. */ + unprotectedHeader?: JWSHeaderParameters +} + +export interface GeneralVerifyResult extends FlattenedVerifyResult {} + +export interface CompactVerifyResult { + /** JWS Payload. */ + payload: Uint8Array + + /** JWS Protected Header. */ + protectedHeader: CompactJWSHeaderParameters +} + +export interface JWTVerifyResult { + /** JWT Claims Set. */ + payload: JWTPayload + + /** JWS Protected Header. */ + protectedHeader: JWTHeaderParameters +} + +export interface JWTDecryptResult { + /** JWT Claims Set. */ + payload: JWTPayload + + /** JWE Protected Header. */ + protectedHeader: CompactJWEHeaderParameters +} + +export interface ResolvedKey { + /** Key resolved from the key resolver function. */ + key: T | Uint8Array +} + +/** Recognized Compact JWS Header Parameters, any other Header Members may also be present. */ +export interface CompactJWSHeaderParameters extends JWSHeaderParameters { + alg: string +} + +/** Recognized Signed JWT Header Parameters, any other Header Members may also be present. */ +export interface JWTHeaderParameters extends CompactJWSHeaderParameters { + b64?: true +} + +/** Recognized Compact JWE Header Parameters, any other Header Members may also be present. */ +export interface CompactJWEHeaderParameters extends JWEHeaderParameters { + alg: string + enc: string +} + +/** JSON Web Key Set */ +export interface JSONWebKeySet { + keys: JWK[] +} diff --git a/dist/deno/util/base64url.ts b/dist/deno/util/base64url.ts new file mode 100644 index 0000000000..f3d76163fc --- /dev/null +++ b/dist/deno/util/base64url.ts @@ -0,0 +1,21 @@ +import * as base64url from '../runtime/base64url.ts' + +/** + * Utility function to encode a string or Uint8Array as a base64url string. + * + * @param input Value that will be base64url-encoded. + */ +interface Base64UrlEncode { + (input: Uint8Array | string): string +} +/** + * Utility function to decode a base64url encoded string. + * + * @param input Value that will be base64url-decoded. + */ +interface Base64UrlDecode { + (input: Uint8Array | string): Uint8Array +} + +export const encode: Base64UrlEncode = base64url.encode +export const decode: Base64UrlDecode = base64url.decode diff --git a/dist/deno/util/decode_jwt.ts b/dist/deno/util/decode_jwt.ts new file mode 100644 index 0000000000..e6bf848e82 --- /dev/null +++ b/dist/deno/util/decode_jwt.ts @@ -0,0 +1,42 @@ +import { decode as base64url } from './base64url.ts' +import { decoder } from '../lib/buffer_utils.ts' +import isObject from '../lib/is_object.ts' +import type { JWTPayload } from '../types.d.ts' +import { JWTInvalid } from './errors.ts' + +/** + * Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set types or + * values. This does not validate the JWS Signature. For a proper Signed JWT Claims Set validation + * and JWS signature verification use `jose.jwtVerify()`. For an encrypted JWT Claims Set validation + * and JWE decryption use `jose.jwtDecrypt()`. + * + * @param jwt JWT token in compact JWS serialization. + */ +export function decodeJwt(jwt: string) { + if (typeof jwt !== 'string') + throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string') + + const { 1: payload, length } = jwt.split('.') + + if (length === 5) throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded') + if (length !== 3) throw new JWTInvalid('Invalid JWT') + if (!payload) throw new JWTInvalid('JWTs must contain a payload') + + let decoded: Uint8Array + try { + decoded = base64url(payload) + } catch { + throw new JWTInvalid('Failed to parse the base64url encoded payload') + } + + let result: unknown + try { + result = JSON.parse(decoder.decode(decoded)) + } catch { + throw new JWTInvalid('Failed to parse the decoded payload as JSON') + } + + if (!isObject(result)) throw new JWTInvalid('Invalid JWT Claims Set') + + return result +} diff --git a/dist/deno/util/decode_protected_header.ts b/dist/deno/util/decode_protected_header.ts new file mode 100644 index 0000000000..a9d0c83c6a --- /dev/null +++ b/dist/deno/util/decode_protected_header.ts @@ -0,0 +1,41 @@ +import { decode as base64url } from './base64url.ts' +import { decoder } from '../lib/buffer_utils.ts' +import isObject from '../lib/is_object.ts' +import type { JWSHeaderParameters, JWEHeaderParameters } from '../types.d.ts' + +export type ProtectedHeaderParameters = JWSHeaderParameters & JWEHeaderParameters + +/** + * Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization. + * + * @param token JWE/JWS/JWT token in any JOSE serialization. + */ +export function decodeProtectedHeader(token: string | object) { + let protectedB64u!: unknown + + if (typeof token === 'string') { + const parts = token.split('.') + if (parts.length === 3 || parts.length === 5) { + ;[protectedB64u] = parts + } + } else if (typeof token === 'object' && token) { + if ('protected' in token) { + protectedB64u = token.protected + } else { + throw new TypeError('Token does not contain a Protected Header') + } + } + + try { + if (typeof protectedB64u !== 'string' || !protectedB64u) { + throw new Error() + } + const result = JSON.parse(decoder.decode(base64url(protectedB64u!))) + if (!isObject(result)) { + throw new Error() + } + return result + } catch { + throw new TypeError('Invalid Token or Protected Header formatting') + } +} diff --git a/dist/deno/util/errors.ts b/dist/deno/util/errors.ts new file mode 100644 index 0000000000..b5552f1632 --- /dev/null +++ b/dist/deno/util/errors.ts @@ -0,0 +1,228 @@ +import type { KeyLike } from '../types.d.ts' + +/** + * A generic Error that all other JOSE specific Error subclasses extend. + * + */ +export class JOSEError extends Error { + /** A unique error code for the particular error subclass. */ + static get code(): string { + return 'ERR_JOSE_GENERIC' + } + + /** A unique error code for the particular error subclass. */ + code: string = 'ERR_JOSE_GENERIC' + + constructor(message?: string) { + super(message) + this.name = this.constructor.name + // @ts-ignore + Error.captureStackTrace?.(this, this.constructor) + } +} + +/** + * An error subclass thrown when a JWT Claim Set member validation fails. + * + */ +export class JWTClaimValidationFailed extends JOSEError { + static get code(): 'ERR_JWT_CLAIM_VALIDATION_FAILED' { + return 'ERR_JWT_CLAIM_VALIDATION_FAILED' + } + + code = 'ERR_JWT_CLAIM_VALIDATION_FAILED' + + /** The Claim for which the validation failed. */ + claim: string + + /** Reason code for the validation failure. */ + reason: string + + constructor(message: string, claim = 'unspecified', reason = 'unspecified') { + super(message) + this.claim = claim + this.reason = reason + } +} + +/** + * An error subclass thrown when a JWT is expired. + * + */ +export class JWTExpired extends JOSEError implements JWTClaimValidationFailed { + static get code(): 'ERR_JWT_EXPIRED' { + return 'ERR_JWT_EXPIRED' + } + + code = 'ERR_JWT_EXPIRED' + + /** The Claim for which the validation failed. */ + claim: string + + /** Reason code for the validation failure. */ + reason: string + + constructor(message: string, claim = 'unspecified', reason = 'unspecified') { + super(message) + this.claim = claim + this.reason = reason + } +} + +/** + * An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. + * + */ +export class JOSEAlgNotAllowed extends JOSEError { + static get code(): 'ERR_JOSE_ALG_NOT_ALLOWED' { + return 'ERR_JOSE_ALG_NOT_ALLOWED' + } + + code = 'ERR_JOSE_ALG_NOT_ALLOWED' +} + +/** + * An error subclass thrown when a particular feature or algorithm is not supported by this + * implementation or JOSE in general. + * + */ +export class JOSENotSupported extends JOSEError { + static get code(): 'ERR_JOSE_NOT_SUPPORTED' { + return 'ERR_JOSE_NOT_SUPPORTED' + } + + code = 'ERR_JOSE_NOT_SUPPORTED' +} + +/** + * An error subclass thrown when a JWE ciphertext decryption fails. + * + */ +export class JWEDecryptionFailed extends JOSEError { + static get code(): 'ERR_JWE_DECRYPTION_FAILED' { + return 'ERR_JWE_DECRYPTION_FAILED' + } + + code = 'ERR_JWE_DECRYPTION_FAILED' + + message = 'decryption operation failed' +} + +/** + * An error subclass thrown when a JWE is invalid. + * + */ +export class JWEInvalid extends JOSEError { + static get code(): 'ERR_JWE_INVALID' { + return 'ERR_JWE_INVALID' + } + + code = 'ERR_JWE_INVALID' +} + +/** + * An error subclass thrown when a JWS is invalid. + * + */ +export class JWSInvalid extends JOSEError { + static get code(): 'ERR_JWS_INVALID' { + return 'ERR_JWS_INVALID' + } + + code = 'ERR_JWS_INVALID' +} + +/** + * An error subclass thrown when a JWT is invalid. + * + */ +export class JWTInvalid extends JOSEError { + static get code(): 'ERR_JWT_INVALID' { + return 'ERR_JWT_INVALID' + } + + code = 'ERR_JWT_INVALID' +} + +/** + * An error subclass thrown when a JWK is invalid. + * + */ +export class JWKInvalid extends JOSEError { + static get code(): 'ERR_JWK_INVALID' { + return 'ERR_JWK_INVALID' + } + + code = 'ERR_JWK_INVALID' +} + +/** + * An error subclass thrown when a JWKS is invalid. + * + */ +export class JWKSInvalid extends JOSEError { + static get code(): 'ERR_JWKS_INVALID' { + return 'ERR_JWKS_INVALID' + } + + code = 'ERR_JWKS_INVALID' +} + +/** + * An error subclass thrown when no keys match from a JWKS. + * + */ +export class JWKSNoMatchingKey extends JOSEError { + static get code(): 'ERR_JWKS_NO_MATCHING_KEY' { + return 'ERR_JWKS_NO_MATCHING_KEY' + } + + code = 'ERR_JWKS_NO_MATCHING_KEY' + + message = 'no applicable key found in the JSON Web Key Set' +} + +/** + * An error subclass thrown when multiple keys match from a JWKS. + * + */ +export class JWKSMultipleMatchingKeys extends JOSEError { + /** @ignore */ + [Symbol.asyncIterator]!: () => AsyncIterableIterator + + static get code(): 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' { + return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' + } + + code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' + + message = 'multiple matching keys found in the JSON Web Key Set' +} + +/** + * Timeout was reached when retrieving the JWKS response. + * + */ +export class JWKSTimeout extends JOSEError { + static get code(): 'ERR_JWKS_TIMEOUT' { + return 'ERR_JWKS_TIMEOUT' + } + + code = 'ERR_JWKS_TIMEOUT' + + message = 'request timed out' +} + +/** + * An error subclass thrown when JWS signature verification fails. + * + */ +export class JWSSignatureVerificationFailed extends JOSEError { + static get code(): 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' { + return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' + } + + code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' + + message = 'signature verification failed' +} diff --git a/dist/node/cjs/index.js b/dist/node/cjs/index.js new file mode 100644 index 0000000000..5f41840500 --- /dev/null +++ b/dist/node/cjs/index.js @@ -0,0 +1,65 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeJwt = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.createLocalJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprintUri = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0; +var decrypt_js_1 = require("./jwe/compact/decrypt.js"); +Object.defineProperty(exports, "compactDecrypt", { enumerable: true, get: function () { return decrypt_js_1.compactDecrypt; } }); +var decrypt_js_2 = require("./jwe/flattened/decrypt.js"); +Object.defineProperty(exports, "flattenedDecrypt", { enumerable: true, get: function () { return decrypt_js_2.flattenedDecrypt; } }); +var decrypt_js_3 = require("./jwe/general/decrypt.js"); +Object.defineProperty(exports, "generalDecrypt", { enumerable: true, get: function () { return decrypt_js_3.generalDecrypt; } }); +var encrypt_js_1 = require("./jwe/general/encrypt.js"); +Object.defineProperty(exports, "GeneralEncrypt", { enumerable: true, get: function () { return encrypt_js_1.GeneralEncrypt; } }); +var verify_js_1 = require("./jws/compact/verify.js"); +Object.defineProperty(exports, "compactVerify", { enumerable: true, get: function () { return verify_js_1.compactVerify; } }); +var verify_js_2 = require("./jws/flattened/verify.js"); +Object.defineProperty(exports, "flattenedVerify", { enumerable: true, get: function () { return verify_js_2.flattenedVerify; } }); +var verify_js_3 = require("./jws/general/verify.js"); +Object.defineProperty(exports, "generalVerify", { enumerable: true, get: function () { return verify_js_3.generalVerify; } }); +var verify_js_4 = require("./jwt/verify.js"); +Object.defineProperty(exports, "jwtVerify", { enumerable: true, get: function () { return verify_js_4.jwtVerify; } }); +var decrypt_js_4 = require("./jwt/decrypt.js"); +Object.defineProperty(exports, "jwtDecrypt", { enumerable: true, get: function () { return decrypt_js_4.jwtDecrypt; } }); +var encrypt_js_2 = require("./jwe/compact/encrypt.js"); +Object.defineProperty(exports, "CompactEncrypt", { enumerable: true, get: function () { return encrypt_js_2.CompactEncrypt; } }); +var encrypt_js_3 = require("./jwe/flattened/encrypt.js"); +Object.defineProperty(exports, "FlattenedEncrypt", { enumerable: true, get: function () { return encrypt_js_3.FlattenedEncrypt; } }); +var sign_js_1 = require("./jws/compact/sign.js"); +Object.defineProperty(exports, "CompactSign", { enumerable: true, get: function () { return sign_js_1.CompactSign; } }); +var sign_js_2 = require("./jws/flattened/sign.js"); +Object.defineProperty(exports, "FlattenedSign", { enumerable: true, get: function () { return sign_js_2.FlattenedSign; } }); +var sign_js_3 = require("./jws/general/sign.js"); +Object.defineProperty(exports, "GeneralSign", { enumerable: true, get: function () { return sign_js_3.GeneralSign; } }); +var sign_js_4 = require("./jwt/sign.js"); +Object.defineProperty(exports, "SignJWT", { enumerable: true, get: function () { return sign_js_4.SignJWT; } }); +var encrypt_js_4 = require("./jwt/encrypt.js"); +Object.defineProperty(exports, "EncryptJWT", { enumerable: true, get: function () { return encrypt_js_4.EncryptJWT; } }); +var thumbprint_js_1 = require("./jwk/thumbprint.js"); +Object.defineProperty(exports, "calculateJwkThumbprint", { enumerable: true, get: function () { return thumbprint_js_1.calculateJwkThumbprint; } }); +Object.defineProperty(exports, "calculateJwkThumbprintUri", { enumerable: true, get: function () { return thumbprint_js_1.calculateJwkThumbprintUri; } }); +var embedded_js_1 = require("./jwk/embedded.js"); +Object.defineProperty(exports, "EmbeddedJWK", { enumerable: true, get: function () { return embedded_js_1.EmbeddedJWK; } }); +var local_js_1 = require("./jwks/local.js"); +Object.defineProperty(exports, "createLocalJWKSet", { enumerable: true, get: function () { return local_js_1.createLocalJWKSet; } }); +var remote_js_1 = require("./jwks/remote.js"); +Object.defineProperty(exports, "createRemoteJWKSet", { enumerable: true, get: function () { return remote_js_1.createRemoteJWKSet; } }); +var unsecured_js_1 = require("./jwt/unsecured.js"); +Object.defineProperty(exports, "UnsecuredJWT", { enumerable: true, get: function () { return unsecured_js_1.UnsecuredJWT; } }); +var export_js_1 = require("./key/export.js"); +Object.defineProperty(exports, "exportPKCS8", { enumerable: true, get: function () { return export_js_1.exportPKCS8; } }); +Object.defineProperty(exports, "exportSPKI", { enumerable: true, get: function () { return export_js_1.exportSPKI; } }); +Object.defineProperty(exports, "exportJWK", { enumerable: true, get: function () { return export_js_1.exportJWK; } }); +var import_js_1 = require("./key/import.js"); +Object.defineProperty(exports, "importSPKI", { enumerable: true, get: function () { return import_js_1.importSPKI; } }); +Object.defineProperty(exports, "importPKCS8", { enumerable: true, get: function () { return import_js_1.importPKCS8; } }); +Object.defineProperty(exports, "importX509", { enumerable: true, get: function () { return import_js_1.importX509; } }); +Object.defineProperty(exports, "importJWK", { enumerable: true, get: function () { return import_js_1.importJWK; } }); +var decode_protected_header_js_1 = require("./util/decode_protected_header.js"); +Object.defineProperty(exports, "decodeProtectedHeader", { enumerable: true, get: function () { return decode_protected_header_js_1.decodeProtectedHeader; } }); +var decode_jwt_js_1 = require("./util/decode_jwt.js"); +Object.defineProperty(exports, "decodeJwt", { enumerable: true, get: function () { return decode_jwt_js_1.decodeJwt; } }); +exports.errors = require("./util/errors.js"); +var generate_key_pair_js_1 = require("./key/generate_key_pair.js"); +Object.defineProperty(exports, "generateKeyPair", { enumerable: true, get: function () { return generate_key_pair_js_1.generateKeyPair; } }); +var generate_secret_js_1 = require("./key/generate_secret.js"); +Object.defineProperty(exports, "generateSecret", { enumerable: true, get: function () { return generate_secret_js_1.generateSecret; } }); +exports.base64url = require("./util/base64url.js"); diff --git a/dist/node/cjs/jwe/compact/decrypt.js b/dist/node/cjs/jwe/compact/decrypt.js new file mode 100644 index 0000000000..055b3c4564 --- /dev/null +++ b/dist/node/cjs/jwe/compact/decrypt.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.compactDecrypt = void 0; +const decrypt_js_1 = require("../flattened/decrypt.js"); +const errors_js_1 = require("../../util/errors.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +async function compactDecrypt(jwe, key, options) { + if (jwe instanceof Uint8Array) { + jwe = buffer_utils_js_1.decoder.decode(jwe); + } + if (typeof jwe !== 'string') { + throw new errors_js_1.JWEInvalid('Compact JWE must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length, } = jwe.split('.'); + if (length !== 5) { + throw new errors_js_1.JWEInvalid('Invalid Compact JWE'); + } + const decrypted = await (0, decrypt_js_1.flattenedDecrypt)({ + ciphertext, + iv: (iv || undefined), + protected: protectedHeader || undefined, + tag: (tag || undefined), + encrypted_key: encryptedKey || undefined, + }, key, options); + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} +exports.compactDecrypt = compactDecrypt; diff --git a/dist/node/cjs/jwe/compact/encrypt.js b/dist/node/cjs/jwe/compact/encrypt.js new file mode 100644 index 0000000000..949fbb6df7 --- /dev/null +++ b/dist/node/cjs/jwe/compact/encrypt.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CompactEncrypt = void 0; +const encrypt_js_1 = require("../flattened/encrypt.js"); +class CompactEncrypt { + constructor(plaintext) { + this._flattened = new encrypt_js_1.FlattenedEncrypt(plaintext); + } + setContentEncryptionKey(cek) { + this._flattened.setContentEncryptionKey(cek); + return this; + } + setInitializationVector(iv) { + this._flattened.setInitializationVector(iv); + return this; + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + setKeyManagementParameters(parameters) { + this._flattened.setKeyManagementParameters(parameters); + return this; + } + async encrypt(key, options) { + const jwe = await this._flattened.encrypt(key, options); + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.'); + } +} +exports.CompactEncrypt = CompactEncrypt; diff --git a/dist/node/cjs/jwe/flattened/decrypt.js b/dist/node/cjs/jwe/flattened/decrypt.js new file mode 100644 index 0000000000..fc0ef8d3c6 --- /dev/null +++ b/dist/node/cjs/jwe/flattened/decrypt.js @@ -0,0 +1,141 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.flattenedDecrypt = void 0; +const base64url_js_1 = require("../../runtime/base64url.js"); +const decrypt_js_1 = require("../../runtime/decrypt.js"); +const zlib_js_1 = require("../../runtime/zlib.js"); +const errors_js_1 = require("../../util/errors.js"); +const is_disjoint_js_1 = require("../../lib/is_disjoint.js"); +const is_object_js_1 = require("../../lib/is_object.js"); +const decrypt_key_management_js_1 = require("../../lib/decrypt_key_management.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +const cek_js_1 = require("../../lib/cek.js"); +const validate_crit_js_1 = require("../../lib/validate_crit.js"); +const validate_algorithms_js_1 = require("../../lib/validate_algorithms.js"); +async function flattenedDecrypt(jwe, key, options) { + var _a; + if (!(0, is_object_js_1.default)(jwe)) { + throw new errors_js_1.JWEInvalid('Flattened JWE must be an object'); + } + if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { + throw new errors_js_1.JWEInvalid('JOSE Header missing'); + } + if (typeof jwe.iv !== 'string') { + throw new errors_js_1.JWEInvalid('JWE Initialization Vector missing or incorrect type'); + } + if (typeof jwe.ciphertext !== 'string') { + throw new errors_js_1.JWEInvalid('JWE Ciphertext missing or incorrect type'); + } + if (typeof jwe.tag !== 'string') { + throw new errors_js_1.JWEInvalid('JWE Authentication Tag missing or incorrect type'); + } + if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { + throw new errors_js_1.JWEInvalid('JWE Protected Header incorrect type'); + } + if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { + throw new errors_js_1.JWEInvalid('JWE Encrypted Key incorrect type'); + } + if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { + throw new errors_js_1.JWEInvalid('JWE AAD incorrect type'); + } + if (jwe.header !== undefined && !(0, is_object_js_1.default)(jwe.header)) { + throw new errors_js_1.JWEInvalid('JWE Shared Unprotected Header incorrect type'); + } + if (jwe.unprotected !== undefined && !(0, is_object_js_1.default)(jwe.unprotected)) { + throw new errors_js_1.JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); + } + let parsedProt; + if (jwe.protected) { + try { + const protectedHeader = (0, base64url_js_1.decode)(jwe.protected); + parsedProt = JSON.parse(buffer_utils_js_1.decoder.decode(protectedHeader)); + } + catch { + throw new errors_js_1.JWEInvalid('JWE Protected Header is invalid'); + } + } + if (!(0, is_disjoint_js_1.default)(parsedProt, jwe.header, jwe.unprotected)) { + throw new errors_js_1.JWEInvalid('JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected, + }; + (0, validate_crit_js_1.default)(errors_js_1.JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + if (joseHeader.zip !== undefined) { + if (!parsedProt || !parsedProt.zip) { + throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new errors_js_1.JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new errors_js_1.JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); + } + if (typeof enc !== 'string' || !enc) { + throw new errors_js_1.JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); + } + const keyManagementAlgorithms = options && (0, validate_algorithms_js_1.default)('keyManagementAlgorithms', options.keyManagementAlgorithms); + const contentEncryptionAlgorithms = options && + (0, validate_algorithms_js_1.default)('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms); + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new errors_js_1.JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new errors_js_1.JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed'); + } + let encryptedKey; + if (jwe.encrypted_key !== undefined) { + encryptedKey = (0, base64url_js_1.decode)(jwe.encrypted_key); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jwe); + resolvedKey = true; + } + let cek; + try { + cek = await (0, decrypt_key_management_js_1.default)(alg, key, encryptedKey, joseHeader, options); + } + catch (err) { + if (err instanceof TypeError || err instanceof errors_js_1.JWEInvalid || err instanceof errors_js_1.JOSENotSupported) { + throw err; + } + cek = (0, cek_js_1.default)(enc); + } + const iv = (0, base64url_js_1.decode)(jwe.iv); + const tag = (0, base64url_js_1.decode)(jwe.tag); + const protectedHeader = buffer_utils_js_1.encoder.encode((_a = jwe.protected) !== null && _a !== void 0 ? _a : ''); + let additionalData; + if (jwe.aad !== undefined) { + additionalData = (0, buffer_utils_js_1.concat)(protectedHeader, buffer_utils_js_1.encoder.encode('.'), buffer_utils_js_1.encoder.encode(jwe.aad)); + } + else { + additionalData = protectedHeader; + } + let plaintext = await (0, decrypt_js_1.default)(enc, cek, (0, base64url_js_1.decode)(jwe.ciphertext), iv, tag, additionalData); + if (joseHeader.zip === 'DEF') { + plaintext = await ((options === null || options === void 0 ? void 0 : options.inflateRaw) || zlib_js_1.inflate)(plaintext); + } + const result = { plaintext }; + if (jwe.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jwe.aad !== undefined) { + result.additionalAuthenticatedData = (0, base64url_js_1.decode)(jwe.aad); + } + if (jwe.unprotected !== undefined) { + result.sharedUnprotectedHeader = jwe.unprotected; + } + if (jwe.header !== undefined) { + result.unprotectedHeader = jwe.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} +exports.flattenedDecrypt = flattenedDecrypt; diff --git a/dist/node/cjs/jwe/flattened/encrypt.js b/dist/node/cjs/jwe/flattened/encrypt.js new file mode 100644 index 0000000000..8a69ccf40a --- /dev/null +++ b/dist/node/cjs/jwe/flattened/encrypt.js @@ -0,0 +1,179 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FlattenedEncrypt = exports.unprotected = void 0; +const base64url_js_1 = require("../../runtime/base64url.js"); +const encrypt_js_1 = require("../../runtime/encrypt.js"); +const zlib_js_1 = require("../../runtime/zlib.js"); +const iv_js_1 = require("../../lib/iv.js"); +const encrypt_key_management_js_1 = require("../../lib/encrypt_key_management.js"); +const errors_js_1 = require("../../util/errors.js"); +const is_disjoint_js_1 = require("../../lib/is_disjoint.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +const validate_crit_js_1 = require("../../lib/validate_crit.js"); +exports.unprotected = Symbol(); +class FlattenedEncrypt { + constructor(plaintext) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError('plaintext must be an instance of Uint8Array'); + } + this._plaintext = plaintext; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._sharedUnprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + async encrypt(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new errors_js_1.JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()'); + } + if (!(0, is_disjoint_js_1.default)(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) { + throw new errors_js_1.JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader, + }; + (0, validate_crit_js_1.default)(errors_js_1.JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new errors_js_1.JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (typeof enc !== 'string' || !enc) { + throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + let encryptedKey; + if (alg === 'dir') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption'); + } + } + else if (alg === 'ECDH-ES') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement'); + } + } + let cek; + { + let parameters; + ({ cek, encryptedKey, parameters } = await (0, encrypt_key_management_js_1.default)(alg, enc, key, this._cek, this._keyManagementParameters)); + if (parameters) { + if (options && exports.unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters); + } + else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters }; + } + } + else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters); + } + else { + this._protectedHeader = { ...this._protectedHeader, ...parameters }; + } + } + } + } + this._iv || (this._iv = (0, iv_js_1.default)(enc)); + let additionalData; + let protectedHeader; + let aadMember; + if (this._protectedHeader) { + protectedHeader = buffer_utils_js_1.encoder.encode((0, base64url_js_1.encode)(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = buffer_utils_js_1.encoder.encode(''); + } + if (this._aad) { + aadMember = (0, base64url_js_1.encode)(this._aad); + additionalData = (0, buffer_utils_js_1.concat)(protectedHeader, buffer_utils_js_1.encoder.encode('.'), buffer_utils_js_1.encoder.encode(aadMember)); + } + else { + additionalData = protectedHeader; + } + let ciphertext; + let tag; + if (joseHeader.zip === 'DEF') { + const deflated = await ((options === null || options === void 0 ? void 0 : options.deflateRaw) || zlib_js_1.deflate)(this._plaintext); + ({ ciphertext, tag } = await (0, encrypt_js_1.default)(enc, deflated, cek, this._iv, additionalData)); + } + else { + ; + ({ ciphertext, tag } = await (0, encrypt_js_1.default)(enc, this._plaintext, cek, this._iv, additionalData)); + } + const jwe = { + ciphertext: (0, base64url_js_1.encode)(ciphertext), + iv: (0, base64url_js_1.encode)(this._iv), + tag: (0, base64url_js_1.encode)(tag), + }; + if (encryptedKey) { + jwe.encrypted_key = (0, base64url_js_1.encode)(encryptedKey); + } + if (aadMember) { + jwe.aad = aadMember; + } + if (this._protectedHeader) { + jwe.protected = buffer_utils_js_1.decoder.decode(protectedHeader); + } + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader; + } + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader; + } + return jwe; + } +} +exports.FlattenedEncrypt = FlattenedEncrypt; diff --git a/dist/node/cjs/jwe/general/decrypt.js b/dist/node/cjs/jwe/general/decrypt.js new file mode 100644 index 0000000000..accb23aa6a --- /dev/null +++ b/dist/node/cjs/jwe/general/decrypt.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generalDecrypt = void 0; +const decrypt_js_1 = require("../flattened/decrypt.js"); +const errors_js_1 = require("../../util/errors.js"); +const is_object_js_1 = require("../../lib/is_object.js"); +async function generalDecrypt(jwe, key, options) { + if (!(0, is_object_js_1.default)(jwe)) { + throw new errors_js_1.JWEInvalid('General JWE must be an object'); + } + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(is_object_js_1.default)) { + throw new errors_js_1.JWEInvalid('JWE Recipients missing or incorrect type'); + } + if (!jwe.recipients.length) { + throw new errors_js_1.JWEInvalid('JWE Recipients has no members'); + } + for (const recipient of jwe.recipients) { + try { + return await (0, decrypt_js_1.flattenedDecrypt)({ + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected, + }, key, options); + } + catch { + } + } + throw new errors_js_1.JWEDecryptionFailed(); +} +exports.generalDecrypt = generalDecrypt; diff --git a/dist/node/cjs/jwe/general/encrypt.js b/dist/node/cjs/jwe/general/encrypt.js new file mode 100644 index 0000000000..3caa084eda --- /dev/null +++ b/dist/node/cjs/jwe/general/encrypt.js @@ -0,0 +1,182 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GeneralEncrypt = void 0; +const encrypt_js_1 = require("../flattened/encrypt.js"); +const errors_js_1 = require("../../util/errors.js"); +const cek_js_1 = require("../../lib/cek.js"); +const is_disjoint_js_1 = require("../../lib/is_disjoint.js"); +const encrypt_key_management_js_1 = require("../../lib/encrypt_key_management.js"); +const base64url_js_1 = require("../../runtime/base64url.js"); +const validate_crit_js_1 = require("../../lib/validate_crit.js"); +class IndividualRecipient { + constructor(enc, key, options) { + this.parent = enc; + this.key = key; + this.options = options; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addRecipient(...args) { + return this.parent.addRecipient(...args); + } + encrypt(...args) { + return this.parent.encrypt(...args); + } + done() { + return this.parent; + } +} +class GeneralEncrypt { + constructor(plaintext) { + this._recipients = []; + this._plaintext = plaintext; + } + addRecipient(key, options) { + const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit }); + this._recipients.push(recipient); + return recipient; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = sharedUnprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + async encrypt(options) { + var _a, _b, _c; + if (!this._recipients.length) { + throw new errors_js_1.JWEInvalid('at least one recipient must be added'); + } + options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw }; + if (this._recipients.length === 1) { + const [recipient] = this._recipients; + const flattened = await new encrypt_js_1.FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .encrypt(recipient.key, { ...recipient.options, ...options }); + let jwe = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag, + }; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + if (flattened.encrypted_key) + jwe.recipients[0].encrypted_key = flattened.encrypted_key; + if (flattened.header) + jwe.recipients[0].header = flattened.header; + return jwe; + } + let enc; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + if (!(0, is_disjoint_js_1.default)(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) { + throw new errors_js_1.JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (alg === 'dir' || alg === 'ECDH-ES') { + throw new errors_js_1.JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient'); + } + if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) { + throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + if (!enc) { + enc = joseHeader.enc; + } + else if (enc !== joseHeader.enc) { + throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients'); + } + (0, validate_crit_js_1.default)(errors_js_1.JWEInvalid, new Map(), recipient.options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + } + } + const cek = (0, cek_js_1.default)(enc); + let jwe = { + ciphertext: '', + iv: '', + recipients: [], + tag: '', + }; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + const target = {}; + jwe.recipients.push(target); + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const p2c = joseHeader.alg.startsWith('PBES2') ? 2048 + i : undefined; + if (i === 0) { + const flattened = await new encrypt_js_1.FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setContentEncryptionKey(cek) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .setKeyManagementParameters({ p2c }) + .encrypt(recipient.key, { + ...recipient.options, + ...options, + [encrypt_js_1.unprotected]: true, + }); + jwe.ciphertext = flattened.ciphertext; + jwe.iv = flattened.iv; + jwe.tag = flattened.tag; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + target.encrypted_key = flattened.encrypted_key; + if (flattened.header) + target.header = flattened.header; + continue; + } + const { encryptedKey, parameters } = await (0, encrypt_key_management_js_1.default)(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || + ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) || + ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c }); + target.encrypted_key = (0, base64url_js_1.encode)(encryptedKey); + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters }; + } + return jwe; + } +} +exports.GeneralEncrypt = GeneralEncrypt; diff --git a/dist/node/cjs/jwk/embedded.js b/dist/node/cjs/jwk/embedded.js new file mode 100644 index 0000000000..47f534fdbb --- /dev/null +++ b/dist/node/cjs/jwk/embedded.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmbeddedJWK = void 0; +const import_js_1 = require("../key/import.js"); +const is_object_js_1 = require("../lib/is_object.js"); +const errors_js_1 = require("../util/errors.js"); +async function EmbeddedJWK(protectedHeader, token) { + const joseHeader = { + ...protectedHeader, + ...token === null || token === void 0 ? void 0 : token.header, + }; + if (!(0, is_object_js_1.default)(joseHeader.jwk)) { + throw new errors_js_1.JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object'); + } + const key = await (0, import_js_1.importJWK)({ ...joseHeader.jwk, ext: true }, joseHeader.alg, true); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new errors_js_1.JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key'); + } + return key; +} +exports.EmbeddedJWK = EmbeddedJWK; diff --git a/dist/node/cjs/jwk/thumbprint.js b/dist/node/cjs/jwk/thumbprint.js new file mode 100644 index 0000000000..d50e2ba357 --- /dev/null +++ b/dist/node/cjs/jwk/thumbprint.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.calculateJwkThumbprintUri = exports.calculateJwkThumbprint = void 0; +const digest_js_1 = require("../runtime/digest.js"); +const base64url_js_1 = require("../runtime/base64url.js"); +const errors_js_1 = require("../util/errors.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const is_object_js_1 = require("../lib/is_object.js"); +const check = (value, description) => { + if (typeof value !== 'string' || !value) { + throw new errors_js_1.JWKInvalid(`${description} missing or invalid`); + } +}; +async function calculateJwkThumbprint(jwk, digestAlgorithm) { + if (!(0, is_object_js_1.default)(jwk)) { + throw new TypeError('JWK must be an object'); + } + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + if (digestAlgorithm !== 'sha256' && + digestAlgorithm !== 'sha384' && + digestAlgorithm !== 'sha512') { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"'); + } + let components; + switch (jwk.kty) { + case 'EC': + check(jwk.crv, '"crv" (Curve) Parameter'); + check(jwk.x, '"x" (X Coordinate) Parameter'); + check(jwk.y, '"y" (Y Coordinate) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; + break; + case 'OKP': + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'); + check(jwk.x, '"x" (Public Key) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; + break; + case 'RSA': + check(jwk.e, '"e" (Exponent) Parameter'); + check(jwk.n, '"n" (Modulus) Parameter'); + components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; + break; + case 'oct': + check(jwk.k, '"k" (Key Value) Parameter'); + components = { k: jwk.k, kty: jwk.kty }; + break; + default: + throw new errors_js_1.JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported'); + } + const data = buffer_utils_js_1.encoder.encode(JSON.stringify(components)); + return (0, base64url_js_1.encode)(await (0, digest_js_1.default)(digestAlgorithm, data)); +} +exports.calculateJwkThumbprint = calculateJwkThumbprint; +async function calculateJwkThumbprintUri(jwk, digestAlgorithm) { + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm); + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`; +} +exports.calculateJwkThumbprintUri = calculateJwkThumbprintUri; diff --git a/dist/node/cjs/jwks/local.js b/dist/node/cjs/jwks/local.js new file mode 100644 index 0000000000..f6a7617bb8 --- /dev/null +++ b/dist/node/cjs/jwks/local.js @@ -0,0 +1,122 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createLocalJWKSet = exports.LocalJWKSet = exports.isJWKSLike = void 0; +const import_js_1 = require("../key/import.js"); +const errors_js_1 = require("../util/errors.js"); +const is_object_js_1 = require("../lib/is_object.js"); +function getKtyFromAlg(alg) { + switch (typeof alg === 'string' && alg.slice(0, 2)) { + case 'RS': + case 'PS': + return 'RSA'; + case 'ES': + return 'EC'; + case 'Ed': + return 'OKP'; + default: + throw new errors_js_1.JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set'); + } +} +function isJWKSLike(jwks) { + return (jwks && + typeof jwks === 'object' && + Array.isArray(jwks.keys) && + jwks.keys.every(isJWKLike)); +} +exports.isJWKSLike = isJWKSLike; +function isJWKLike(key) { + return (0, is_object_js_1.default)(key); +} +function clone(obj) { + if (typeof structuredClone === 'function') { + return structuredClone(obj); + } + return JSON.parse(JSON.stringify(obj)); +} +class LocalJWKSet { + constructor(jwks) { + this._cached = new WeakMap(); + if (!isJWKSLike(jwks)) { + throw new errors_js_1.JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = clone(jwks); + } + async getKey(protectedHeader, token) { + const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header }; + const kty = getKtyFromAlg(alg); + const candidates = this._jwks.keys.filter((jwk) => { + let candidate = kty === jwk.kty; + if (candidate && typeof kid === 'string') { + candidate = kid === jwk.kid; + } + if (candidate && typeof jwk.alg === 'string') { + candidate = alg === jwk.alg; + } + if (candidate && typeof jwk.use === 'string') { + candidate = jwk.use === 'sig'; + } + if (candidate && Array.isArray(jwk.key_ops)) { + candidate = jwk.key_ops.includes('verify'); + } + if (candidate && alg === 'EdDSA') { + candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448'; + } + if (candidate) { + switch (alg) { + case 'ES256': + candidate = jwk.crv === 'P-256'; + break; + case 'ES256K': + candidate = jwk.crv === 'secp256k1'; + break; + case 'ES384': + candidate = jwk.crv === 'P-384'; + break; + case 'ES512': + candidate = jwk.crv === 'P-521'; + break; + } + } + return candidate; + }); + const { 0: jwk, length } = candidates; + if (length === 0) { + throw new errors_js_1.JWKSNoMatchingKey(); + } + else if (length !== 1) { + const error = new errors_js_1.JWKSMultipleMatchingKeys(); + const { _cached } = this; + error[Symbol.asyncIterator] = async function* () { + for (const jwk of candidates) { + try { + yield await importWithAlgCache(_cached, jwk, alg); + } + catch { + continue; + } + } + }; + throw error; + } + return importWithAlgCache(this._cached, jwk, alg); + } +} +exports.LocalJWKSet = LocalJWKSet; +async function importWithAlgCache(cache, jwk, alg) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk); + if (cached[alg] === undefined) { + const key = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, alg); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new errors_js_1.JWKSInvalid('JSON Web Key Set members must be public keys'); + } + cached[alg] = key; + } + return cached[alg]; +} +function createLocalJWKSet(jwks) { + const set = new LocalJWKSet(jwks); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} +exports.createLocalJWKSet = createLocalJWKSet; diff --git a/dist/node/cjs/jwks/remote.js b/dist/node/cjs/jwks/remote.js new file mode 100644 index 0000000000..cf09a0aae9 --- /dev/null +++ b/dist/node/cjs/jwks/remote.js @@ -0,0 +1,76 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createRemoteJWKSet = void 0; +const fetch_jwks_js_1 = require("../runtime/fetch_jwks.js"); +const env_js_1 = require("../runtime/env.js"); +const errors_js_1 = require("../util/errors.js"); +const local_js_1 = require("./local.js"); +class RemoteJWKSet extends local_js_1.LocalJWKSet { + constructor(url, options) { + super({ keys: [] }); + this._jwks = undefined; + if (!(url instanceof URL)) { + throw new TypeError('url must be an instance of URL'); + } + this._url = new URL(url.href); + this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers }; + this._timeoutDuration = + typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; + this._cooldownDuration = + typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 30000; + this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === 'number' ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 600000; + } + coolingDown() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cooldownDuration + : false; + } + fresh() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cacheMaxAge + : false; + } + async getKey(protectedHeader, token) { + if (!this._jwks || !this.fresh()) { + await this.reload(); + } + try { + return await super.getKey(protectedHeader, token); + } + catch (err) { + if (err instanceof errors_js_1.JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload(); + return super.getKey(protectedHeader, token); + } + } + throw err; + } + } + async reload() { + if (this._pendingFetch && (0, env_js_1.isCloudflareWorkers)()) { + this._pendingFetch = undefined; + } + this._pendingFetch || (this._pendingFetch = (0, fetch_jwks_js_1.default)(this._url, this._timeoutDuration, this._options) + .then((json) => { + if (!(0, local_js_1.isJWKSLike)(json)) { + throw new errors_js_1.JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = { keys: json.keys }; + this._jwksTimestamp = Date.now(); + this._pendingFetch = undefined; + }) + .catch((err) => { + this._pendingFetch = undefined; + throw err; + })); + await this._pendingFetch; + } +} +function createRemoteJWKSet(url, options) { + const set = new RemoteJWKSet(url, options); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} +exports.createRemoteJWKSet = createRemoteJWKSet; diff --git a/dist/node/cjs/jws/compact/sign.js b/dist/node/cjs/jws/compact/sign.js new file mode 100644 index 0000000000..e960ade473 --- /dev/null +++ b/dist/node/cjs/jws/compact/sign.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CompactSign = void 0; +const sign_js_1 = require("../flattened/sign.js"); +class CompactSign { + constructor(payload) { + this._flattened = new sign_js_1.FlattenedSign(payload); + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + async sign(key, options) { + const jws = await this._flattened.sign(key, options); + if (jws.payload === undefined) { + throw new TypeError('use the flattened module for creating JWS with b64: false'); + } + return `${jws.protected}.${jws.payload}.${jws.signature}`; + } +} +exports.CompactSign = CompactSign; diff --git a/dist/node/cjs/jws/compact/verify.js b/dist/node/cjs/jws/compact/verify.js new file mode 100644 index 0000000000..b7e672477f --- /dev/null +++ b/dist/node/cjs/jws/compact/verify.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.compactVerify = void 0; +const verify_js_1 = require("../flattened/verify.js"); +const errors_js_1 = require("../../util/errors.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +async function compactVerify(jws, key, options) { + if (jws instanceof Uint8Array) { + jws = buffer_utils_js_1.decoder.decode(jws); + } + if (typeof jws !== 'string') { + throw new errors_js_1.JWSInvalid('Compact JWS must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); + if (length !== 3) { + throw new errors_js_1.JWSInvalid('Invalid Compact JWS'); + } + const verified = await (0, verify_js_1.flattenedVerify)({ payload, protected: protectedHeader, signature }, key, options); + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} +exports.compactVerify = compactVerify; diff --git a/dist/node/cjs/jws/flattened/sign.js b/dist/node/cjs/jws/flattened/sign.js new file mode 100644 index 0000000000..2281e97cf3 --- /dev/null +++ b/dist/node/cjs/jws/flattened/sign.js @@ -0,0 +1,85 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FlattenedSign = void 0; +const base64url_js_1 = require("../../runtime/base64url.js"); +const sign_js_1 = require("../../runtime/sign.js"); +const is_disjoint_js_1 = require("../../lib/is_disjoint.js"); +const errors_js_1 = require("../../util/errors.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +const check_key_type_js_1 = require("../../lib/check_key_type.js"); +const validate_crit_js_1 = require("../../lib/validate_crit.js"); +class FlattenedSign { + constructor(payload) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError('payload must be an instance of Uint8Array'); + } + this._payload = payload; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + async sign(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new errors_js_1.JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); + } + if (!(0, is_disjoint_js_1.default)(this._protectedHeader, this._unprotectedHeader)) { + throw new errors_js_1.JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + }; + const extensions = (0, validate_crit_js_1.default)(errors_js_1.JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = this._protectedHeader.b64; + if (typeof b64 !== 'boolean') { + throw new errors_js_1.JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + (0, check_key_type_js_1.default)(alg, key, 'sign'); + let payload = this._payload; + if (b64) { + payload = buffer_utils_js_1.encoder.encode((0, base64url_js_1.encode)(payload)); + } + let protectedHeader; + if (this._protectedHeader) { + protectedHeader = buffer_utils_js_1.encoder.encode((0, base64url_js_1.encode)(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = buffer_utils_js_1.encoder.encode(''); + } + const data = (0, buffer_utils_js_1.concat)(protectedHeader, buffer_utils_js_1.encoder.encode('.'), payload); + const signature = await (0, sign_js_1.default)(alg, key, data); + const jws = { + signature: (0, base64url_js_1.encode)(signature), + payload: '', + }; + if (b64) { + jws.payload = buffer_utils_js_1.decoder.decode(payload); + } + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader; + } + if (this._protectedHeader) { + jws.protected = buffer_utils_js_1.decoder.decode(protectedHeader); + } + return jws; + } +} +exports.FlattenedSign = FlattenedSign; diff --git a/dist/node/cjs/jws/flattened/verify.js b/dist/node/cjs/jws/flattened/verify.js new file mode 100644 index 0000000000..5f0c3930e1 --- /dev/null +++ b/dist/node/cjs/jws/flattened/verify.js @@ -0,0 +1,108 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.flattenedVerify = void 0; +const base64url_js_1 = require("../../runtime/base64url.js"); +const verify_js_1 = require("../../runtime/verify.js"); +const errors_js_1 = require("../../util/errors.js"); +const buffer_utils_js_1 = require("../../lib/buffer_utils.js"); +const is_disjoint_js_1 = require("../../lib/is_disjoint.js"); +const is_object_js_1 = require("../../lib/is_object.js"); +const check_key_type_js_1 = require("../../lib/check_key_type.js"); +const validate_crit_js_1 = require("../../lib/validate_crit.js"); +const validate_algorithms_js_1 = require("../../lib/validate_algorithms.js"); +async function flattenedVerify(jws, key, options) { + var _a; + if (!(0, is_object_js_1.default)(jws)) { + throw new errors_js_1.JWSInvalid('Flattened JWS must be an object'); + } + if (jws.protected === undefined && jws.header === undefined) { + throw new errors_js_1.JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); + } + if (jws.protected !== undefined && typeof jws.protected !== 'string') { + throw new errors_js_1.JWSInvalid('JWS Protected Header incorrect type'); + } + if (jws.payload === undefined) { + throw new errors_js_1.JWSInvalid('JWS Payload missing'); + } + if (typeof jws.signature !== 'string') { + throw new errors_js_1.JWSInvalid('JWS Signature missing or incorrect type'); + } + if (jws.header !== undefined && !(0, is_object_js_1.default)(jws.header)) { + throw new errors_js_1.JWSInvalid('JWS Unprotected Header incorrect type'); + } + let parsedProt = {}; + if (jws.protected) { + try { + const protectedHeader = (0, base64url_js_1.decode)(jws.protected); + parsedProt = JSON.parse(buffer_utils_js_1.decoder.decode(protectedHeader)); + } + catch { + throw new errors_js_1.JWSInvalid('JWS Protected Header is invalid'); + } + } + if (!(0, is_disjoint_js_1.default)(parsedProt, jws.header)) { + throw new errors_js_1.JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jws.header, + }; + const extensions = (0, validate_crit_js_1.default)(errors_js_1.JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = parsedProt.b64; + if (typeof b64 !== 'boolean') { + throw new errors_js_1.JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + const algorithms = options && (0, validate_algorithms_js_1.default)('algorithms', options.algorithms); + if (algorithms && !algorithms.has(alg)) { + throw new errors_js_1.JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (b64) { + if (typeof jws.payload !== 'string') { + throw new errors_js_1.JWSInvalid('JWS Payload must be a string'); + } + } + else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) { + throw new errors_js_1.JWSInvalid('JWS Payload must be a string or an Uint8Array instance'); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jws); + resolvedKey = true; + } + (0, check_key_type_js_1.default)(alg, key, 'verify'); + const data = (0, buffer_utils_js_1.concat)(buffer_utils_js_1.encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ''), buffer_utils_js_1.encoder.encode('.'), typeof jws.payload === 'string' ? buffer_utils_js_1.encoder.encode(jws.payload) : jws.payload); + const signature = (0, base64url_js_1.decode)(jws.signature); + const verified = await (0, verify_js_1.default)(alg, key, signature, data); + if (!verified) { + throw new errors_js_1.JWSSignatureVerificationFailed(); + } + let payload; + if (b64) { + payload = (0, base64url_js_1.decode)(jws.payload); + } + else if (typeof jws.payload === 'string') { + payload = buffer_utils_js_1.encoder.encode(jws.payload); + } + else { + payload = jws.payload; + } + const result = { payload }; + if (jws.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jws.header !== undefined) { + result.unprotectedHeader = jws.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} +exports.flattenedVerify = flattenedVerify; diff --git a/dist/node/cjs/jws/general/sign.js b/dist/node/cjs/jws/general/sign.js new file mode 100644 index 0000000000..b5362cffa5 --- /dev/null +++ b/dist/node/cjs/jws/general/sign.js @@ -0,0 +1,71 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GeneralSign = void 0; +const sign_js_1 = require("../flattened/sign.js"); +const errors_js_1 = require("../../util/errors.js"); +class IndividualSignature { + constructor(sig, key, options) { + this.parent = sig; + this.key = key; + this.options = options; + } + setProtectedHeader(protectedHeader) { + if (this.protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this.protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addSignature(...args) { + return this.parent.addSignature(...args); + } + sign(...args) { + return this.parent.sign(...args); + } + done() { + return this.parent; + } +} +class GeneralSign { + constructor(payload) { + this._signatures = []; + this._payload = payload; + } + addSignature(key, options) { + const signature = new IndividualSignature(this, key, options); + this._signatures.push(signature); + return signature; + } + async sign() { + if (!this._signatures.length) { + throw new errors_js_1.JWSInvalid('at least one signature must be added'); + } + const jws = { + signatures: [], + payload: '', + }; + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i]; + const flattened = new sign_js_1.FlattenedSign(this._payload); + flattened.setProtectedHeader(signature.protectedHeader); + flattened.setUnprotectedHeader(signature.unprotectedHeader); + const { payload, ...rest } = await flattened.sign(signature.key, signature.options); + if (i === 0) { + jws.payload = payload; + } + else if (jws.payload !== payload) { + throw new errors_js_1.JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)'); + } + jws.signatures.push(rest); + } + return jws; + } +} +exports.GeneralSign = GeneralSign; diff --git a/dist/node/cjs/jws/general/verify.js b/dist/node/cjs/jws/general/verify.js new file mode 100644 index 0000000000..9d4a07a296 --- /dev/null +++ b/dist/node/cjs/jws/general/verify.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generalVerify = void 0; +const verify_js_1 = require("../flattened/verify.js"); +const errors_js_1 = require("../../util/errors.js"); +const is_object_js_1 = require("../../lib/is_object.js"); +async function generalVerify(jws, key, options) { + if (!(0, is_object_js_1.default)(jws)) { + throw new errors_js_1.JWSInvalid('General JWS must be an object'); + } + if (!Array.isArray(jws.signatures) || !jws.signatures.every(is_object_js_1.default)) { + throw new errors_js_1.JWSInvalid('JWS Signatures missing or incorrect type'); + } + for (const signature of jws.signatures) { + try { + return await (0, verify_js_1.flattenedVerify)({ + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature, + }, key, options); + } + catch { + } + } + throw new errors_js_1.JWSSignatureVerificationFailed(); +} +exports.generalVerify = generalVerify; diff --git a/dist/node/cjs/jwt/decrypt.js b/dist/node/cjs/jwt/decrypt.js new file mode 100644 index 0000000000..3539ceab53 --- /dev/null +++ b/dist/node/cjs/jwt/decrypt.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jwtDecrypt = void 0; +const decrypt_js_1 = require("../jwe/compact/decrypt.js"); +const jwt_claims_set_js_1 = require("../lib/jwt_claims_set.js"); +const errors_js_1 = require("../util/errors.js"); +async function jwtDecrypt(jwt, key, options) { + const decrypted = await (0, decrypt_js_1.compactDecrypt)(jwt, key, options); + const payload = (0, jwt_claims_set_js_1.default)(decrypted.protectedHeader, decrypted.plaintext, options); + const { protectedHeader } = decrypted; + if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { + throw new errors_js_1.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); + } + if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { + throw new errors_js_1.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); + } + if (protectedHeader.aud !== undefined && + JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { + throw new errors_js_1.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); + } + const result = { payload, protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} +exports.jwtDecrypt = jwtDecrypt; diff --git a/dist/node/cjs/jwt/encrypt.js b/dist/node/cjs/jwt/encrypt.js new file mode 100644 index 0000000000..8ce18f1e73 --- /dev/null +++ b/dist/node/cjs/jwt/encrypt.js @@ -0,0 +1,72 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EncryptJWT = void 0; +const encrypt_js_1 = require("../jwe/compact/encrypt.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const produce_js_1 = require("./produce.js"); +class EncryptJWT extends produce_js_1.ProduceJWT { + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true; + return this; + } + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true; + return this; + } + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true; + return this; + } + async encrypt(key, options) { + const enc = new encrypt_js_1.CompactEncrypt(buffer_utils_js_1.encoder.encode(JSON.stringify(this._payload))); + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss }; + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub }; + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud }; + } + enc.setProtectedHeader(this._protectedHeader); + if (this._iv) { + enc.setInitializationVector(this._iv); + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek); + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters); + } + return enc.encrypt(key, options); + } +} +exports.EncryptJWT = EncryptJWT; diff --git a/dist/node/cjs/jwt/produce.js b/dist/node/cjs/jwt/produce.js new file mode 100644 index 0000000000..54d9ccb134 --- /dev/null +++ b/dist/node/cjs/jwt/produce.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ProduceJWT = void 0; +const epoch_js_1 = require("../lib/epoch.js"); +const is_object_js_1 = require("../lib/is_object.js"); +const secs_js_1 = require("../lib/secs.js"); +class ProduceJWT { + constructor(payload) { + if (!(0, is_object_js_1.default)(payload)) { + throw new TypeError('JWT Claims Set MUST be an object'); + } + this._payload = payload; + } + setIssuer(issuer) { + this._payload = { ...this._payload, iss: issuer }; + return this; + } + setSubject(subject) { + this._payload = { ...this._payload, sub: subject }; + return this; + } + setAudience(audience) { + this._payload = { ...this._payload, aud: audience }; + return this; + } + setJti(jwtId) { + this._payload = { ...this._payload, jti: jwtId }; + return this; + } + setNotBefore(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, nbf: input }; + } + else { + this._payload = { ...this._payload, nbf: (0, epoch_js_1.default)(new Date()) + (0, secs_js_1.default)(input) }; + } + return this; + } + setExpirationTime(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, exp: input }; + } + else { + this._payload = { ...this._payload, exp: (0, epoch_js_1.default)(new Date()) + (0, secs_js_1.default)(input) }; + } + return this; + } + setIssuedAt(input) { + if (typeof input === 'undefined') { + this._payload = { ...this._payload, iat: (0, epoch_js_1.default)(new Date()) }; + } + else { + this._payload = { ...this._payload, iat: input }; + } + return this; + } +} +exports.ProduceJWT = ProduceJWT; diff --git a/dist/node/cjs/jwt/sign.js b/dist/node/cjs/jwt/sign.js new file mode 100644 index 0000000000..ad9193c13a --- /dev/null +++ b/dist/node/cjs/jwt/sign.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SignJWT = void 0; +const sign_js_1 = require("../jws/compact/sign.js"); +const errors_js_1 = require("../util/errors.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const produce_js_1 = require("./produce.js"); +class SignJWT extends produce_js_1.ProduceJWT { + setProtectedHeader(protectedHeader) { + this._protectedHeader = protectedHeader; + return this; + } + async sign(key, options) { + var _a; + const sig = new sign_js_1.CompactSign(buffer_utils_js_1.encoder.encode(JSON.stringify(this._payload))); + sig.setProtectedHeader(this._protectedHeader); + if (Array.isArray((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.crit) && + this._protectedHeader.crit.includes('b64') && + this._protectedHeader.b64 === false) { + throw new errors_js_1.JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + return sig.sign(key, options); + } +} +exports.SignJWT = SignJWT; diff --git a/dist/node/cjs/jwt/unsecured.js b/dist/node/cjs/jwt/unsecured.js new file mode 100644 index 0000000000..482782fed1 --- /dev/null +++ b/dist/node/cjs/jwt/unsecured.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UnsecuredJWT = void 0; +const base64url = require("../runtime/base64url.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const errors_js_1 = require("../util/errors.js"); +const jwt_claims_set_js_1 = require("../lib/jwt_claims_set.js"); +const produce_js_1 = require("./produce.js"); +class UnsecuredJWT extends produce_js_1.ProduceJWT { + encode() { + const header = base64url.encode(JSON.stringify({ alg: 'none' })); + const payload = base64url.encode(JSON.stringify(this._payload)); + return `${header}.${payload}.`; + } + static decode(jwt, options) { + if (typeof jwt !== 'string') { + throw new errors_js_1.JWTInvalid('Unsecured JWT must be a string'); + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.'); + if (length !== 3 || signature !== '') { + throw new errors_js_1.JWTInvalid('Invalid Unsecured JWT'); + } + let header; + try { + header = JSON.parse(buffer_utils_js_1.decoder.decode(base64url.decode(encodedHeader))); + if (header.alg !== 'none') + throw new Error(); + } + catch { + throw new errors_js_1.JWTInvalid('Invalid Unsecured JWT'); + } + const payload = (0, jwt_claims_set_js_1.default)(header, base64url.decode(encodedPayload), options); + return { payload, header }; + } +} +exports.UnsecuredJWT = UnsecuredJWT; diff --git a/dist/node/cjs/jwt/verify.js b/dist/node/cjs/jwt/verify.js new file mode 100644 index 0000000000..d77bb96096 --- /dev/null +++ b/dist/node/cjs/jwt/verify.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jwtVerify = void 0; +const verify_js_1 = require("../jws/compact/verify.js"); +const jwt_claims_set_js_1 = require("../lib/jwt_claims_set.js"); +const errors_js_1 = require("../util/errors.js"); +async function jwtVerify(jwt, key, options) { + var _a; + const verified = await (0, verify_js_1.compactVerify)(jwt, key, options); + if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes('b64')) && verified.protectedHeader.b64 === false) { + throw new errors_js_1.JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + const payload = (0, jwt_claims_set_js_1.default)(verified.protectedHeader, verified.payload, options); + const result = { payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} +exports.jwtVerify = jwtVerify; diff --git a/dist/node/cjs/key/export.js b/dist/node/cjs/key/export.js new file mode 100644 index 0000000000..1c083efe72 --- /dev/null +++ b/dist/node/cjs/key/export.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.exportJWK = exports.exportPKCS8 = exports.exportSPKI = void 0; +const asn1_js_1 = require("../runtime/asn1.js"); +const asn1_js_2 = require("../runtime/asn1.js"); +const key_to_jwk_js_1 = require("../runtime/key_to_jwk.js"); +async function exportSPKI(key) { + return (0, asn1_js_1.toSPKI)(key); +} +exports.exportSPKI = exportSPKI; +async function exportPKCS8(key) { + return (0, asn1_js_2.toPKCS8)(key); +} +exports.exportPKCS8 = exportPKCS8; +async function exportJWK(key) { + return (0, key_to_jwk_js_1.default)(key); +} +exports.exportJWK = exportJWK; diff --git a/dist/node/cjs/key/generate_key_pair.js b/dist/node/cjs/key/generate_key_pair.js new file mode 100644 index 0000000000..3d14da5c3e --- /dev/null +++ b/dist/node/cjs/key/generate_key_pair.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateKeyPair = void 0; +const generate_js_1 = require("../runtime/generate.js"); +async function generateKeyPair(alg, options) { + return (0, generate_js_1.generateKeyPair)(alg, options); +} +exports.generateKeyPair = generateKeyPair; diff --git a/dist/node/cjs/key/generate_secret.js b/dist/node/cjs/key/generate_secret.js new file mode 100644 index 0000000000..5f3c9ba214 --- /dev/null +++ b/dist/node/cjs/key/generate_secret.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateSecret = void 0; +const generate_js_1 = require("../runtime/generate.js"); +async function generateSecret(alg, options) { + return (0, generate_js_1.generateSecret)(alg, options); +} +exports.generateSecret = generateSecret; diff --git a/dist/node/cjs/key/import.js b/dist/node/cjs/key/import.js new file mode 100644 index 0000000000..e12b288006 --- /dev/null +++ b/dist/node/cjs/key/import.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.importJWK = exports.importPKCS8 = exports.importX509 = exports.importSPKI = void 0; +const base64url_js_1 = require("../runtime/base64url.js"); +const asn1_js_1 = require("../runtime/asn1.js"); +const jwk_to_key_js_1 = require("../runtime/jwk_to_key.js"); +const errors_js_1 = require("../util/errors.js"); +const is_object_js_1 = require("../lib/is_object.js"); +async function importSPKI(spki, alg, options) { + if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) { + throw new TypeError('"spki" must be SPKI formatted string'); + } + return (0, asn1_js_1.fromSPKI)(spki, alg, options); +} +exports.importSPKI = importSPKI; +async function importX509(x509, alg, options) { + if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) { + throw new TypeError('"x509" must be X.509 formatted string'); + } + return (0, asn1_js_1.fromX509)(x509, alg, options); +} +exports.importX509 = importX509; +async function importPKCS8(pkcs8, alg, options) { + if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string'); + } + return (0, asn1_js_1.fromPKCS8)(pkcs8, alg, options); +} +exports.importPKCS8 = importPKCS8; +async function importJWK(jwk, alg, octAsKeyObject) { + var _a; + if (!(0, is_object_js_1.default)(jwk)) { + throw new TypeError('JWK must be an object'); + } + alg || (alg = jwk.alg); + switch (jwk.kty) { + case 'oct': + if (typeof jwk.k !== 'string' || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value'); + } + octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : (octAsKeyObject = jwk.ext !== true); + if (octAsKeyObject) { + return (0, jwk_to_key_js_1.default)({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false }); + } + return (0, base64url_js_1.decode)(jwk.k); + case 'RSA': + if (jwk.oth !== undefined) { + throw new errors_js_1.JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported'); + } + case 'EC': + case 'OKP': + return (0, jwk_to_key_js_1.default)({ ...jwk, alg }); + default: + throw new errors_js_1.JOSENotSupported('Unsupported "kty" (Key Type) Parameter value'); + } +} +exports.importJWK = importJWK; diff --git a/dist/node/cjs/lib/aesgcmkw.js b/dist/node/cjs/lib/aesgcmkw.js new file mode 100644 index 0000000000..c3e61e8eab --- /dev/null +++ b/dist/node/cjs/lib/aesgcmkw.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unwrap = exports.wrap = void 0; +const encrypt_js_1 = require("../runtime/encrypt.js"); +const decrypt_js_1 = require("../runtime/decrypt.js"); +const iv_js_1 = require("./iv.js"); +const base64url_js_1 = require("../runtime/base64url.js"); +async function wrap(alg, key, cek, iv) { + const jweAlgorithm = alg.slice(0, 7); + iv || (iv = (0, iv_js_1.default)(jweAlgorithm)); + const { ciphertext: encryptedKey, tag } = await (0, encrypt_js_1.default)(jweAlgorithm, cek, key, iv, new Uint8Array(0)); + return { encryptedKey, iv: (0, base64url_js_1.encode)(iv), tag: (0, base64url_js_1.encode)(tag) }; +} +exports.wrap = wrap; +async function unwrap(alg, key, encryptedKey, iv, tag) { + const jweAlgorithm = alg.slice(0, 7); + return (0, decrypt_js_1.default)(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)); +} +exports.unwrap = unwrap; diff --git a/dist/node/cjs/lib/buffer_utils.js b/dist/node/cjs/lib/buffer_utils.js new file mode 100644 index 0000000000..7932178142 --- /dev/null +++ b/dist/node/cjs/lib/buffer_utils.js @@ -0,0 +1,60 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.concatKdf = exports.lengthAndInput = exports.uint32be = exports.uint64be = exports.p2s = exports.concat = exports.decoder = exports.encoder = void 0; +const digest_js_1 = require("../runtime/digest.js"); +exports.encoder = new TextEncoder(); +exports.decoder = new TextDecoder(); +const MAX_INT32 = 2 ** 32; +function concat(...buffers) { + const size = buffers.reduce((acc, { length }) => acc + length, 0); + const buf = new Uint8Array(size); + let i = 0; + buffers.forEach((buffer) => { + buf.set(buffer, i); + i += buffer.length; + }); + return buf; +} +exports.concat = concat; +function p2s(alg, p2sInput) { + return concat(exports.encoder.encode(alg), new Uint8Array([0]), p2sInput); +} +exports.p2s = p2s; +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 & 0xff], 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; +} +exports.uint64be = uint64be; +function uint32be(value) { + const buf = new Uint8Array(4); + writeUInt32BE(buf, value); + return buf; +} +exports.uint32be = uint32be; +function lengthAndInput(input) { + return concat(uint32be(input.length), input); +} +exports.lengthAndInput = lengthAndInput; +async function concatKdf(secret, bits, value) { + const iterations = Math.ceil((bits >> 3) / 32); + const res = new Uint8Array(iterations * 32); + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length); + buf.set(uint32be(iter + 1)); + buf.set(secret, 4); + buf.set(value, 4 + secret.length); + res.set(await (0, digest_js_1.default)('sha256', buf), iter * 32); + } + return res.slice(0, bits >> 3); +} +exports.concatKdf = concatKdf; diff --git a/dist/node/cjs/lib/cek.js b/dist/node/cjs/lib/cek.js new file mode 100644 index 0000000000..e4a0133909 --- /dev/null +++ b/dist/node/cjs/lib/cek.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bitLength = void 0; +const errors_js_1 = require("../util/errors.js"); +const random_js_1 = require("../runtime/random.js"); +function bitLength(alg) { + switch (alg) { + case 'A128GCM': + return 128; + case 'A192GCM': + return 192; + case 'A256GCM': + case 'A128CBC-HS256': + return 256; + case 'A192CBC-HS384': + return 384; + case 'A256CBC-HS512': + return 512; + default: + throw new errors_js_1.JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } +} +exports.bitLength = bitLength; +exports.default = (alg) => (0, random_js_1.default)(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/node/cjs/lib/check_iv_length.js b/dist/node/cjs/lib/check_iv_length.js new file mode 100644 index 0000000000..3fbee85bb0 --- /dev/null +++ b/dist/node/cjs/lib/check_iv_length.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +const iv_js_1 = require("./iv.js"); +const checkIvLength = (enc, iv) => { + if (iv.length << 3 !== (0, iv_js_1.bitLength)(enc)) { + throw new errors_js_1.JWEInvalid('Invalid Initialization Vector length'); + } +}; +exports.default = checkIvLength; diff --git a/dist/node/cjs/lib/check_key_type.js b/dist/node/cjs/lib/check_key_type.js new file mode 100644 index 0000000000..5035f81f6b --- /dev/null +++ b/dist/node/cjs/lib/check_key_type.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const invalid_key_input_js_1 = require("./invalid_key_input.js"); +const is_key_like_js_1 = require("../runtime/is_key_like.js"); +const symmetricTypeCheck = (alg, key) => { + if (key instanceof Uint8Array) + return; + if (!(0, is_key_like_js_1.default)(key)) { + throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types, 'Uint8Array')); + } + if (key.type !== 'secret') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for symmetric algorithms must be of type "secret"`); + } +}; +const asymmetricTypeCheck = (alg, key, usage) => { + if (!(0, is_key_like_js_1.default)(key)) { + throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types)); + } + if (key.type === 'secret') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`); + } + if (usage === 'sign' && key.type === 'public') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithm signing must be of type "private"`); + } + if (usage === 'decrypt' && key.type === 'public') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithm decryption must be of type "private"`); + } + if (key.algorithm && usage === 'verify' && key.type === 'private') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithm verifying must be of type "public"`); + } + if (key.algorithm && usage === 'encrypt' && key.type === 'private') { + throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithm encryption must be of type "public"`); + } +}; +const checkKeyType = (alg, key, usage) => { + const symmetric = alg.startsWith('HS') || + alg === 'dir' || + alg.startsWith('PBES2') || + /^A\d{3}(?:GCM)?KW$/.test(alg); + if (symmetric) { + symmetricTypeCheck(alg, key); + } + else { + asymmetricTypeCheck(alg, key, usage); + } +}; +exports.default = checkKeyType; diff --git a/dist/node/cjs/lib/check_p2s.js b/dist/node/cjs/lib/check_p2s.js new file mode 100644 index 0000000000..4bdeaa766c --- /dev/null +++ b/dist/node/cjs/lib/check_p2s.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +function checkP2s(p2s) { + if (!(p2s instanceof Uint8Array) || p2s.length < 8) { + throw new errors_js_1.JWEInvalid('PBES2 Salt Input must be 8 or more octets'); + } +} +exports.default = checkP2s; diff --git a/dist/node/cjs/lib/crypto_key.js b/dist/node/cjs/lib/crypto_key.js new file mode 100644 index 0000000000..59de5e20e3 --- /dev/null +++ b/dist/node/cjs/lib/crypto_key.js @@ -0,0 +1,163 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkEncCryptoKey = exports.checkSigCryptoKey = void 0; +const env_js_1 = require("../runtime/env.js"); +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 getNamedCurve(alg) { + switch (alg) { + case 'ES256': + return 'P-256'; + case 'ES384': + return 'P-384'; + case 'ES512': + return 'P-521'; + default: + throw new Error('unreachable'); + } +} +function checkUsage(key, usages) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = 'CryptoKey does not support this operation, its usages must include '; + if (usages.length > 2) { + const last = usages.pop(); + msg += `one of ${usages.join(', ')}, or ${last}.`; + } + else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.`; + } + else { + msg += `${usages[0]}.`; + } + throw new TypeError(msg); + } +} +function checkSigCryptoKey(key, alg, ...usages) { + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': { + if (!isAlgorithm(key.algorithm, 'HMAC')) + throw unusable('HMAC'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'RS256': + case 'RS384': + case 'RS512': { + if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5')) + throw unusable('RSASSA-PKCS1-v1_5'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'PS256': + case 'PS384': + case 'PS512': { + if (!isAlgorithm(key.algorithm, 'RSA-PSS')) + throw unusable('RSA-PSS'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'EdDSA': { + if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') { + if ((0, env_js_1.isCloudflareWorkers)()) { + if (isAlgorithm(key.algorithm, 'NODE-ED25519')) + break; + throw unusable('Ed25519, Ed448, or NODE-ED25519'); + } + throw unusable('Ed25519 or Ed448'); + } + break; + } + case 'ES256': + case 'ES384': + case 'ES512': { + if (!isAlgorithm(key.algorithm, 'ECDSA')) + throw unusable('ECDSA'); + const expected = getNamedCurve(alg); + const actual = key.algorithm.namedCurve; + if (actual !== expected) + throw unusable(expected, 'algorithm.namedCurve'); + break; + } + default: + throw new TypeError('CryptoKey does not support this operation'); + } + checkUsage(key, usages); +} +exports.checkSigCryptoKey = checkSigCryptoKey; +function checkEncCryptoKey(key, alg, ...usages) { + 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': + case 'X448': + break; + default: + throw unusable('ECDH, X25519, or X448'); + } + 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, usages); +} +exports.checkEncCryptoKey = checkEncCryptoKey; diff --git a/dist/node/cjs/lib/decrypt_key_management.js b/dist/node/cjs/lib/decrypt_key_management.js new file mode 100644 index 0000000000..fb2aa37698 --- /dev/null +++ b/dist/node/cjs/lib/decrypt_key_management.js @@ -0,0 +1,100 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const aeskw_js_1 = require("../runtime/aeskw.js"); +const ECDH = require("../runtime/ecdhes.js"); +const pbes2kw_js_1 = require("../runtime/pbes2kw.js"); +const rsaes_js_1 = require("../runtime/rsaes.js"); +const base64url_js_1 = require("../runtime/base64url.js"); +const errors_js_1 = require("../util/errors.js"); +const cek_js_1 = require("../lib/cek.js"); +const import_js_1 = require("../key/import.js"); +const check_key_type_js_1 = require("./check_key_type.js"); +const is_object_js_1 = require("./is_object.js"); +const aesgcmkw_js_1 = require("./aesgcmkw.js"); +async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) { + (0, check_key_type_js_1.default)(alg, key, 'decrypt'); + switch (alg) { + case 'dir': { + if (encryptedKey !== undefined) + throw new errors_js_1.JWEInvalid('Encountered unexpected JWE Encrypted Key'); + return key; + } + case 'ECDH-ES': + if (encryptedKey !== undefined) + throw new errors_js_1.JWEInvalid('Encountered unexpected JWE Encrypted Key'); + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!(0, is_object_js_1.default)(joseHeader.epk)) + throw new errors_js_1.JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`); + if (!ECDH.ecdhAllowed(key)) + throw new errors_js_1.JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + const epk = await (0, import_js_1.importJWK)(joseHeader.epk, alg); + let partyUInfo; + let partyVInfo; + if (joseHeader.apu !== undefined) { + if (typeof joseHeader.apu !== 'string') + throw new errors_js_1.JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`); + partyUInfo = (0, base64url_js_1.decode)(joseHeader.apu); + } + if (joseHeader.apv !== undefined) { + if (typeof joseHeader.apv !== 'string') + throw new errors_js_1.JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`); + partyVInfo = (0, base64url_js_1.decode)(joseHeader.apv); + } + const sharedSecret = await ECDH.deriveKey(epk, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, alg === 'ECDH-ES' ? (0, cek_js_1.bitLength)(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo); + if (alg === 'ECDH-ES') + return sharedSecret; + if (encryptedKey === undefined) + throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing'); + return (0, aeskw_js_1.unwrap)(alg.slice(-6), sharedSecret, encryptedKey); + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + if (encryptedKey === undefined) + throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing'); + return (0, rsaes_js_1.decrypt)(alg, key, encryptedKey); + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + if (encryptedKey === undefined) + throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.p2c !== 'number') + throw new errors_js_1.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`); + const p2cLimit = (options === null || options === void 0 ? void 0 : options.maxPBES2Count) || 10000; + if (joseHeader.p2c > p2cLimit) + throw new errors_js_1.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`); + if (typeof joseHeader.p2s !== 'string') + throw new errors_js_1.JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`); + return (0, pbes2kw_js_1.decrypt)(alg, key, encryptedKey, joseHeader.p2c, (0, base64url_js_1.decode)(joseHeader.p2s)); + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + if (encryptedKey === undefined) + throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing'); + return (0, aeskw_js_1.unwrap)(alg, key, encryptedKey); + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + if (encryptedKey === undefined) + throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.iv !== 'string') + throw new errors_js_1.JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`); + if (typeof joseHeader.tag !== 'string') + throw new errors_js_1.JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`); + const iv = (0, base64url_js_1.decode)(joseHeader.iv); + const tag = (0, base64url_js_1.decode)(joseHeader.tag); + return (0, aesgcmkw_js_1.unwrap)(alg, key, encryptedKey, iv, tag); + } + default: { + throw new errors_js_1.JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } +} +exports.default = decryptKeyManagement; diff --git a/dist/node/cjs/lib/encrypt_key_management.js b/dist/node/cjs/lib/encrypt_key_management.js new file mode 100644 index 0000000000..709364b064 --- /dev/null +++ b/dist/node/cjs/lib/encrypt_key_management.js @@ -0,0 +1,89 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const aeskw_js_1 = require("../runtime/aeskw.js"); +const ECDH = require("../runtime/ecdhes.js"); +const pbes2kw_js_1 = require("../runtime/pbes2kw.js"); +const rsaes_js_1 = require("../runtime/rsaes.js"); +const base64url_js_1 = require("../runtime/base64url.js"); +const cek_js_1 = require("../lib/cek.js"); +const errors_js_1 = require("../util/errors.js"); +const export_js_1 = require("../key/export.js"); +const check_key_type_js_1 = require("./check_key_type.js"); +const aesgcmkw_js_1 = require("./aesgcmkw.js"); +async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) { + let encryptedKey; + let parameters; + let cek; + (0, check_key_type_js_1.default)(alg, key, 'encrypt'); + switch (alg) { + case 'dir': { + cek = key; + break; + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!ECDH.ecdhAllowed(key)) { + throw new errors_js_1.JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + } + const { apu, apv } = providedParameters; + let { epk: ephemeralKey } = providedParameters; + ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey); + const { x, y, crv, kty } = await (0, export_js_1.exportJWK)(ephemeralKey); + const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? (0, cek_js_1.bitLength)(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv); + parameters = { epk: { x, crv, kty } }; + if (kty === 'EC') + parameters.epk.y = y; + if (apu) + parameters.apu = (0, base64url_js_1.encode)(apu); + if (apv) + parameters.apv = (0, base64url_js_1.encode)(apv); + if (alg === 'ECDH-ES') { + cek = sharedSecret; + break; + } + cek = providedCek || (0, cek_js_1.default)(enc); + const kwAlg = alg.slice(-6); + encryptedKey = await (0, aeskw_js_1.wrap)(kwAlg, sharedSecret, cek); + break; + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + cek = providedCek || (0, cek_js_1.default)(enc); + encryptedKey = await (0, rsaes_js_1.encrypt)(alg, key, cek); + break; + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + cek = providedCek || (0, cek_js_1.default)(enc); + const { p2c, p2s } = providedParameters; + ({ encryptedKey, ...parameters } = await (0, pbes2kw_js_1.encrypt)(alg, key, cek, p2c, p2s)); + break; + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + cek = providedCek || (0, cek_js_1.default)(enc); + encryptedKey = await (0, aeskw_js_1.wrap)(alg, key, cek); + break; + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + cek = providedCek || (0, cek_js_1.default)(enc); + const { iv } = providedParameters; + ({ encryptedKey, ...parameters } = await (0, aesgcmkw_js_1.wrap)(alg, key, cek, iv)); + break; + } + default: { + throw new errors_js_1.JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + return { cek, encryptedKey, parameters }; +} +exports.default = encryptKeyManagement; diff --git a/dist/node/cjs/lib/epoch.js b/dist/node/cjs/lib/epoch.js new file mode 100644 index 0000000000..a0792b4f7f --- /dev/null +++ b/dist/node/cjs/lib/epoch.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = (date) => Math.floor(date.getTime() / 1000); diff --git a/dist/node/cjs/lib/invalid_key_input.js b/dist/node/cjs/lib/invalid_key_input.js new file mode 100644 index 0000000000..e1d5fc1c55 --- /dev/null +++ b/dist/node/cjs/lib/invalid_key_input.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withAlg = void 0; +function message(msg, actual, ...types) { + 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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}`; + } + } + return msg; +} +exports.default = (actual, ...types) => { + return message('Key must be ', actual, ...types); +}; +function withAlg(alg, actual, ...types) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types); +} +exports.withAlg = withAlg; diff --git a/dist/node/cjs/lib/is_disjoint.js b/dist/node/cjs/lib/is_disjoint.js new file mode 100644 index 0000000000..66c2fc6c82 --- /dev/null +++ b/dist/node/cjs/lib/is_disjoint.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const isDisjoint = (...headers) => { + const sources = headers.filter(Boolean); + if (sources.length === 0 || sources.length === 1) { + return true; + } + let acc; + for (const header of sources) { + const parameters = Object.keys(header); + if (!acc || acc.size === 0) { + acc = new Set(parameters); + continue; + } + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false; + } + acc.add(parameter); + } + } + return true; +}; +exports.default = isDisjoint; diff --git a/dist/node/cjs/lib/is_object.js b/dist/node/cjs/lib/is_object.js new file mode 100644 index 0000000000..870938b59b --- /dev/null +++ b/dist/node/cjs/lib/is_object.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function isObjectLike(value) { + return typeof value === 'object' && value !== null; +} +function isObject(input) { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') { + return false; + } + if (Object.getPrototypeOf(input) === null) { + return true; + } + let proto = input; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(input) === proto; +} +exports.default = isObject; diff --git a/dist/node/cjs/lib/iv.js b/dist/node/cjs/lib/iv.js new file mode 100644 index 0000000000..db7d3a5912 --- /dev/null +++ b/dist/node/cjs/lib/iv.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bitLength = void 0; +const errors_js_1 = require("../util/errors.js"); +const random_js_1 = require("../runtime/random.js"); +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 errors_js_1.JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } +} +exports.bitLength = bitLength; +exports.default = (alg) => (0, random_js_1.default)(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/node/cjs/lib/jwt_claims_set.js b/dist/node/cjs/lib/jwt_claims_set.js new file mode 100644 index 0000000000..ce5e0ebbe3 --- /dev/null +++ b/dist/node/cjs/lib/jwt_claims_set.js @@ -0,0 +1,93 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +const buffer_utils_js_1 = require("./buffer_utils.js"); +const epoch_js_1 = require("./epoch.js"); +const secs_js_1 = require("./secs.js"); +const is_object_js_1 = require("./is_object.js"); +const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, ''); +const checkAudiencePresence = (audPayload, audOption) => { + if (typeof audPayload === 'string') { + return audOption.includes(audPayload); + } + if (Array.isArray(audPayload)) { + return audOption.some(Set.prototype.has.bind(new Set(audPayload))); + } + return false; +}; +exports.default = (protectedHeader, encodedPayload, options = {}) => { + const { typ } = options; + if (typ && + (typeof protectedHeader.typ !== 'string' || + normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) { + throw new errors_js_1.JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed'); + } + let payload; + try { + payload = JSON.parse(buffer_utils_js_1.decoder.decode(encodedPayload)); + } + catch { + } + if (!(0, is_object_js_1.default)(payload)) { + throw new errors_js_1.JWTInvalid('JWT Claims Set must be a top-level JSON object'); + } + const { issuer } = options; + if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) { + throw new errors_js_1.JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed'); + } + const { subject } = options; + if (subject && payload.sub !== subject) { + throw new errors_js_1.JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed'); + } + const { audience } = options; + if (audience && + !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) { + throw new errors_js_1.JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed'); + } + let tolerance; + switch (typeof options.clockTolerance) { + case 'string': + tolerance = (0, secs_js_1.default)(options.clockTolerance); + break; + case 'number': + tolerance = options.clockTolerance; + break; + case 'undefined': + tolerance = 0; + break; + default: + throw new TypeError('Invalid clockTolerance option type'); + } + const { currentDate } = options; + const now = (0, epoch_js_1.default)(currentDate || new Date()); + if ((payload.iat !== undefined || options.maxTokenAge) && typeof payload.iat !== 'number') { + throw new errors_js_1.JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); + } + if (payload.nbf !== undefined) { + if (typeof payload.nbf !== 'number') { + throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); + } + if (payload.nbf > now + tolerance) { + throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed'); + } + } + if (payload.exp !== undefined) { + if (typeof payload.exp !== 'number') { + throw new errors_js_1.JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); + } + if (payload.exp <= now - tolerance) { + throw new errors_js_1.JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed'); + } + } + if (options.maxTokenAge) { + const age = now - payload.iat; + const max = typeof options.maxTokenAge === 'number' ? options.maxTokenAge : (0, secs_js_1.default)(options.maxTokenAge); + if (age - tolerance > max) { + throw new errors_js_1.JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed'); + } + if (age < 0 - tolerance) { + throw new errors_js_1.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); + } + } + return payload; +}; diff --git a/dist/node/cjs/lib/secs.js b/dist/node/cjs/lib/secs.js new file mode 100644 index 0000000000..f1b6a0c15d --- /dev/null +++ b/dist/node/cjs/lib/secs.js @@ -0,0 +1,46 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const year = day * 365.25; +const REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i; +exports.default = (str) => { + const matched = REGEX.exec(str); + if (!matched) { + throw new TypeError('Invalid time period format'); + } + const value = parseFloat(matched[1]); + const unit = matched[2].toLowerCase(); + switch (unit) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + case 's': + return Math.round(value); + case 'minute': + case 'minutes': + case 'min': + case 'mins': + case 'm': + return Math.round(value * minute); + case 'hour': + case 'hours': + case 'hr': + case 'hrs': + case 'h': + return Math.round(value * hour); + case 'day': + case 'days': + case 'd': + return Math.round(value * day); + case 'week': + case 'weeks': + case 'w': + return Math.round(value * week); + default: + return Math.round(value * year); + } +}; diff --git a/dist/node/cjs/lib/validate_algorithms.js b/dist/node/cjs/lib/validate_algorithms.js new file mode 100644 index 0000000000..29f4e80df9 --- /dev/null +++ b/dist/node/cjs/lib/validate_algorithms.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const validateAlgorithms = (option, algorithms) => { + if (algorithms !== undefined && + (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) { + throw new TypeError(`"${option}" option must be an array of strings`); + } + if (!algorithms) { + return undefined; + } + return new Set(algorithms); +}; +exports.default = validateAlgorithms; diff --git a/dist/node/cjs/lib/validate_crit.js b/dist/node/cjs/lib/validate_crit.js new file mode 100644 index 0000000000..b0229e3b72 --- /dev/null +++ b/dist/node/cjs/lib/validate_crit.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { + if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); + } + if (!protectedHeader || protectedHeader.crit === undefined) { + return new Set(); + } + if (!Array.isArray(protectedHeader.crit) || + protectedHeader.crit.length === 0 || + protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) { + throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); + } + let recognized; + if (recognizedOption !== undefined) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); + } + else { + recognized = recognizedDefault; + } + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new errors_js_1.JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); + } + if (joseHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`); + } + else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); + } + } + return new Set(protectedHeader.crit); +} +exports.default = validateCrit; diff --git a/dist/node/cjs/runtime/aeskw.js b/dist/node/cjs/runtime/aeskw.js new file mode 100644 index 0000000000..ca26086881 --- /dev/null +++ b/dist/node/cjs/runtime/aeskw.js @@ -0,0 +1,55 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unwrap = exports.wrap = void 0; +const buffer_1 = require("buffer"); +const crypto_1 = require("crypto"); +const errors_js_1 = require("../util/errors.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const ciphers_js_1 = require("./ciphers.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +function checkKeySize(key, alg) { + if (key.symmetricKeySize << 3 !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`); + } +} +function ensureKeyObject(key, alg, usage) { + if ((0, is_key_object_js_1.default)(key)) { + return key; + } + if (key instanceof Uint8Array) { + return (0, crypto_1.createSecretKey)(key); + } + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + (0, crypto_key_js_1.checkEncCryptoKey)(key, alg, usage); + return crypto_1.KeyObject.from(key); + } + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types, 'Uint8Array')); +} +const wrap = (alg, key, cek) => { + const size = parseInt(alg.slice(1, 4), 10); + const algorithm = `aes${size}-wrap`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + const keyObject = ensureKeyObject(key, alg, 'wrapKey'); + checkKeySize(keyObject, alg); + const cipher = (0, crypto_1.createCipheriv)(algorithm, keyObject, buffer_1.Buffer.alloc(8, 0xa6)); + return (0, buffer_utils_js_1.concat)(cipher.update(cek), cipher.final()); +}; +exports.wrap = wrap; +const unwrap = (alg, key, encryptedKey) => { + const size = parseInt(alg.slice(1, 4), 10); + const algorithm = `aes${size}-wrap`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + const keyObject = ensureKeyObject(key, alg, 'unwrapKey'); + checkKeySize(keyObject, alg); + const cipher = (0, crypto_1.createDecipheriv)(algorithm, keyObject, buffer_1.Buffer.alloc(8, 0xa6)); + return (0, buffer_utils_js_1.concat)(cipher.update(encryptedKey), cipher.final()); +}; +exports.unwrap = unwrap; diff --git a/dist/node/cjs/runtime/asn1.js b/dist/node/cjs/runtime/asn1.js new file mode 100644 index 0000000000..3695dae247 --- /dev/null +++ b/dist/node/cjs/runtime/asn1.js @@ -0,0 +1,54 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.fromX509 = exports.fromSPKI = exports.fromPKCS8 = exports.toPKCS8 = exports.toSPKI = void 0; +const crypto_1 = require("crypto"); +const buffer_1 = require("buffer"); +const webcrypto_js_1 = require("./webcrypto.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const genericExport = (keyType, keyFormat, key) => { + let keyObject; + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable'); + } + keyObject = crypto_1.KeyObject.from(key); + } + else if ((0, is_key_object_js_1.default)(key)) { + keyObject = key; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types)); + } + if (keyObject.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`); + } + return keyObject.export({ format: 'pem', type: keyFormat }); +}; +const toSPKI = (key) => { + return genericExport('public', 'spki', key); +}; +exports.toSPKI = toSPKI; +const toPKCS8 = (key) => { + return genericExport('private', 'pkcs8', key); +}; +exports.toPKCS8 = toPKCS8; +const fromPKCS8 = (pem) => (0, crypto_1.createPrivateKey)({ + key: buffer_1.Buffer.from(pem.replace(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, ''), 'base64'), + type: 'pkcs8', + format: 'der', +}); +exports.fromPKCS8 = fromPKCS8; +const fromSPKI = (pem) => (0, crypto_1.createPublicKey)({ + key: buffer_1.Buffer.from(pem.replace(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, ''), 'base64'), + type: 'spki', + format: 'der', +}); +exports.fromSPKI = fromSPKI; +const fromX509 = (pem) => (0, crypto_1.createPublicKey)({ + key: pem, + type: 'spki', + format: 'pem', +}); +exports.fromX509 = fromX509; diff --git a/dist/node/cjs/runtime/asn1_sequence_decoder.js b/dist/node/cjs/runtime/asn1_sequence_decoder.js new file mode 100644 index 0000000000..4f96522335 --- /dev/null +++ b/dist/node/cjs/runtime/asn1_sequence_decoder.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const tagInteger = 0x02; +const tagSequence = 0x30; +class Asn1SequenceDecoder { + constructor(buffer) { + if (buffer[0] !== tagSequence) { + throw new TypeError(); + } + this.buffer = buffer; + this.offset = 1; + const len = this.decodeLength(); + if (len !== buffer.length - this.offset) { + throw new TypeError(); + } + } + decodeLength() { + let length = this.buffer[this.offset++]; + if (length & 0x80) { + const nBytes = length & ~0x80; + length = 0; + for (let i = 0; i < nBytes; i++) + length = (length << 8) | this.buffer[this.offset + i]; + this.offset += nBytes; + } + return length; + } + unsignedInteger() { + if (this.buffer[this.offset++] !== tagInteger) { + throw new TypeError(); + } + let length = this.decodeLength(); + if (this.buffer[this.offset] === 0) { + this.offset++; + length--; + } + const result = this.buffer.slice(this.offset, this.offset + length); + this.offset += length; + return result; + } + end() { + if (this.offset !== this.buffer.length) { + throw new TypeError(); + } + } +} +exports.default = Asn1SequenceDecoder; diff --git a/dist/node/cjs/runtime/asn1_sequence_encoder.js b/dist/node/cjs/runtime/asn1_sequence_encoder.js new file mode 100644 index 0000000000..8b71d6dc28 --- /dev/null +++ b/dist/node/cjs/runtime/asn1_sequence_encoder.js @@ -0,0 +1,91 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const buffer_1 = require("buffer"); +const errors_js_1 = require("../util/errors.js"); +const tagInteger = 0x02; +const tagBitStr = 0x03; +const tagOctStr = 0x04; +const tagSequence = 0x30; +const bZero = buffer_1.Buffer.from([0x00]); +const bTagInteger = buffer_1.Buffer.from([tagInteger]); +const bTagBitStr = buffer_1.Buffer.from([tagBitStr]); +const bTagSequence = buffer_1.Buffer.from([tagSequence]); +const bTagOctStr = buffer_1.Buffer.from([tagOctStr]); +const encodeLength = (len) => { + if (len < 128) + return buffer_1.Buffer.from([len]); + const buffer = buffer_1.Buffer.alloc(5); + buffer.writeUInt32BE(len, 1); + let offset = 1; + while (buffer[offset] === 0) + offset++; + buffer[offset - 1] = 0x80 | (5 - offset); + return buffer.slice(offset - 1); +}; +const oids = new Map([ + ['P-256', buffer_1.Buffer.from('06 08 2A 86 48 CE 3D 03 01 07'.replace(/ /g, ''), 'hex')], + ['secp256k1', buffer_1.Buffer.from('06 05 2B 81 04 00 0A'.replace(/ /g, ''), 'hex')], + ['P-384', buffer_1.Buffer.from('06 05 2B 81 04 00 22'.replace(/ /g, ''), 'hex')], + ['P-521', buffer_1.Buffer.from('06 05 2B 81 04 00 23'.replace(/ /g, ''), 'hex')], + ['ecPublicKey', buffer_1.Buffer.from('06 07 2A 86 48 CE 3D 02 01'.replace(/ /g, ''), 'hex')], + ['X25519', buffer_1.Buffer.from('06 03 2B 65 6E'.replace(/ /g, ''), 'hex')], + ['X448', buffer_1.Buffer.from('06 03 2B 65 6F'.replace(/ /g, ''), 'hex')], + ['Ed25519', buffer_1.Buffer.from('06 03 2B 65 70'.replace(/ /g, ''), 'hex')], + ['Ed448', buffer_1.Buffer.from('06 03 2B 65 71'.replace(/ /g, ''), 'hex')], +]); +class DumbAsn1Encoder { + constructor() { + this.length = 0; + this.elements = []; + } + oidFor(oid) { + const bOid = oids.get(oid); + if (!bOid) { + throw new errors_js_1.JOSENotSupported('Invalid or unsupported OID'); + } + this.elements.push(bOid); + this.length += bOid.length; + } + zero() { + this.elements.push(bTagInteger, buffer_1.Buffer.from([0x01]), bZero); + this.length += 3; + } + one() { + this.elements.push(bTagInteger, buffer_1.Buffer.from([0x01]), buffer_1.Buffer.from([0x01])); + this.length += 3; + } + unsignedInteger(integer) { + if (integer[0] & 0x80) { + const len = encodeLength(integer.length + 1); + this.elements.push(bTagInteger, len, bZero, integer); + this.length += 2 + len.length + integer.length; + } + else { + let i = 0; + while (integer[i] === 0 && (integer[i + 1] & 0x80) === 0) + i++; + const len = encodeLength(integer.length - i); + this.elements.push(bTagInteger, encodeLength(integer.length - i), integer.slice(i)); + this.length += 1 + len.length + integer.length - i; + } + } + octStr(octStr) { + const len = encodeLength(octStr.length); + this.elements.push(bTagOctStr, encodeLength(octStr.length), octStr); + this.length += 1 + len.length + octStr.length; + } + bitStr(bitS) { + const len = encodeLength(bitS.length + 1); + this.elements.push(bTagBitStr, encodeLength(bitS.length + 1), bZero, bitS); + this.length += 1 + len.length + bitS.length + 1; + } + add(seq) { + this.elements.push(seq); + this.length += seq.length; + } + end(tag = bTagSequence) { + const len = encodeLength(this.length); + return buffer_1.Buffer.concat([tag, len, ...this.elements], 1 + len.length + this.length); + } +} +exports.default = DumbAsn1Encoder; diff --git a/dist/node/cjs/runtime/base64url.js b/dist/node/cjs/runtime/base64url.js new file mode 100644 index 0000000000..3a38a4ba74 --- /dev/null +++ b/dist/node/cjs/runtime/base64url.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decode = exports.encode = exports.encodeBase64 = exports.decodeBase64 = void 0; +const buffer_1 = require("buffer"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +let encode; +exports.encode = encode; +function normalize(input) { + let encoded = input; + if (encoded instanceof Uint8Array) { + encoded = buffer_utils_js_1.decoder.decode(encoded); + } + return encoded; +} +if (buffer_1.Buffer.isEncoding('base64url')) { + exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64url'); +} +else { + exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); +} +const decodeBase64 = (input) => buffer_1.Buffer.from(input, 'base64'); +exports.decodeBase64 = decodeBase64; +const encodeBase64 = (input) => buffer_1.Buffer.from(input).toString('base64'); +exports.encodeBase64 = encodeBase64; +const decode = (input) => buffer_1.Buffer.from(normalize(input), 'base64'); +exports.decode = decode; diff --git a/dist/node/cjs/runtime/cbc_tag.js b/dist/node/cjs/runtime/cbc_tag.js new file mode 100644 index 0000000000..03412c2cfb --- /dev/null +++ b/dist/node/cjs/runtime/cbc_tag.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +function cbcTag(aad, iv, ciphertext, macSize, macKey, keySize) { + const macData = (0, buffer_utils_js_1.concat)(aad, iv, ciphertext, (0, buffer_utils_js_1.uint64be)(aad.length << 3)); + const hmac = (0, crypto_1.createHmac)(`sha${macSize}`, macKey); + hmac.update(macData); + return hmac.digest().slice(0, keySize >> 3); +} +exports.default = cbcTag; diff --git a/dist/node/cjs/runtime/check_cek_length.js b/dist/node/cjs/runtime/check_cek_length.js new file mode 100644 index 0000000000..5192e750db --- /dev/null +++ b/dist/node/cjs/runtime/check_cek_length.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const checkCekLength = (enc, cek) => { + let expected; + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + expected = parseInt(enc.slice(-3), 10); + break; + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + expected = parseInt(enc.slice(1, 4), 10); + break; + default: + throw new errors_js_1.JOSENotSupported(`Content Encryption Algorithm ${enc} is not supported either by JOSE or your javascript runtime`); + } + if (cek instanceof Uint8Array) { + const actual = cek.byteLength << 3; + if (actual !== expected) { + throw new errors_js_1.JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } + return; + } + if ((0, is_key_object_js_1.default)(cek) && cek.type === 'secret') { + const actual = cek.symmetricKeySize << 3; + if (actual !== expected) { + throw new errors_js_1.JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } + return; + } + throw new TypeError('Invalid Content Encryption Key type'); +}; +exports.default = checkCekLength; diff --git a/dist/node/cjs/runtime/check_modulus_length.js b/dist/node/cjs/runtime/check_modulus_length.js new file mode 100644 index 0000000000..72c670eba3 --- /dev/null +++ b/dist/node/cjs/runtime/check_modulus_length.js @@ -0,0 +1,52 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setModulusLength = exports.weakMap = void 0; +exports.weakMap = new WeakMap(); +const getLength = (buf, index) => { + let len = buf.readUInt8(1); + if ((len & 0x80) === 0) { + if (index === 0) { + return len; + } + return getLength(buf.subarray(2 + len), index - 1); + } + const num = len & 0x7f; + len = 0; + for (let i = 0; i < num; i++) { + len <<= 8; + const j = buf.readUInt8(2 + i); + len |= j; + } + if (index === 0) { + return len; + } + return getLength(buf.subarray(2 + len), index - 1); +}; +const getLengthOfSeqIndex = (sequence, index) => { + const len = sequence.readUInt8(1); + if ((len & 0x80) === 0) { + return getLength(sequence.subarray(2), index); + } + const num = len & 0x7f; + return getLength(sequence.subarray(2 + num), index); +}; +const getModulusLength = (key) => { + var _a, _b; + if (exports.weakMap.has(key)) { + return exports.weakMap.get(key); + } + const modulusLength = (_b = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.modulusLength) !== null && _b !== void 0 ? _b : (getLengthOfSeqIndex(key.export({ format: 'der', type: 'pkcs1' }), key.type === 'private' ? 1 : 0) - + 1) << + 3; + exports.weakMap.set(key, modulusLength); + return modulusLength; +}; +const setModulusLength = (keyObject, modulusLength) => { + exports.weakMap.set(keyObject, modulusLength); +}; +exports.setModulusLength = setModulusLength; +exports.default = (key, alg) => { + if (getModulusLength(key) < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); + } +}; diff --git a/dist/node/cjs/runtime/ciphers.js b/dist/node/cjs/runtime/ciphers.js new file mode 100644 index 0000000000..e004c84562 --- /dev/null +++ b/dist/node/cjs/runtime/ciphers.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +let ciphers; +exports.default = (algorithm) => { + ciphers || (ciphers = new Set((0, crypto_1.getCiphers)())); + return ciphers.has(algorithm); +}; diff --git a/dist/node/cjs/runtime/decrypt.js b/dist/node/cjs/runtime/decrypt.js new file mode 100644 index 0000000000..6e0de6d472 --- /dev/null +++ b/dist/node/cjs/runtime/decrypt.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const check_iv_length_js_1 = require("../lib/check_iv_length.js"); +const check_cek_length_js_1 = require("./check_cek_length.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const errors_js_1 = require("../util/errors.js"); +const timing_safe_equal_js_1 = require("./timing_safe_equal.js"); +const cbc_tag_js_1 = require("./cbc_tag.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const ciphers_js_1 = require("./ciphers.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + if ((0, is_key_object_js_1.default)(cek)) { + cek = cek.export(); + } + const encKey = cek.subarray(keySize >> 3); + const macKey = cek.subarray(0, keySize >> 3); + const macSize = parseInt(enc.slice(-3), 10); + const algorithm = `aes-${keySize}-cbc`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const expectedTag = (0, cbc_tag_js_1.default)(aad, iv, ciphertext, macSize, macKey, keySize); + let macCheckPassed; + try { + macCheckPassed = (0, timing_safe_equal_js_1.default)(tag, expectedTag); + } + catch { + } + if (!macCheckPassed) { + throw new errors_js_1.JWEDecryptionFailed(); + } + let plaintext; + try { + const decipher = (0, crypto_1.createDecipheriv)(algorithm, encKey, iv); + plaintext = (0, buffer_utils_js_1.concat)(decipher.update(ciphertext), decipher.final()); + } + catch { + } + if (!plaintext) { + throw new errors_js_1.JWEDecryptionFailed(); + } + return plaintext; +} +function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + const algorithm = `aes-${keySize}-gcm`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + try { + const decipher = (0, crypto_1.createDecipheriv)(algorithm, cek, iv, { authTagLength: 16 }); + decipher.setAuthTag(tag); + if (aad.byteLength) { + decipher.setAAD(aad, { plaintextLength: ciphertext.length }); + } + const plaintext = decipher.update(ciphertext); + decipher.final(); + return plaintext; + } + catch { + throw new errors_js_1.JWEDecryptionFailed(); + } +} +const decrypt = (enc, cek, ciphertext, iv, tag, aad) => { + let key; + if ((0, webcrypto_js_1.isCryptoKey)(cek)) { + (0, crypto_key_js_1.checkEncCryptoKey)(cek, enc, 'decrypt'); + key = crypto_1.KeyObject.from(cek); + } + else if (cek instanceof Uint8Array || (0, is_key_object_js_1.default)(cek)) { + key = cek; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(cek, ...is_key_like_js_1.types, 'Uint8Array')); + } + (0, check_cek_length_js_1.default)(enc, key); + (0, check_iv_length_js_1.default)(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + return cbcDecrypt(enc, key, ciphertext, iv, tag, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + return gcmDecrypt(enc, key, ciphertext, iv, tag, aad); + default: + throw new errors_js_1.JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +exports.default = decrypt; diff --git a/dist/node/cjs/runtime/digest.js b/dist/node/cjs/runtime/digest.js new file mode 100644 index 0000000000..13c194a8ed --- /dev/null +++ b/dist/node/cjs/runtime/digest.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const digest = (algorithm, data) => (0, crypto_1.createHash)(algorithm).update(data).digest(); +exports.default = digest; diff --git a/dist/node/cjs/runtime/dsa_digest.js b/dist/node/cjs/runtime/dsa_digest.js new file mode 100644 index 0000000000..fde4883e81 --- /dev/null +++ b/dist/node/cjs/runtime/dsa_digest.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +function dsaDigest(alg) { + switch (alg) { + case 'PS256': + case 'RS256': + case 'ES256': + case 'ES256K': + return 'sha256'; + case 'PS384': + case 'RS384': + case 'ES384': + return 'sha384'; + case 'PS512': + case 'RS512': + case 'ES512': + return 'sha512'; + case 'EdDSA': + return undefined; + default: + throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} +exports.default = dsaDigest; diff --git a/dist/node/cjs/runtime/ecdhes.js b/dist/node/cjs/runtime/ecdhes.js new file mode 100644 index 0000000000..d769af7603 --- /dev/null +++ b/dist/node/cjs/runtime/ecdhes.js @@ -0,0 +1,70 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ecdhAllowed = exports.generateEpk = exports.deriveKey = void 0; +const crypto_1 = require("crypto"); +const util_1 = require("util"); +const get_named_curve_js_1 = require("./get_named_curve.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const errors_js_1 = require("../util/errors.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const generateKeyPair = (0, util_1.promisify)(crypto_1.generateKeyPair); +async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { + let publicKey; + if ((0, webcrypto_js_1.isCryptoKey)(publicKee)) { + (0, crypto_key_js_1.checkEncCryptoKey)(publicKee, 'ECDH'); + publicKey = crypto_1.KeyObject.from(publicKee); + } + else if ((0, is_key_object_js_1.default)(publicKee)) { + publicKey = publicKee; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(publicKee, ...is_key_like_js_1.types)); + } + let privateKey; + if ((0, webcrypto_js_1.isCryptoKey)(privateKee)) { + (0, crypto_key_js_1.checkEncCryptoKey)(privateKee, 'ECDH', 'deriveBits'); + privateKey = crypto_1.KeyObject.from(privateKee); + } + else if ((0, is_key_object_js_1.default)(privateKee)) { + privateKey = privateKee; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(privateKee, ...is_key_like_js_1.types)); + } + const value = (0, buffer_utils_js_1.concat)((0, buffer_utils_js_1.lengthAndInput)(buffer_utils_js_1.encoder.encode(algorithm)), (0, buffer_utils_js_1.lengthAndInput)(apu), (0, buffer_utils_js_1.lengthAndInput)(apv), (0, buffer_utils_js_1.uint32be)(keyLength)); + const sharedSecret = (0, crypto_1.diffieHellman)({ privateKey, publicKey }); + return (0, buffer_utils_js_1.concatKdf)(sharedSecret, keyLength, value); +} +exports.deriveKey = deriveKey; +async function generateEpk(kee) { + let key; + if ((0, webcrypto_js_1.isCryptoKey)(kee)) { + key = crypto_1.KeyObject.from(kee); + } + else if ((0, is_key_object_js_1.default)(kee)) { + key = kee; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(kee, ...is_key_like_js_1.types)); + } + switch (key.asymmetricKeyType) { + case 'x25519': + return generateKeyPair('x25519'); + case 'x448': { + return generateKeyPair('x448'); + } + case 'ec': { + const namedCurve = (0, get_named_curve_js_1.default)(key); + return generateKeyPair('ec', { namedCurve }); + } + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported EPK'); + } +} +exports.generateEpk = generateEpk; +const ecdhAllowed = (key) => ['P-256', 'P-384', 'P-521', 'X25519', 'X448'].includes((0, get_named_curve_js_1.default)(key)); +exports.ecdhAllowed = ecdhAllowed; diff --git a/dist/node/cjs/runtime/encrypt.js b/dist/node/cjs/runtime/encrypt.js new file mode 100644 index 0000000000..940a1ed28b --- /dev/null +++ b/dist/node/cjs/runtime/encrypt.js @@ -0,0 +1,74 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const check_iv_length_js_1 = require("../lib/check_iv_length.js"); +const check_cek_length_js_1 = require("./check_cek_length.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const cbc_tag_js_1 = require("./cbc_tag.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const errors_js_1 = require("../util/errors.js"); +const ciphers_js_1 = require("./ciphers.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +function cbcEncrypt(enc, plaintext, cek, iv, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + if ((0, is_key_object_js_1.default)(cek)) { + cek = cek.export(); + } + const encKey = cek.subarray(keySize >> 3); + const macKey = cek.subarray(0, keySize >> 3); + const algorithm = `aes-${keySize}-cbc`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const cipher = (0, crypto_1.createCipheriv)(algorithm, encKey, iv); + const ciphertext = (0, buffer_utils_js_1.concat)(cipher.update(plaintext), cipher.final()); + const macSize = parseInt(enc.slice(-3), 10); + const tag = (0, cbc_tag_js_1.default)(aad, iv, ciphertext, macSize, macKey, keySize); + return { ciphertext, tag }; +} +function gcmEncrypt(enc, plaintext, cek, iv, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + const algorithm = `aes-${keySize}-gcm`; + if (!(0, ciphers_js_1.default)(algorithm)) { + throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const cipher = (0, crypto_1.createCipheriv)(algorithm, cek, iv, { authTagLength: 16 }); + if (aad.byteLength) { + cipher.setAAD(aad, { plaintextLength: plaintext.length }); + } + const ciphertext = cipher.update(plaintext); + cipher.final(); + const tag = cipher.getAuthTag(); + return { ciphertext, tag }; +} +const encrypt = (enc, plaintext, cek, iv, aad) => { + let key; + if ((0, webcrypto_js_1.isCryptoKey)(cek)) { + (0, crypto_key_js_1.checkEncCryptoKey)(cek, enc, 'encrypt'); + key = crypto_1.KeyObject.from(cek); + } + else if (cek instanceof Uint8Array || (0, is_key_object_js_1.default)(cek)) { + key = cek; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(cek, ...is_key_like_js_1.types, 'Uint8Array')); + } + (0, check_cek_length_js_1.default)(enc, key); + (0, check_iv_length_js_1.default)(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + return cbcEncrypt(enc, plaintext, key, iv, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + return gcmEncrypt(enc, plaintext, key, iv, aad); + default: + throw new errors_js_1.JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +exports.default = encrypt; diff --git a/dist/node/cjs/runtime/env.js b/dist/node/cjs/runtime/env.js new file mode 100644 index 0000000000..e6a6f4acfe --- /dev/null +++ b/dist/node/cjs/runtime/env.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isCloudflareWorkers = void 0; +function isCloudflareWorkers() { + return false; +} +exports.isCloudflareWorkers = isCloudflareWorkers; diff --git a/dist/node/cjs/runtime/fetch_jwks.js b/dist/node/cjs/runtime/fetch_jwks.js new file mode 100644 index 0000000000..b034fd5bb4 --- /dev/null +++ b/dist/node/cjs/runtime/fetch_jwks.js @@ -0,0 +1,45 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const http = require("http"); +const https = require("https"); +const events_1 = require("events"); +const errors_js_1 = require("../util/errors.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const fetchJwks = async (url, timeout, options) => { + let get; + switch (url.protocol) { + case 'https:': + get = https.get; + break; + case 'http:': + get = http.get; + break; + default: + throw new TypeError('Unsupported URL protocol.'); + } + const { agent, headers } = options; + const req = get(url.href, { + agent, + timeout, + headers, + }); + const [response] = (await Promise.race([(0, events_1.once)(req, 'response'), (0, events_1.once)(req, 'timeout')])); + if (!response) { + req.destroy(); + throw new errors_js_1.JWKSTimeout(); + } + if (response.statusCode !== 200) { + throw new errors_js_1.JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response'); + } + const parts = []; + for await (const part of response) { + parts.push(part); + } + try { + return JSON.parse(buffer_utils_js_1.decoder.decode((0, buffer_utils_js_1.concat)(...parts))); + } + catch { + throw new errors_js_1.JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON'); + } +}; +exports.default = fetchJwks; diff --git a/dist/node/cjs/runtime/flags.js b/dist/node/cjs/runtime/flags.js new file mode 100644 index 0000000000..7a4e64dd74 --- /dev/null +++ b/dist/node/cjs/runtime/flags.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jwkImport = exports.jwkExport = exports.rsaPssParams = exports.oneShotCallback = void 0; +const [major, minor] = process.versions.node.split('.').map((str) => parseInt(str, 10)); +exports.oneShotCallback = major >= 16 || (major === 15 && minor >= 13); +exports.rsaPssParams = !('electron' in process.versions) && (major >= 17 || (major === 16 && minor >= 9)); +exports.jwkExport = major >= 16 || (major === 15 && minor >= 9); +exports.jwkImport = major >= 16 || (major === 15 && minor >= 12); diff --git a/dist/node/cjs/runtime/generate.js b/dist/node/cjs/runtime/generate.js new file mode 100644 index 0000000000..38aa92bae3 --- /dev/null +++ b/dist/node/cjs/runtime/generate.js @@ -0,0 +1,105 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateKeyPair = exports.generateSecret = void 0; +const crypto_1 = require("crypto"); +const util_1 = require("util"); +const random_js_1 = require("./random.js"); +const check_modulus_length_js_1 = require("./check_modulus_length.js"); +const errors_js_1 = require("../util/errors.js"); +const generate = (0, util_1.promisify)(crypto_1.generateKeyPair); +async function generateSecret(alg, options) { + let length; + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + length = parseInt(alg.slice(-3), 10); + break; + case 'A128KW': + case 'A192KW': + case 'A256KW': + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + length = parseInt(alg.slice(1, 4), 10); + break; + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + return (0, crypto_1.createSecretKey)((0, random_js_1.default)(new Uint8Array(length >> 3))); +} +exports.generateSecret = generateSecret; +async function generateKeyPair(alg, options) { + var _a, _b; + switch (alg) { + case 'RS256': + case 'RS384': + case 'RS512': + case 'PS256': + case 'PS384': + case 'PS512': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + case 'RSA1_5': { + const modulusLength = (_a = options === null || options === void 0 ? void 0 : options.modulusLength) !== null && _a !== void 0 ? _a : 2048; + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new errors_js_1.JOSENotSupported('Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used'); + } + const keypair = await generate('rsa', { + modulusLength, + publicExponent: 0x10001, + }); + (0, check_modulus_length_js_1.setModulusLength)(keypair.privateKey, modulusLength); + (0, check_modulus_length_js_1.setModulusLength)(keypair.publicKey, modulusLength); + return keypair; + } + case 'ES256': + return generate('ec', { namedCurve: 'P-256' }); + case 'ES256K': + return generate('ec', { namedCurve: 'secp256k1' }); + case 'ES384': + return generate('ec', { namedCurve: 'P-384' }); + case 'ES512': + return generate('ec', { namedCurve: 'P-521' }); + case 'EdDSA': { + switch (options === null || options === void 0 ? void 0 : options.crv) { + case undefined: + case 'Ed25519': + return generate('ed25519'); + case 'Ed448': + return generate('ed448'); + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported crv option provided, supported values are Ed25519 and Ed448'); + } + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + const crv = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : 'P-256'; + switch (crv) { + case undefined: + case 'P-256': + case 'P-384': + case 'P-521': + return generate('ec', { namedCurve: crv }); + case 'X25519': + return generate('x25519'); + case 'X448': + return generate('x448'); + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448'); + } + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } +} +exports.generateKeyPair = generateKeyPair; diff --git a/dist/node/cjs/runtime/get_named_curve.js b/dist/node/cjs/runtime/get_named_curve.js new file mode 100644 index 0000000000..bb46729e3d --- /dev/null +++ b/dist/node/cjs/runtime/get_named_curve.js @@ -0,0 +1,95 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setCurve = exports.weakMap = void 0; +const buffer_1 = require("buffer"); +const crypto_1 = require("crypto"); +const errors_js_1 = require("../util/errors.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const p256 = buffer_1.Buffer.from([42, 134, 72, 206, 61, 3, 1, 7]); +const p384 = buffer_1.Buffer.from([43, 129, 4, 0, 34]); +const p521 = buffer_1.Buffer.from([43, 129, 4, 0, 35]); +const secp256k1 = buffer_1.Buffer.from([43, 129, 4, 0, 10]); +exports.weakMap = new WeakMap(); +const namedCurveToJOSE = (namedCurve) => { + switch (namedCurve) { + case 'prime256v1': + return 'P-256'; + case 'secp384r1': + return 'P-384'; + case 'secp521r1': + return 'P-521'; + case 'secp256k1': + return 'secp256k1'; + default: + throw new errors_js_1.JOSENotSupported('Unsupported key curve for this operation'); + } +}; +const getNamedCurve = (kee, raw) => { + var _a; + let key; + if ((0, webcrypto_js_1.isCryptoKey)(kee)) { + key = crypto_1.KeyObject.from(kee); + } + else if ((0, is_key_object_js_1.default)(kee)) { + key = kee; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(kee, ...is_key_like_js_1.types)); + } + if (key.type === 'secret') { + throw new TypeError('only "private" or "public" type keys can be used for this operation'); + } + switch (key.asymmetricKeyType) { + case 'ed25519': + case 'ed448': + return `Ed${key.asymmetricKeyType.slice(2)}`; + case 'x25519': + case 'x448': + return `X${key.asymmetricKeyType.slice(1)}`; + case 'ec': { + if (exports.weakMap.has(key)) { + return exports.weakMap.get(key); + } + let namedCurve = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.namedCurve; + if (!namedCurve && key.type === 'private') { + namedCurve = getNamedCurve((0, crypto_1.createPublicKey)(key), true); + } + else if (!namedCurve) { + const buf = key.export({ format: 'der', type: 'spki' }); + const i = buf[1] < 128 ? 14 : 15; + const len = buf[i]; + const curveOid = buf.slice(i + 1, i + 1 + len); + if (curveOid.equals(p256)) { + namedCurve = 'prime256v1'; + } + else if (curveOid.equals(p384)) { + namedCurve = 'secp384r1'; + } + else if (curveOid.equals(p521)) { + namedCurve = 'secp521r1'; + } + else if (curveOid.equals(secp256k1)) { + namedCurve = 'secp256k1'; + } + else { + throw new errors_js_1.JOSENotSupported('Unsupported key curve for this operation'); + } + } + if (raw) + return namedCurve; + const curve = namedCurveToJOSE(namedCurve); + exports.weakMap.set(key, curve); + return curve; + } + default: + throw new TypeError('Invalid asymmetric key type for this operation'); + } +}; +function setCurve(keyObject, curve) { + exports.weakMap.set(keyObject, curve); +} +exports.setCurve = setCurve; +exports.default = getNamedCurve; diff --git a/dist/node/cjs/runtime/get_sign_verify_key.js b/dist/node/cjs/runtime/get_sign_verify_key.js new file mode 100644 index 0000000000..fea98c71d5 --- /dev/null +++ b/dist/node/cjs/runtime/get_sign_verify_key.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +function getSignVerifyKey(alg, key, usage) { + if (key instanceof Uint8Array) { + if (!alg.startsWith('HS')) { + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types)); + } + return (0, crypto_1.createSecretKey)(key); + } + if (key instanceof crypto_1.KeyObject) { + return key; + } + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + (0, crypto_key_js_1.checkSigCryptoKey)(key, alg, usage); + return crypto_1.KeyObject.from(key); + } + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types, 'Uint8Array')); +} +exports.default = getSignVerifyKey; diff --git a/dist/node/cjs/runtime/hmac_digest.js b/dist/node/cjs/runtime/hmac_digest.js new file mode 100644 index 0000000000..c3935124c7 --- /dev/null +++ b/dist/node/cjs/runtime/hmac_digest.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const errors_js_1 = require("../util/errors.js"); +function hmacDigest(alg) { + switch (alg) { + case 'HS256': + return 'sha256'; + case 'HS384': + return 'sha384'; + case 'HS512': + return 'sha512'; + default: + throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} +exports.default = hmacDigest; diff --git a/dist/node/cjs/runtime/is_key_like.js b/dist/node/cjs/runtime/is_key_like.js new file mode 100644 index 0000000000..cd157df0a3 --- /dev/null +++ b/dist/node/cjs/runtime/is_key_like.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.types = void 0; +const webcrypto_js_1 = require("./webcrypto.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +exports.default = (key) => (0, is_key_object_js_1.default)(key) || (0, webcrypto_js_1.isCryptoKey)(key); +const types = ['KeyObject']; +exports.types = types; +if (globalThis.CryptoKey || (webcrypto_js_1.default === null || webcrypto_js_1.default === void 0 ? void 0 : webcrypto_js_1.default.CryptoKey)) { + types.push('CryptoKey'); +} diff --git a/dist/node/cjs/runtime/is_key_object.js b/dist/node/cjs/runtime/is_key_object.js new file mode 100644 index 0000000000..54b02cca1b --- /dev/null +++ b/dist/node/cjs/runtime/is_key_object.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const util = require("util"); +exports.default = util.types.isKeyObject + ? (obj) => util.types.isKeyObject(obj) + : (obj) => obj != null && obj instanceof crypto_1.KeyObject; diff --git a/dist/node/cjs/runtime/jwk_to_key.js b/dist/node/cjs/runtime/jwk_to_key.js new file mode 100644 index 0000000000..8a3ead7800 --- /dev/null +++ b/dist/node/cjs/runtime/jwk_to_key.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const buffer_1 = require("buffer"); +const crypto_1 = require("crypto"); +const base64url_js_1 = require("./base64url.js"); +const errors_js_1 = require("../util/errors.js"); +const get_named_curve_js_1 = require("./get_named_curve.js"); +const check_modulus_length_js_1 = require("./check_modulus_length.js"); +const asn1_sequence_encoder_js_1 = require("./asn1_sequence_encoder.js"); +const flags_js_1 = require("./flags.js"); +const parse = (jwk) => { + if (flags_js_1.jwkImport && jwk.kty !== 'oct') { + return jwk.d + ? (0, crypto_1.createPrivateKey)({ format: 'jwk', key: jwk }) + : (0, crypto_1.createPublicKey)({ format: 'jwk', key: jwk }); + } + switch (jwk.kty) { + case 'oct': { + return (0, crypto_1.createSecretKey)((0, base64url_js_1.decode)(jwk.k)); + } + case 'RSA': { + const enc = new asn1_sequence_encoder_js_1.default(); + const isPrivate = jwk.d !== undefined; + const modulus = buffer_1.Buffer.from(jwk.n, 'base64'); + const exponent = buffer_1.Buffer.from(jwk.e, 'base64'); + if (isPrivate) { + enc.zero(); + enc.unsignedInteger(modulus); + enc.unsignedInteger(exponent); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.d, 'base64')); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.p, 'base64')); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.q, 'base64')); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.dp, 'base64')); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.dq, 'base64')); + enc.unsignedInteger(buffer_1.Buffer.from(jwk.qi, 'base64')); + } + else { + enc.unsignedInteger(modulus); + enc.unsignedInteger(exponent); + } + const der = enc.end(); + const createInput = { + key: der, + format: 'der', + type: 'pkcs1', + }; + const keyObject = isPrivate ? (0, crypto_1.createPrivateKey)(createInput) : (0, crypto_1.createPublicKey)(createInput); + (0, check_modulus_length_js_1.setModulusLength)(keyObject, modulus.length << 3); + return keyObject; + } + case 'EC': { + const enc = new asn1_sequence_encoder_js_1.default(); + const isPrivate = jwk.d !== undefined; + const pub = buffer_1.Buffer.concat([ + buffer_1.Buffer.alloc(1, 4), + buffer_1.Buffer.from(jwk.x, 'base64'), + buffer_1.Buffer.from(jwk.y, 'base64'), + ]); + if (isPrivate) { + enc.zero(); + const enc$1 = new asn1_sequence_encoder_js_1.default(); + enc$1.oidFor('ecPublicKey'); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + const enc$2 = new asn1_sequence_encoder_js_1.default(); + enc$2.one(); + enc$2.octStr(buffer_1.Buffer.from(jwk.d, 'base64')); + const enc$3 = new asn1_sequence_encoder_js_1.default(); + enc$3.bitStr(pub); + const f2 = enc$3.end(buffer_1.Buffer.from([0xa1])); + enc$2.add(f2); + const f = enc$2.end(); + const enc$4 = new asn1_sequence_encoder_js_1.default(); + enc$4.add(f); + const f3 = enc$4.end(buffer_1.Buffer.from([0x04])); + enc.add(f3); + const der = enc.end(); + const keyObject = (0, crypto_1.createPrivateKey)({ key: der, format: 'der', type: 'pkcs8' }); + (0, get_named_curve_js_1.setCurve)(keyObject, jwk.crv); + return keyObject; + } + const enc$1 = new asn1_sequence_encoder_js_1.default(); + enc$1.oidFor('ecPublicKey'); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + enc.bitStr(pub); + const der = enc.end(); + const keyObject = (0, crypto_1.createPublicKey)({ key: der, format: 'der', type: 'spki' }); + (0, get_named_curve_js_1.setCurve)(keyObject, jwk.crv); + return keyObject; + } + case 'OKP': { + const enc = new asn1_sequence_encoder_js_1.default(); + const isPrivate = jwk.d !== undefined; + if (isPrivate) { + enc.zero(); + const enc$1 = new asn1_sequence_encoder_js_1.default(); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + const enc$2 = new asn1_sequence_encoder_js_1.default(); + enc$2.octStr(buffer_1.Buffer.from(jwk.d, 'base64')); + const f = enc$2.end(buffer_1.Buffer.from([0x04])); + enc.add(f); + const der = enc.end(); + return (0, crypto_1.createPrivateKey)({ key: der, format: 'der', type: 'pkcs8' }); + } + const enc$1 = new asn1_sequence_encoder_js_1.default(); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + enc.bitStr(buffer_1.Buffer.from(jwk.x, 'base64')); + const der = enc.end(); + return (0, crypto_1.createPublicKey)({ key: der, format: 'der', type: 'spki' }); + } + default: + throw new errors_js_1.JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); + } +}; +exports.default = parse; diff --git a/dist/node/cjs/runtime/key_to_jwk.js b/dist/node/cjs/runtime/key_to_jwk.js new file mode 100644 index 0000000000..6ee2127570 --- /dev/null +++ b/dist/node/cjs/runtime/key_to_jwk.js @@ -0,0 +1,160 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const base64url_js_1 = require("./base64url.js"); +const asn1_sequence_decoder_js_1 = require("./asn1_sequence_decoder.js"); +const errors_js_1 = require("../util/errors.js"); +const get_named_curve_js_1 = require("./get_named_curve.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const flags_js_1 = require("./flags.js"); +const keyToJWK = (key) => { + let keyObject; + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable'); + } + keyObject = crypto_1.KeyObject.from(key); + } + else if ((0, is_key_object_js_1.default)(key)) { + keyObject = key; + } + else if (key instanceof Uint8Array) { + return { + kty: 'oct', + k: (0, base64url_js_1.encode)(key), + }; + } + else { + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types, 'Uint8Array')); + } + if (flags_js_1.jwkExport) { + if (keyObject.type !== 'secret' && + !['rsa', 'ec', 'ed25519', 'x25519', 'ed448', 'x448'].includes(keyObject.asymmetricKeyType)) { + throw new errors_js_1.JOSENotSupported('Unsupported key asymmetricKeyType'); + } + return keyObject.export({ format: 'jwk' }); + } + switch (keyObject.type) { + case 'secret': + return { + kty: 'oct', + k: (0, base64url_js_1.encode)(keyObject.export()), + }; + case 'private': + case 'public': { + switch (keyObject.asymmetricKeyType) { + case 'rsa': { + const der = keyObject.export({ format: 'der', type: 'pkcs1' }); + const dec = new asn1_sequence_decoder_js_1.default(der); + if (keyObject.type === 'private') { + dec.unsignedInteger(); + } + const n = (0, base64url_js_1.encode)(dec.unsignedInteger()); + const e = (0, base64url_js_1.encode)(dec.unsignedInteger()); + let jwk; + if (keyObject.type === 'private') { + jwk = { + d: (0, base64url_js_1.encode)(dec.unsignedInteger()), + p: (0, base64url_js_1.encode)(dec.unsignedInteger()), + q: (0, base64url_js_1.encode)(dec.unsignedInteger()), + dp: (0, base64url_js_1.encode)(dec.unsignedInteger()), + dq: (0, base64url_js_1.encode)(dec.unsignedInteger()), + qi: (0, base64url_js_1.encode)(dec.unsignedInteger()), + }; + } + dec.end(); + return { kty: 'RSA', n, e, ...jwk }; + } + case 'ec': { + const crv = (0, get_named_curve_js_1.default)(keyObject); + let len; + let offset; + let correction; + switch (crv) { + case 'secp256k1': + len = 64; + offset = 31 + 2; + correction = -1; + break; + case 'P-256': + len = 64; + offset = 34 + 2; + correction = -1; + break; + case 'P-384': + len = 96; + offset = 33 + 2; + correction = -3; + break; + case 'P-521': + len = 132; + offset = 33 + 2; + correction = -3; + break; + default: + throw new errors_js_1.JOSENotSupported('Unsupported curve'); + } + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'EC', + crv, + x: (0, base64url_js_1.encode)(der.subarray(-len, -len / 2)), + y: (0, base64url_js_1.encode)(der.subarray(-len / 2)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + if (der.length < 100) { + offset += correction; + } + return { + ...keyToJWK((0, crypto_1.createPublicKey)(keyObject)), + d: (0, base64url_js_1.encode)(der.subarray(offset, offset + len / 2)), + }; + } + case 'ed25519': + case 'x25519': { + const crv = (0, get_named_curve_js_1.default)(keyObject); + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'OKP', + crv, + x: (0, base64url_js_1.encode)(der.subarray(-32)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + return { + ...keyToJWK((0, crypto_1.createPublicKey)(keyObject)), + d: (0, base64url_js_1.encode)(der.subarray(-32)), + }; + } + case 'ed448': + case 'x448': { + const crv = (0, get_named_curve_js_1.default)(keyObject); + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'OKP', + crv, + x: (0, base64url_js_1.encode)(der.subarray(crv === 'Ed448' ? -57 : -56)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + return { + ...keyToJWK((0, crypto_1.createPublicKey)(keyObject)), + d: (0, base64url_js_1.encode)(der.subarray(crv === 'Ed448' ? -57 : -56)), + }; + } + default: + throw new errors_js_1.JOSENotSupported('Unsupported key asymmetricKeyType'); + } + } + default: + throw new errors_js_1.JOSENotSupported('Unsupported key type'); + } +}; +exports.default = keyToJWK; diff --git a/dist/node/cjs/runtime/node_key.js b/dist/node/cjs/runtime/node_key.js new file mode 100644 index 0000000000..df67562ab4 --- /dev/null +++ b/dist/node/cjs/runtime/node_key.js @@ -0,0 +1,78 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const get_named_curve_js_1 = require("./get_named_curve.js"); +const errors_js_1 = require("../util/errors.js"); +const check_modulus_length_js_1 = require("./check_modulus_length.js"); +const flags_js_1 = require("./flags.js"); +const PSS = { + padding: crypto_1.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto_1.constants.RSA_PSS_SALTLEN_DIGEST, +}; +const ecCurveAlgMap = new Map([ + ['ES256', 'P-256'], + ['ES256K', 'secp256k1'], + ['ES384', 'P-384'], + ['ES512', 'P-521'], +]); +function keyForCrypto(alg, key) { + switch (alg) { + case 'EdDSA': + if (!['ed25519', 'ed448'].includes(key.asymmetricKeyType)) { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448'); + } + return key; + case 'RS256': + case 'RS384': + case 'RS512': + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + (0, check_modulus_length_js_1.default)(key, alg); + return key; + case flags_js_1.rsaPssParams && 'PS256': + case flags_js_1.rsaPssParams && 'PS384': + case flags_js_1.rsaPssParams && 'PS512': + if (key.asymmetricKeyType === 'rsa-pss') { + const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails; + const length = parseInt(alg.slice(-3), 10); + if (hashAlgorithm !== undefined && + (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) { + throw new TypeError(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${alg}`); + } + if (saltLength !== undefined && saltLength > length >> 3) { + throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`); + } + } + else if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss'); + } + (0, check_modulus_length_js_1.default)(key, alg); + return { key, ...PSS }; + case !flags_js_1.rsaPssParams && 'PS256': + case !flags_js_1.rsaPssParams && 'PS384': + case !flags_js_1.rsaPssParams && 'PS512': + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + (0, check_modulus_length_js_1.default)(key, alg); + return { key, ...PSS }; + case 'ES256': + case 'ES256K': + case 'ES384': + case 'ES512': { + if (key.asymmetricKeyType !== 'ec') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ec'); + } + const actual = (0, get_named_curve_js_1.default)(key); + const expected = ecCurveAlgMap.get(alg); + if (actual !== expected) { + throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`); + } + return { dsaEncoding: 'ieee-p1363', key }; + } + default: + throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} +exports.default = keyForCrypto; diff --git a/dist/node/cjs/runtime/pbes2kw.js b/dist/node/cjs/runtime/pbes2kw.js new file mode 100644 index 0000000000..8298b0e7d8 --- /dev/null +++ b/dist/node/cjs/runtime/pbes2kw.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decrypt = exports.encrypt = void 0; +const util_1 = require("util"); +const crypto_1 = require("crypto"); +const random_js_1 = require("./random.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const base64url_js_1 = require("./base64url.js"); +const aeskw_js_1 = require("./aeskw.js"); +const check_p2s_js_1 = require("../lib/check_p2s.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const pbkdf2 = (0, util_1.promisify)(crypto_1.pbkdf2); +function getPassword(key, alg) { + if ((0, is_key_object_js_1.default)(key)) { + return key.export(); + } + if (key instanceof Uint8Array) { + return key; + } + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + (0, crypto_key_js_1.checkEncCryptoKey)(key, alg, 'deriveBits', 'deriveKey'); + return crypto_1.KeyObject.from(key).export(); + } + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types, 'Uint8Array')); +} +const encrypt = async (alg, key, cek, p2c = 2048, p2s = (0, random_js_1.default)(new Uint8Array(16))) => { + (0, check_p2s_js_1.default)(p2s); + const salt = (0, buffer_utils_js_1.p2s)(alg, p2s); + const keylen = parseInt(alg.slice(13, 16), 10) >> 3; + const password = getPassword(key, alg); + const derivedKey = await pbkdf2(password, salt, p2c, keylen, `sha${alg.slice(8, 11)}`); + const encryptedKey = await (0, aeskw_js_1.wrap)(alg.slice(-6), derivedKey, cek); + return { encryptedKey, p2c, p2s: (0, base64url_js_1.encode)(p2s) }; +}; +exports.encrypt = encrypt; +const decrypt = async (alg, key, encryptedKey, p2c, p2s) => { + (0, check_p2s_js_1.default)(p2s); + const salt = (0, buffer_utils_js_1.p2s)(alg, p2s); + const keylen = parseInt(alg.slice(13, 16), 10) >> 3; + const password = getPassword(key, alg); + const derivedKey = await pbkdf2(password, salt, p2c, keylen, `sha${alg.slice(8, 11)}`); + return (0, aeskw_js_1.unwrap)(alg.slice(-6), derivedKey, encryptedKey); +}; +exports.decrypt = decrypt; diff --git a/dist/node/cjs/runtime/random.js b/dist/node/cjs/runtime/random.js new file mode 100644 index 0000000000..459da2bb08 --- /dev/null +++ b/dist/node/cjs/runtime/random.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = void 0; +var crypto_1 = require("crypto"); +Object.defineProperty(exports, "default", { enumerable: true, get: function () { return crypto_1.randomFillSync; } }); diff --git a/dist/node/cjs/runtime/rsaes.js b/dist/node/cjs/runtime/rsaes.js new file mode 100644 index 0000000000..4e625226b8 --- /dev/null +++ b/dist/node/cjs/runtime/rsaes.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decrypt = exports.encrypt = void 0; +const crypto_1 = require("crypto"); +const check_modulus_length_js_1 = require("./check_modulus_length.js"); +const webcrypto_js_1 = require("./webcrypto.js"); +const crypto_key_js_1 = require("../lib/crypto_key.js"); +const is_key_object_js_1 = require("./is_key_object.js"); +const invalid_key_input_js_1 = require("../lib/invalid_key_input.js"); +const is_key_like_js_1 = require("./is_key_like.js"); +const checkKey = (key, alg) => { + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + (0, check_modulus_length_js_1.default)(key, alg); +}; +const resolvePadding = (alg) => { + switch (alg) { + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + return crypto_1.constants.RSA_PKCS1_OAEP_PADDING; + case 'RSA1_5': + return crypto_1.constants.RSA_PKCS1_PADDING; + default: + return undefined; + } +}; +const resolveOaepHash = (alg) => { + switch (alg) { + case 'RSA-OAEP': + return 'sha1'; + case 'RSA-OAEP-256': + return 'sha256'; + case 'RSA-OAEP-384': + return 'sha384'; + case 'RSA-OAEP-512': + return 'sha512'; + default: + return undefined; + } +}; +function ensureKeyObject(key, alg, ...usages) { + if ((0, is_key_object_js_1.default)(key)) { + return key; + } + if ((0, webcrypto_js_1.isCryptoKey)(key)) { + (0, crypto_key_js_1.checkEncCryptoKey)(key, alg, ...usages); + return crypto_1.KeyObject.from(key); + } + throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types)); +} +const encrypt = (alg, key, cek) => { + const padding = resolvePadding(alg); + const oaepHash = resolveOaepHash(alg); + const keyObject = ensureKeyObject(key, alg, 'wrapKey', 'encrypt'); + checkKey(keyObject, alg); + return (0, crypto_1.publicEncrypt)({ key: keyObject, oaepHash, padding }, cek); +}; +exports.encrypt = encrypt; +const decrypt = (alg, key, encryptedKey) => { + const padding = resolvePadding(alg); + const oaepHash = resolveOaepHash(alg); + const keyObject = ensureKeyObject(key, alg, 'unwrapKey', 'decrypt'); + checkKey(keyObject, alg); + return (0, crypto_1.privateDecrypt)({ key: keyObject, oaepHash, padding }, encryptedKey); +}; +exports.decrypt = decrypt; diff --git a/dist/node/cjs/runtime/sign.js b/dist/node/cjs/runtime/sign.js new file mode 100644 index 0000000000..cc19300d9e --- /dev/null +++ b/dist/node/cjs/runtime/sign.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto = require("crypto"); +const util_1 = require("util"); +const dsa_digest_js_1 = require("./dsa_digest.js"); +const hmac_digest_js_1 = require("./hmac_digest.js"); +const node_key_js_1 = require("./node_key.js"); +const get_sign_verify_key_js_1 = require("./get_sign_verify_key.js"); +let oneShotSign; +if (crypto.sign.length > 3) { + oneShotSign = (0, util_1.promisify)(crypto.sign); +} +else { + oneShotSign = crypto.sign; +} +const sign = async (alg, key, data) => { + const keyObject = (0, get_sign_verify_key_js_1.default)(alg, key, 'sign'); + if (alg.startsWith('HS')) { + const hmac = crypto.createHmac((0, hmac_digest_js_1.default)(alg), keyObject); + hmac.update(data); + return hmac.digest(); + } + return oneShotSign((0, dsa_digest_js_1.default)(alg), data, (0, node_key_js_1.default)(alg, keyObject)); +}; +exports.default = sign; diff --git a/dist/node/cjs/runtime/timing_safe_equal.js b/dist/node/cjs/runtime/timing_safe_equal.js new file mode 100644 index 0000000000..e1e4ef6100 --- /dev/null +++ b/dist/node/cjs/runtime/timing_safe_equal.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const timingSafeEqual = crypto_1.timingSafeEqual; +exports.default = timingSafeEqual; diff --git a/dist/node/cjs/runtime/verify.js b/dist/node/cjs/runtime/verify.js new file mode 100644 index 0000000000..c9a1549020 --- /dev/null +++ b/dist/node/cjs/runtime/verify.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto = require("crypto"); +const util_1 = require("util"); +const dsa_digest_js_1 = require("./dsa_digest.js"); +const node_key_js_1 = require("./node_key.js"); +const sign_js_1 = require("./sign.js"); +const get_sign_verify_key_js_1 = require("./get_sign_verify_key.js"); +const flags_js_1 = require("./flags.js"); +let oneShotVerify; +if (crypto.verify.length > 4 && flags_js_1.oneShotCallback) { + oneShotVerify = (0, util_1.promisify)(crypto.verify); +} +else { + oneShotVerify = crypto.verify; +} +const verify = async (alg, key, signature, data) => { + const keyObject = (0, get_sign_verify_key_js_1.default)(alg, key, 'verify'); + if (alg.startsWith('HS')) { + const expected = await (0, sign_js_1.default)(alg, keyObject, data); + const actual = signature; + try { + return crypto.timingSafeEqual(actual, expected); + } + catch { + return false; + } + } + const algorithm = (0, dsa_digest_js_1.default)(alg); + const keyInput = (0, node_key_js_1.default)(alg, keyObject); + try { + return await oneShotVerify(algorithm, data, keyInput, signature); + } + catch { + return false; + } +}; +exports.default = verify; diff --git a/dist/node/cjs/runtime/webcrypto.js b/dist/node/cjs/runtime/webcrypto.js new file mode 100644 index 0000000000..8a3e72e06e --- /dev/null +++ b/dist/node/cjs/runtime/webcrypto.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isCryptoKey = void 0; +const crypto = require("crypto"); +const util = require("util"); +const webcrypto = crypto.webcrypto; +exports.default = webcrypto; +exports.isCryptoKey = util.types.isCryptoKey + ? (key) => util.types.isCryptoKey(key) + : + (key) => false; diff --git a/dist/node/cjs/runtime/zlib.js b/dist/node/cjs/runtime/zlib.js new file mode 100644 index 0000000000..7dcfddf49b --- /dev/null +++ b/dist/node/cjs/runtime/zlib.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.deflate = exports.inflate = void 0; +const util_1 = require("util"); +const zlib_1 = require("zlib"); +const inflateRaw = (0, util_1.promisify)(zlib_1.inflateRaw); +const deflateRaw = (0, util_1.promisify)(zlib_1.deflateRaw); +const inflate = (input) => inflateRaw(input); +exports.inflate = inflate; +const deflate = (input) => deflateRaw(input); +exports.deflate = deflate; diff --git a/dist/node/cjs/util/base64url.js b/dist/node/cjs/util/base64url.js new file mode 100644 index 0000000000..4d383b5167 --- /dev/null +++ b/dist/node/cjs/util/base64url.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decode = exports.encode = void 0; +const base64url = require("../runtime/base64url.js"); +exports.encode = base64url.encode; +exports.decode = base64url.decode; diff --git a/dist/node/cjs/util/decode_jwt.js b/dist/node/cjs/util/decode_jwt.js new file mode 100644 index 0000000000..f5b6122e46 --- /dev/null +++ b/dist/node/cjs/util/decode_jwt.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeJwt = void 0; +const base64url_js_1 = require("./base64url.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const is_object_js_1 = require("../lib/is_object.js"); +const errors_js_1 = require("./errors.js"); +function decodeJwt(jwt) { + if (typeof jwt !== 'string') + throw new errors_js_1.JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string'); + const { 1: payload, length } = jwt.split('.'); + if (length === 5) + throw new errors_js_1.JWTInvalid('Only JWTs using Compact JWS serialization can be decoded'); + if (length !== 3) + throw new errors_js_1.JWTInvalid('Invalid JWT'); + if (!payload) + throw new errors_js_1.JWTInvalid('JWTs must contain a payload'); + let decoded; + try { + decoded = (0, base64url_js_1.decode)(payload); + } + catch { + throw new errors_js_1.JWTInvalid('Failed to parse the base64url encoded payload'); + } + let result; + try { + result = JSON.parse(buffer_utils_js_1.decoder.decode(decoded)); + } + catch { + throw new errors_js_1.JWTInvalid('Failed to parse the decoded payload as JSON'); + } + if (!(0, is_object_js_1.default)(result)) + throw new errors_js_1.JWTInvalid('Invalid JWT Claims Set'); + return result; +} +exports.decodeJwt = decodeJwt; diff --git a/dist/node/cjs/util/decode_protected_header.js b/dist/node/cjs/util/decode_protected_header.js new file mode 100644 index 0000000000..601ebb7297 --- /dev/null +++ b/dist/node/cjs/util/decode_protected_header.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeProtectedHeader = void 0; +const base64url_js_1 = require("./base64url.js"); +const buffer_utils_js_1 = require("../lib/buffer_utils.js"); +const is_object_js_1 = require("../lib/is_object.js"); +function decodeProtectedHeader(token) { + let protectedB64u; + if (typeof token === 'string') { + const parts = token.split('.'); + if (parts.length === 3 || parts.length === 5) { + ; + [protectedB64u] = parts; + } + } + else if (typeof token === 'object' && token) { + if ('protected' in token) { + protectedB64u = token.protected; + } + else { + throw new TypeError('Token does not contain a Protected Header'); + } + } + try { + if (typeof protectedB64u !== 'string' || !protectedB64u) { + throw new Error(); + } + const result = JSON.parse(buffer_utils_js_1.decoder.decode((0, base64url_js_1.decode)(protectedB64u))); + if (!(0, is_object_js_1.default)(result)) { + throw new Error(); + } + return result; + } + catch { + throw new TypeError('Invalid Token or Protected Header formatting'); + } +} +exports.decodeProtectedHeader = decodeProtectedHeader; diff --git a/dist/node/cjs/util/errors.js b/dist/node/cjs/util/errors.js new file mode 100644 index 0000000000..5ec68a5b12 --- /dev/null +++ b/dist/node/cjs/util/errors.js @@ -0,0 +1,166 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.JWSSignatureVerificationFailed = exports.JWKSTimeout = exports.JWKSMultipleMatchingKeys = exports.JWKSNoMatchingKey = exports.JWKSInvalid = exports.JWKInvalid = exports.JWTInvalid = exports.JWSInvalid = exports.JWEInvalid = exports.JWEDecryptionFailed = exports.JOSENotSupported = exports.JOSEAlgNotAllowed = exports.JWTExpired = exports.JWTClaimValidationFailed = exports.JOSEError = void 0; +class JOSEError extends Error { + static get code() { + return 'ERR_JOSE_GENERIC'; + } + constructor(message) { + var _a; + super(message); + this.code = 'ERR_JOSE_GENERIC'; + this.name = this.constructor.name; + (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); + } +} +exports.JOSEError = JOSEError; +class JWTClaimValidationFailed extends JOSEError { + static get code() { + return 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + this.claim = claim; + this.reason = reason; + } +} +exports.JWTClaimValidationFailed = JWTClaimValidationFailed; +class JWTExpired extends JOSEError { + static get code() { + return 'ERR_JWT_EXPIRED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_EXPIRED'; + this.claim = claim; + this.reason = reason; + } +} +exports.JWTExpired = JWTExpired; +class JOSEAlgNotAllowed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_ALG_NOT_ALLOWED'; + } + static get code() { + return 'ERR_JOSE_ALG_NOT_ALLOWED'; + } +} +exports.JOSEAlgNotAllowed = JOSEAlgNotAllowed; +class JOSENotSupported extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_NOT_SUPPORTED'; + } + static get code() { + return 'ERR_JOSE_NOT_SUPPORTED'; + } +} +exports.JOSENotSupported = JOSENotSupported; +class JWEDecryptionFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_DECRYPTION_FAILED'; + this.message = 'decryption operation failed'; + } + static get code() { + return 'ERR_JWE_DECRYPTION_FAILED'; + } +} +exports.JWEDecryptionFailed = JWEDecryptionFailed; +class JWEInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_INVALID'; + } + static get code() { + return 'ERR_JWE_INVALID'; + } +} +exports.JWEInvalid = JWEInvalid; +class JWSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_INVALID'; + } + static get code() { + return 'ERR_JWS_INVALID'; + } +} +exports.JWSInvalid = JWSInvalid; +class JWTInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWT_INVALID'; + } + static get code() { + return 'ERR_JWT_INVALID'; + } +} +exports.JWTInvalid = JWTInvalid; +class JWKInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWK_INVALID'; + } + static get code() { + return 'ERR_JWK_INVALID'; + } +} +exports.JWKInvalid = JWKInvalid; +class JWKSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_INVALID'; + } + static get code() { + return 'ERR_JWKS_INVALID'; + } +} +exports.JWKSInvalid = JWKSInvalid; +class JWKSNoMatchingKey extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_NO_MATCHING_KEY'; + this.message = 'no applicable key found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_NO_MATCHING_KEY'; + } +} +exports.JWKSNoMatchingKey = JWKSNoMatchingKey; +class JWKSMultipleMatchingKeys extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + this.message = 'multiple matching keys found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + } +} +exports.JWKSMultipleMatchingKeys = JWKSMultipleMatchingKeys; +Symbol.asyncIterator; +class JWKSTimeout extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_TIMEOUT'; + this.message = 'request timed out'; + } + static get code() { + return 'ERR_JWKS_TIMEOUT'; + } +} +exports.JWKSTimeout = JWKSTimeout; +class JWSSignatureVerificationFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + this.message = 'signature verification failed'; + } + static get code() { + return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + } +} +exports.JWSSignatureVerificationFailed = JWSSignatureVerificationFailed; diff --git a/dist/node/esm/index.js b/dist/node/esm/index.js new file mode 100644 index 0000000000..d2ce09d71a --- /dev/null +++ b/dist/node/esm/index.js @@ -0,0 +1,29 @@ +export { compactDecrypt } from './jwe/compact/decrypt.js'; +export { flattenedDecrypt } from './jwe/flattened/decrypt.js'; +export { generalDecrypt } from './jwe/general/decrypt.js'; +export { GeneralEncrypt } from './jwe/general/encrypt.js'; +export { compactVerify } from './jws/compact/verify.js'; +export { flattenedVerify } from './jws/flattened/verify.js'; +export { generalVerify } from './jws/general/verify.js'; +export { jwtVerify } from './jwt/verify.js'; +export { jwtDecrypt } from './jwt/decrypt.js'; +export { CompactEncrypt } from './jwe/compact/encrypt.js'; +export { FlattenedEncrypt } from './jwe/flattened/encrypt.js'; +export { CompactSign } from './jws/compact/sign.js'; +export { FlattenedSign } from './jws/flattened/sign.js'; +export { GeneralSign } from './jws/general/sign.js'; +export { SignJWT } from './jwt/sign.js'; +export { EncryptJWT } from './jwt/encrypt.js'; +export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js'; +export { EmbeddedJWK } from './jwk/embedded.js'; +export { createLocalJWKSet } from './jwks/local.js'; +export { createRemoteJWKSet } from './jwks/remote.js'; +export { UnsecuredJWT } from './jwt/unsecured.js'; +export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js'; +export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js'; +export { decodeProtectedHeader } from './util/decode_protected_header.js'; +export { decodeJwt } from './util/decode_jwt.js'; +export * as errors from './util/errors.js'; +export { generateKeyPair } from './key/generate_key_pair.js'; +export { generateSecret } from './key/generate_secret.js'; +export * as base64url from './util/base64url.js'; diff --git a/dist/node/esm/jwe/compact/decrypt.js b/dist/node/esm/jwe/compact/decrypt.js new file mode 100644 index 0000000000..129aeb6e39 --- /dev/null +++ b/dist/node/esm/jwe/compact/decrypt.js @@ -0,0 +1,27 @@ +import { flattenedDecrypt } from '../flattened/decrypt.js'; +import { JWEInvalid } from '../../util/errors.js'; +import { decoder } from '../../lib/buffer_utils.js'; +export async function compactDecrypt(jwe, key, options) { + if (jwe instanceof Uint8Array) { + jwe = decoder.decode(jwe); + } + if (typeof jwe !== 'string') { + throw new JWEInvalid('Compact JWE must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length, } = jwe.split('.'); + if (length !== 5) { + throw new JWEInvalid('Invalid Compact JWE'); + } + const decrypted = await flattenedDecrypt({ + ciphertext, + iv: (iv || undefined), + protected: protectedHeader || undefined, + tag: (tag || undefined), + encrypted_key: encryptedKey || undefined, + }, key, options); + const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} diff --git a/dist/node/esm/jwe/compact/encrypt.js b/dist/node/esm/jwe/compact/encrypt.js new file mode 100644 index 0000000000..e689139465 --- /dev/null +++ b/dist/node/esm/jwe/compact/encrypt.js @@ -0,0 +1,26 @@ +import { FlattenedEncrypt } from '../flattened/encrypt.js'; +export class CompactEncrypt { + constructor(plaintext) { + this._flattened = new FlattenedEncrypt(plaintext); + } + setContentEncryptionKey(cek) { + this._flattened.setContentEncryptionKey(cek); + return this; + } + setInitializationVector(iv) { + this._flattened.setInitializationVector(iv); + return this; + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + setKeyManagementParameters(parameters) { + this._flattened.setKeyManagementParameters(parameters); + return this; + } + async encrypt(key, options) { + const jwe = await this._flattened.encrypt(key, options); + return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.'); + } +} diff --git a/dist/node/esm/jwe/flattened/decrypt.js b/dist/node/esm/jwe/flattened/decrypt.js new file mode 100644 index 0000000000..e918c88954 --- /dev/null +++ b/dist/node/esm/jwe/flattened/decrypt.js @@ -0,0 +1,137 @@ +import { decode as base64url } from '../../runtime/base64url.js'; +import decrypt from '../../runtime/decrypt.js'; +import { inflate } from '../../runtime/zlib.js'; +import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import isObject from '../../lib/is_object.js'; +import decryptKeyManagement from '../../lib/decrypt_key_management.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import generateCek from '../../lib/cek.js'; +import validateCrit from '../../lib/validate_crit.js'; +import validateAlgorithms from '../../lib/validate_algorithms.js'; +export async function flattenedDecrypt(jwe, key, options) { + var _a; + if (!isObject(jwe)) { + throw new JWEInvalid('Flattened JWE must be an object'); + } + if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { + throw new JWEInvalid('JOSE Header missing'); + } + if (typeof jwe.iv !== 'string') { + throw new JWEInvalid('JWE Initialization Vector missing or incorrect type'); + } + if (typeof jwe.ciphertext !== 'string') { + throw new JWEInvalid('JWE Ciphertext missing or incorrect type'); + } + if (typeof jwe.tag !== 'string') { + throw new JWEInvalid('JWE Authentication Tag missing or incorrect type'); + } + if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { + throw new JWEInvalid('JWE Protected Header incorrect type'); + } + if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { + throw new JWEInvalid('JWE Encrypted Key incorrect type'); + } + if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { + throw new JWEInvalid('JWE AAD incorrect type'); + } + if (jwe.header !== undefined && !isObject(jwe.header)) { + throw new JWEInvalid('JWE Shared Unprotected Header incorrect type'); + } + if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { + throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); + } + let parsedProt; + if (jwe.protected) { + try { + const protectedHeader = base64url(jwe.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } + catch { + throw new JWEInvalid('JWE Protected Header is invalid'); + } + } + if (!isDisjoint(parsedProt, jwe.header, jwe.unprotected)) { + throw new JWEInvalid('JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jwe.header, + ...jwe.unprotected, + }; + validateCrit(JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + if (joseHeader.zip !== undefined) { + if (!parsedProt || !parsedProt.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); + } + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); + } + const keyManagementAlgorithms = options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms); + const contentEncryptionAlgorithms = options && + validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms); + if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { + throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed'); + } + let encryptedKey; + if (jwe.encrypted_key !== undefined) { + encryptedKey = base64url(jwe.encrypted_key); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jwe); + resolvedKey = true; + } + let cek; + try { + cek = await decryptKeyManagement(alg, key, encryptedKey, joseHeader, options); + } + catch (err) { + if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { + throw err; + } + cek = generateCek(enc); + } + const iv = base64url(jwe.iv); + const tag = base64url(jwe.tag); + const protectedHeader = encoder.encode((_a = jwe.protected) !== null && _a !== void 0 ? _a : ''); + let additionalData; + if (jwe.aad !== undefined) { + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)); + } + else { + additionalData = protectedHeader; + } + let plaintext = await decrypt(enc, cek, base64url(jwe.ciphertext), iv, tag, additionalData); + if (joseHeader.zip === 'DEF') { + plaintext = await ((options === null || options === void 0 ? void 0 : options.inflateRaw) || inflate)(plaintext); + } + const result = { plaintext }; + if (jwe.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jwe.aad !== undefined) { + result.additionalAuthenticatedData = base64url(jwe.aad); + } + if (jwe.unprotected !== undefined) { + result.sharedUnprotectedHeader = jwe.unprotected; + } + if (jwe.header !== undefined) { + result.unprotectedHeader = jwe.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} diff --git a/dist/node/esm/jwe/flattened/encrypt.js b/dist/node/esm/jwe/flattened/encrypt.js new file mode 100644 index 0000000000..1e60ecd8c4 --- /dev/null +++ b/dist/node/esm/jwe/flattened/encrypt.js @@ -0,0 +1,175 @@ +import { encode as base64url } from '../../runtime/base64url.js'; +import encrypt from '../../runtime/encrypt.js'; +import { deflate } from '../../runtime/zlib.js'; +import generateIv from '../../lib/iv.js'; +import encryptKeyManagement from '../../lib/encrypt_key_management.js'; +import { JOSENotSupported, JWEInvalid } from '../../util/errors.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import validateCrit from '../../lib/validate_crit.js'; +export const unprotected = Symbol(); +export class FlattenedEncrypt { + constructor(plaintext) { + if (!(plaintext instanceof Uint8Array)) { + throw new TypeError('plaintext must be an instance of Uint8Array'); + } + this._plaintext = plaintext; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._sharedUnprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._sharedUnprotectedHeader = sharedUnprotectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + async encrypt(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader && !this._sharedUnprotectedHeader) { + throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()'); + } + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) { + throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...this._sharedUnprotectedHeader, + }; + validateCrit(JWEInvalid, new Map(), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + if (joseHeader.zip !== 'DEF') { + throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value'); + } + } + const { alg, enc } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (typeof enc !== 'string' || !enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + let encryptedKey; + if (alg === 'dir') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption'); + } + } + else if (alg === 'ECDH-ES') { + if (this._cek) { + throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement'); + } + } + let cek; + { + let parameters; + ({ cek, encryptedKey, parameters } = await encryptKeyManagement(alg, enc, key, this._cek, this._keyManagementParameters)); + if (parameters) { + if (options && unprotected in options) { + if (!this._unprotectedHeader) { + this.setUnprotectedHeader(parameters); + } + else { + this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters }; + } + } + else { + if (!this._protectedHeader) { + this.setProtectedHeader(parameters); + } + else { + this._protectedHeader = { ...this._protectedHeader, ...parameters }; + } + } + } + } + this._iv || (this._iv = generateIv(enc)); + let additionalData; + let protectedHeader; + let aadMember; + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = encoder.encode(''); + } + if (this._aad) { + aadMember = base64url(this._aad); + additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(aadMember)); + } + else { + additionalData = protectedHeader; + } + let ciphertext; + let tag; + if (joseHeader.zip === 'DEF') { + const deflated = await ((options === null || options === void 0 ? void 0 : options.deflateRaw) || deflate)(this._plaintext); + ({ ciphertext, tag } = await encrypt(enc, deflated, cek, this._iv, additionalData)); + } + else { + ; + ({ ciphertext, tag } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData)); + } + const jwe = { + ciphertext: base64url(ciphertext), + iv: base64url(this._iv), + tag: base64url(tag), + }; + if (encryptedKey) { + jwe.encrypted_key = base64url(encryptedKey); + } + if (aadMember) { + jwe.aad = aadMember; + } + if (this._protectedHeader) { + jwe.protected = decoder.decode(protectedHeader); + } + if (this._sharedUnprotectedHeader) { + jwe.unprotected = this._sharedUnprotectedHeader; + } + if (this._unprotectedHeader) { + jwe.header = this._unprotectedHeader; + } + return jwe; + } +} diff --git a/dist/node/esm/jwe/general/decrypt.js b/dist/node/esm/jwe/general/decrypt.js new file mode 100644 index 0000000000..659958a8bc --- /dev/null +++ b/dist/node/esm/jwe/general/decrypt.js @@ -0,0 +1,31 @@ +import { flattenedDecrypt } from '../flattened/decrypt.js'; +import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.js'; +import isObject from '../../lib/is_object.js'; +export async function generalDecrypt(jwe, key, options) { + if (!isObject(jwe)) { + throw new JWEInvalid('General JWE must be an object'); + } + if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { + throw new JWEInvalid('JWE Recipients missing or incorrect type'); + } + if (!jwe.recipients.length) { + throw new JWEInvalid('JWE Recipients has no members'); + } + for (const recipient of jwe.recipients) { + try { + return await flattenedDecrypt({ + aad: jwe.aad, + ciphertext: jwe.ciphertext, + encrypted_key: recipient.encrypted_key, + header: recipient.header, + iv: jwe.iv, + protected: jwe.protected, + tag: jwe.tag, + unprotected: jwe.unprotected, + }, key, options); + } + catch { + } + } + throw new JWEDecryptionFailed(); +} diff --git a/dist/node/esm/jwe/general/encrypt.js b/dist/node/esm/jwe/general/encrypt.js new file mode 100644 index 0000000000..3ee53dec44 --- /dev/null +++ b/dist/node/esm/jwe/general/encrypt.js @@ -0,0 +1,178 @@ +import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.js'; +import { JWEInvalid } from '../../util/errors.js'; +import generateCek from '../../lib/cek.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import encryptKeyManagement from '../../lib/encrypt_key_management.js'; +import { encode as base64url } from '../../runtime/base64url.js'; +import validateCrit from '../../lib/validate_crit.js'; +class IndividualRecipient { + constructor(enc, key, options) { + this.parent = enc; + this.key = key; + this.options = options; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addRecipient(...args) { + return this.parent.addRecipient(...args); + } + encrypt(...args) { + return this.parent.encrypt(...args); + } + done() { + return this.parent; + } +} +export class GeneralEncrypt { + constructor(plaintext) { + this._recipients = []; + this._plaintext = plaintext; + } + addRecipient(key, options) { + const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit }); + this._recipients.push(recipient); + return recipient; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setSharedUnprotectedHeader(sharedUnprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setSharedUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = sharedUnprotectedHeader; + return this; + } + setAdditionalAuthenticatedData(aad) { + this._aad = aad; + return this; + } + async encrypt(options) { + var _a, _b, _c; + if (!this._recipients.length) { + throw new JWEInvalid('at least one recipient must be added'); + } + options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw }; + if (this._recipients.length === 1) { + const [recipient] = this._recipients; + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .encrypt(recipient.key, { ...recipient.options, ...options }); + let jwe = { + ciphertext: flattened.ciphertext, + iv: flattened.iv, + recipients: [{}], + tag: flattened.tag, + }; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + if (flattened.encrypted_key) + jwe.recipients[0].encrypted_key = flattened.encrypted_key; + if (flattened.header) + jwe.recipients[0].header = flattened.header; + return jwe; + } + let enc; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) { + throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); + } + if (alg === 'dir' || alg === 'ECDH-ES') { + throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient'); + } + if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); + } + if (!enc) { + enc = joseHeader.enc; + } + else if (enc !== joseHeader.enc) { + throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients'); + } + validateCrit(JWEInvalid, new Map(), recipient.options.crit, this._protectedHeader, joseHeader); + if (joseHeader.zip !== undefined) { + if (!this._protectedHeader || !this._protectedHeader.zip) { + throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); + } + } + } + const cek = generateCek(enc); + let jwe = { + ciphertext: '', + iv: '', + recipients: [], + tag: '', + }; + for (let i = 0; i < this._recipients.length; i++) { + const recipient = this._recipients[i]; + const target = {}; + jwe.recipients.push(target); + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + ...recipient.unprotectedHeader, + }; + const p2c = joseHeader.alg.startsWith('PBES2') ? 2048 + i : undefined; + if (i === 0) { + const flattened = await new FlattenedEncrypt(this._plaintext) + .setAdditionalAuthenticatedData(this._aad) + .setContentEncryptionKey(cek) + .setProtectedHeader(this._protectedHeader) + .setSharedUnprotectedHeader(this._unprotectedHeader) + .setUnprotectedHeader(recipient.unprotectedHeader) + .setKeyManagementParameters({ p2c }) + .encrypt(recipient.key, { + ...recipient.options, + ...options, + [unprotected]: true, + }); + jwe.ciphertext = flattened.ciphertext; + jwe.iv = flattened.iv; + jwe.tag = flattened.tag; + if (flattened.aad) + jwe.aad = flattened.aad; + if (flattened.protected) + jwe.protected = flattened.protected; + if (flattened.unprotected) + jwe.unprotected = flattened.unprotected; + target.encrypted_key = flattened.encrypted_key; + if (flattened.header) + target.header = flattened.header; + continue; + } + const { encryptedKey, parameters } = await encryptKeyManagement(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || + ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) || + ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c }); + target.encrypted_key = base64url(encryptedKey); + if (recipient.unprotectedHeader || parameters) + target.header = { ...recipient.unprotectedHeader, ...parameters }; + } + return jwe; + } +} diff --git a/dist/node/esm/jwk/embedded.js b/dist/node/esm/jwk/embedded.js new file mode 100644 index 0000000000..561ae2505b --- /dev/null +++ b/dist/node/esm/jwk/embedded.js @@ -0,0 +1,17 @@ +import { importJWK } from '../key/import.js'; +import isObject from '../lib/is_object.js'; +import { JWSInvalid } from '../util/errors.js'; +export async function EmbeddedJWK(protectedHeader, token) { + const joseHeader = { + ...protectedHeader, + ...token === null || token === void 0 ? void 0 : token.header, + }; + if (!isObject(joseHeader.jwk)) { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object'); + } + const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg, true); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key'); + } + return key; +} diff --git a/dist/node/esm/jwk/thumbprint.js b/dist/node/esm/jwk/thumbprint.js new file mode 100644 index 0000000000..49f86b1743 --- /dev/null +++ b/dist/node/esm/jwk/thumbprint.js @@ -0,0 +1,53 @@ +import digest from '../runtime/digest.js'; +import { encode as base64url } from '../runtime/base64url.js'; +import { JOSENotSupported, JWKInvalid } from '../util/errors.js'; +import { encoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +const check = (value, description) => { + if (typeof value !== 'string' || !value) { + throw new JWKInvalid(`${description} missing or invalid`); + } +}; +export async function calculateJwkThumbprint(jwk, digestAlgorithm) { + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object'); + } + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + if (digestAlgorithm !== 'sha256' && + digestAlgorithm !== 'sha384' && + digestAlgorithm !== 'sha512') { + throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"'); + } + let components; + switch (jwk.kty) { + case 'EC': + check(jwk.crv, '"crv" (Curve) Parameter'); + check(jwk.x, '"x" (X Coordinate) Parameter'); + check(jwk.y, '"y" (Y Coordinate) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }; + break; + case 'OKP': + check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'); + check(jwk.x, '"x" (Public Key) Parameter'); + components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }; + break; + case 'RSA': + check(jwk.e, '"e" (Exponent) Parameter'); + check(jwk.n, '"n" (Modulus) Parameter'); + components = { e: jwk.e, kty: jwk.kty, n: jwk.n }; + break; + case 'oct': + check(jwk.k, '"k" (Key Value) Parameter'); + components = { k: jwk.k, kty: jwk.kty }; + break; + default: + throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported'); + } + const data = encoder.encode(JSON.stringify(components)); + return base64url(await digest(digestAlgorithm, data)); +} +export async function calculateJwkThumbprintUri(jwk, digestAlgorithm) { + digestAlgorithm !== null && digestAlgorithm !== void 0 ? digestAlgorithm : (digestAlgorithm = 'sha256'); + const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm); + return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`; +} diff --git a/dist/node/esm/jwks/local.js b/dist/node/esm/jwks/local.js new file mode 100644 index 0000000000..235675170f --- /dev/null +++ b/dist/node/esm/jwks/local.js @@ -0,0 +1,116 @@ +import { importJWK } from '../key/import.js'; +import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js'; +import isObject from '../lib/is_object.js'; +function getKtyFromAlg(alg) { + switch (typeof alg === 'string' && alg.slice(0, 2)) { + case 'RS': + case 'PS': + return 'RSA'; + case 'ES': + return 'EC'; + case 'Ed': + return 'OKP'; + default: + throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set'); + } +} +export function isJWKSLike(jwks) { + return (jwks && + typeof jwks === 'object' && + Array.isArray(jwks.keys) && + jwks.keys.every(isJWKLike)); +} +function isJWKLike(key) { + return isObject(key); +} +function clone(obj) { + if (typeof structuredClone === 'function') { + return structuredClone(obj); + } + return JSON.parse(JSON.stringify(obj)); +} +export class LocalJWKSet { + constructor(jwks) { + this._cached = new WeakMap(); + if (!isJWKSLike(jwks)) { + throw new JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = clone(jwks); + } + async getKey(protectedHeader, token) { + const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header }; + const kty = getKtyFromAlg(alg); + const candidates = this._jwks.keys.filter((jwk) => { + let candidate = kty === jwk.kty; + if (candidate && typeof kid === 'string') { + candidate = kid === jwk.kid; + } + if (candidate && typeof jwk.alg === 'string') { + candidate = alg === jwk.alg; + } + if (candidate && typeof jwk.use === 'string') { + candidate = jwk.use === 'sig'; + } + if (candidate && Array.isArray(jwk.key_ops)) { + candidate = jwk.key_ops.includes('verify'); + } + if (candidate && alg === 'EdDSA') { + candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448'; + } + if (candidate) { + switch (alg) { + case 'ES256': + candidate = jwk.crv === 'P-256'; + break; + case 'ES256K': + candidate = jwk.crv === 'secp256k1'; + break; + case 'ES384': + candidate = jwk.crv === 'P-384'; + break; + case 'ES512': + candidate = jwk.crv === 'P-521'; + break; + } + } + return candidate; + }); + const { 0: jwk, length } = candidates; + if (length === 0) { + throw new JWKSNoMatchingKey(); + } + else if (length !== 1) { + const error = new JWKSMultipleMatchingKeys(); + const { _cached } = this; + error[Symbol.asyncIterator] = async function* () { + for (const jwk of candidates) { + try { + yield await importWithAlgCache(_cached, jwk, alg); + } + catch { + continue; + } + } + }; + throw error; + } + return importWithAlgCache(this._cached, jwk, alg); + } +} +async function importWithAlgCache(cache, jwk, alg) { + const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk); + if (cached[alg] === undefined) { + const key = await importJWK({ ...jwk, ext: true }, alg); + if (key instanceof Uint8Array || key.type !== 'public') { + throw new JWKSInvalid('JSON Web Key Set members must be public keys'); + } + cached[alg] = key; + } + return cached[alg]; +} +export function createLocalJWKSet(jwks) { + const set = new LocalJWKSet(jwks); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} diff --git a/dist/node/esm/jwks/remote.js b/dist/node/esm/jwks/remote.js new file mode 100644 index 0000000000..d370579412 --- /dev/null +++ b/dist/node/esm/jwks/remote.js @@ -0,0 +1,72 @@ +import fetchJwks from '../runtime/fetch_jwks.js'; +import { isCloudflareWorkers } from '../runtime/env.js'; +import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js'; +import { isJWKSLike, LocalJWKSet } from './local.js'; +class RemoteJWKSet extends LocalJWKSet { + constructor(url, options) { + super({ keys: [] }); + this._jwks = undefined; + if (!(url instanceof URL)) { + throw new TypeError('url must be an instance of URL'); + } + this._url = new URL(url.href); + this._options = { agent: options === null || options === void 0 ? void 0 : options.agent, headers: options === null || options === void 0 ? void 0 : options.headers }; + this._timeoutDuration = + typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; + this._cooldownDuration = + typeof (options === null || options === void 0 ? void 0 : options.cooldownDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.cooldownDuration : 30000; + this._cacheMaxAge = typeof (options === null || options === void 0 ? void 0 : options.cacheMaxAge) === 'number' ? options === null || options === void 0 ? void 0 : options.cacheMaxAge : 600000; + } + coolingDown() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cooldownDuration + : false; + } + fresh() { + return typeof this._jwksTimestamp === 'number' + ? Date.now() < this._jwksTimestamp + this._cacheMaxAge + : false; + } + async getKey(protectedHeader, token) { + if (!this._jwks || !this.fresh()) { + await this.reload(); + } + try { + return await super.getKey(protectedHeader, token); + } + catch (err) { + if (err instanceof JWKSNoMatchingKey) { + if (this.coolingDown() === false) { + await this.reload(); + return super.getKey(protectedHeader, token); + } + } + throw err; + } + } + async reload() { + if (this._pendingFetch && isCloudflareWorkers()) { + this._pendingFetch = undefined; + } + this._pendingFetch || (this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options) + .then((json) => { + if (!isJWKSLike(json)) { + throw new JWKSInvalid('JSON Web Key Set malformed'); + } + this._jwks = { keys: json.keys }; + this._jwksTimestamp = Date.now(); + this._pendingFetch = undefined; + }) + .catch((err) => { + this._pendingFetch = undefined; + throw err; + })); + await this._pendingFetch; + } +} +export function createRemoteJWKSet(url, options) { + const set = new RemoteJWKSet(url, options); + return async function (protectedHeader, token) { + return set.getKey(protectedHeader, token); + }; +} diff --git a/dist/node/esm/jws/compact/sign.js b/dist/node/esm/jws/compact/sign.js new file mode 100644 index 0000000000..b8e5ba0e2b --- /dev/null +++ b/dist/node/esm/jws/compact/sign.js @@ -0,0 +1,17 @@ +import { FlattenedSign } from '../flattened/sign.js'; +export class CompactSign { + constructor(payload) { + this._flattened = new FlattenedSign(payload); + } + setProtectedHeader(protectedHeader) { + this._flattened.setProtectedHeader(protectedHeader); + return this; + } + async sign(key, options) { + const jws = await this._flattened.sign(key, options); + if (jws.payload === undefined) { + throw new TypeError('use the flattened module for creating JWS with b64: false'); + } + return `${jws.protected}.${jws.payload}.${jws.signature}`; + } +} diff --git a/dist/node/esm/jws/compact/verify.js b/dist/node/esm/jws/compact/verify.js new file mode 100644 index 0000000000..c651ffb944 --- /dev/null +++ b/dist/node/esm/jws/compact/verify.js @@ -0,0 +1,21 @@ +import { flattenedVerify } from '../flattened/verify.js'; +import { JWSInvalid } from '../../util/errors.js'; +import { decoder } from '../../lib/buffer_utils.js'; +export async function compactVerify(jws, key, options) { + if (jws instanceof Uint8Array) { + jws = decoder.decode(jws); + } + if (typeof jws !== 'string') { + throw new JWSInvalid('Compact JWS must be a string or Uint8Array'); + } + const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); + if (length !== 3) { + throw new JWSInvalid('Invalid Compact JWS'); + } + const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options); + const result = { payload: verified.payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} diff --git a/dist/node/esm/jws/flattened/sign.js b/dist/node/esm/jws/flattened/sign.js new file mode 100644 index 0000000000..76ae289654 --- /dev/null +++ b/dist/node/esm/jws/flattened/sign.js @@ -0,0 +1,81 @@ +import { encode as base64url } from '../../runtime/base64url.js'; +import sign from '../../runtime/sign.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import { JWSInvalid } from '../../util/errors.js'; +import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; +import checkKeyType from '../../lib/check_key_type.js'; +import validateCrit from '../../lib/validate_crit.js'; +export class FlattenedSign { + constructor(payload) { + if (!(payload instanceof Uint8Array)) { + throw new TypeError('payload must be an instance of Uint8Array'); + } + this._payload = payload; + } + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this._unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this._unprotectedHeader = unprotectedHeader; + return this; + } + async sign(key, options) { + if (!this._protectedHeader && !this._unprotectedHeader) { + throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); + } + if (!isDisjoint(this._protectedHeader, this._unprotectedHeader)) { + throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...this._protectedHeader, + ...this._unprotectedHeader, + }; + const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = this._protectedHeader.b64; + if (typeof b64 !== 'boolean') { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + checkKeyType(alg, key, 'sign'); + let payload = this._payload; + if (b64) { + payload = encoder.encode(base64url(payload)); + } + let protectedHeader; + if (this._protectedHeader) { + protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))); + } + else { + protectedHeader = encoder.encode(''); + } + const data = concat(protectedHeader, encoder.encode('.'), payload); + const signature = await sign(alg, key, data); + const jws = { + signature: base64url(signature), + payload: '', + }; + if (b64) { + jws.payload = decoder.decode(payload); + } + if (this._unprotectedHeader) { + jws.header = this._unprotectedHeader; + } + if (this._protectedHeader) { + jws.protected = decoder.decode(protectedHeader); + } + return jws; + } +} diff --git a/dist/node/esm/jws/flattened/verify.js b/dist/node/esm/jws/flattened/verify.js new file mode 100644 index 0000000000..a6ea3c8a12 --- /dev/null +++ b/dist/node/esm/jws/flattened/verify.js @@ -0,0 +1,104 @@ +import { decode as base64url } from '../../runtime/base64url.js'; +import verify from '../../runtime/verify.js'; +import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; +import { concat, encoder, decoder } from '../../lib/buffer_utils.js'; +import isDisjoint from '../../lib/is_disjoint.js'; +import isObject from '../../lib/is_object.js'; +import checkKeyType from '../../lib/check_key_type.js'; +import validateCrit from '../../lib/validate_crit.js'; +import validateAlgorithms from '../../lib/validate_algorithms.js'; +export async function flattenedVerify(jws, key, options) { + var _a; + if (!isObject(jws)) { + throw new JWSInvalid('Flattened JWS must be an object'); + } + if (jws.protected === undefined && jws.header === undefined) { + throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); + } + if (jws.protected !== undefined && typeof jws.protected !== 'string') { + throw new JWSInvalid('JWS Protected Header incorrect type'); + } + if (jws.payload === undefined) { + throw new JWSInvalid('JWS Payload missing'); + } + if (typeof jws.signature !== 'string') { + throw new JWSInvalid('JWS Signature missing or incorrect type'); + } + if (jws.header !== undefined && !isObject(jws.header)) { + throw new JWSInvalid('JWS Unprotected Header incorrect type'); + } + let parsedProt = {}; + if (jws.protected) { + try { + const protectedHeader = base64url(jws.protected); + parsedProt = JSON.parse(decoder.decode(protectedHeader)); + } + catch { + throw new JWSInvalid('JWS Protected Header is invalid'); + } + } + if (!isDisjoint(parsedProt, jws.header)) { + throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); + } + const joseHeader = { + ...parsedProt, + ...jws.header, + }; + const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, parsedProt, joseHeader); + let b64 = true; + if (extensions.has('b64')) { + b64 = parsedProt.b64; + if (typeof b64 !== 'boolean') { + throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); + } + } + const { alg } = joseHeader; + if (typeof alg !== 'string' || !alg) { + throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); + } + const algorithms = options && validateAlgorithms('algorithms', options.algorithms); + if (algorithms && !algorithms.has(alg)) { + throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed'); + } + if (b64) { + if (typeof jws.payload !== 'string') { + throw new JWSInvalid('JWS Payload must be a string'); + } + } + else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) { + throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance'); + } + let resolvedKey = false; + if (typeof key === 'function') { + key = await key(parsedProt, jws); + resolvedKey = true; + } + checkKeyType(alg, key, 'verify'); + const data = concat(encoder.encode((_a = jws.protected) !== null && _a !== void 0 ? _a : ''), encoder.encode('.'), typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload); + const signature = base64url(jws.signature); + const verified = await verify(alg, key, signature, data); + if (!verified) { + throw new JWSSignatureVerificationFailed(); + } + let payload; + if (b64) { + payload = base64url(jws.payload); + } + else if (typeof jws.payload === 'string') { + payload = encoder.encode(jws.payload); + } + else { + payload = jws.payload; + } + const result = { payload }; + if (jws.protected !== undefined) { + result.protectedHeader = parsedProt; + } + if (jws.header !== undefined) { + result.unprotectedHeader = jws.header; + } + if (resolvedKey) { + return { ...result, key }; + } + return result; +} diff --git a/dist/node/esm/jws/general/sign.js b/dist/node/esm/jws/general/sign.js new file mode 100644 index 0000000000..0e0d645cac --- /dev/null +++ b/dist/node/esm/jws/general/sign.js @@ -0,0 +1,67 @@ +import { FlattenedSign } from '../flattened/sign.js'; +import { JWSInvalid } from '../../util/errors.js'; +class IndividualSignature { + constructor(sig, key, options) { + this.parent = sig; + this.key = key; + this.options = options; + } + setProtectedHeader(protectedHeader) { + if (this.protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this.protectedHeader = protectedHeader; + return this; + } + setUnprotectedHeader(unprotectedHeader) { + if (this.unprotectedHeader) { + throw new TypeError('setUnprotectedHeader can only be called once'); + } + this.unprotectedHeader = unprotectedHeader; + return this; + } + addSignature(...args) { + return this.parent.addSignature(...args); + } + sign(...args) { + return this.parent.sign(...args); + } + done() { + return this.parent; + } +} +export class GeneralSign { + constructor(payload) { + this._signatures = []; + this._payload = payload; + } + addSignature(key, options) { + const signature = new IndividualSignature(this, key, options); + this._signatures.push(signature); + return signature; + } + async sign() { + if (!this._signatures.length) { + throw new JWSInvalid('at least one signature must be added'); + } + const jws = { + signatures: [], + payload: '', + }; + for (let i = 0; i < this._signatures.length; i++) { + const signature = this._signatures[i]; + const flattened = new FlattenedSign(this._payload); + flattened.setProtectedHeader(signature.protectedHeader); + flattened.setUnprotectedHeader(signature.unprotectedHeader); + const { payload, ...rest } = await flattened.sign(signature.key, signature.options); + if (i === 0) { + jws.payload = payload; + } + else if (jws.payload !== payload) { + throw new JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)'); + } + jws.signatures.push(rest); + } + return jws; + } +} diff --git a/dist/node/esm/jws/general/verify.js b/dist/node/esm/jws/general/verify.js new file mode 100644 index 0000000000..c511b722dc --- /dev/null +++ b/dist/node/esm/jws/general/verify.js @@ -0,0 +1,24 @@ +import { flattenedVerify } from '../flattened/verify.js'; +import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; +import isObject from '../../lib/is_object.js'; +export async function generalVerify(jws, key, options) { + if (!isObject(jws)) { + throw new JWSInvalid('General JWS must be an object'); + } + if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { + throw new JWSInvalid('JWS Signatures missing or incorrect type'); + } + for (const signature of jws.signatures) { + try { + return await flattenedVerify({ + header: signature.header, + payload: jws.payload, + protected: signature.protected, + signature: signature.signature, + }, key, options); + } + catch { + } + } + throw new JWSSignatureVerificationFailed(); +} diff --git a/dist/node/esm/jwt/decrypt.js b/dist/node/esm/jwt/decrypt.js new file mode 100644 index 0000000000..1ec2be28f2 --- /dev/null +++ b/dist/node/esm/jwt/decrypt.js @@ -0,0 +1,23 @@ +import { compactDecrypt } from '../jwe/compact/decrypt.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { JWTClaimValidationFailed } from '../util/errors.js'; +export async function jwtDecrypt(jwt, key, options) { + const decrypted = await compactDecrypt(jwt, key, options); + const payload = jwtPayload(decrypted.protectedHeader, decrypted.plaintext, options); + const { protectedHeader } = decrypted; + if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { + throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); + } + if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { + throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); + } + if (protectedHeader.aud !== undefined && + JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { + throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); + } + const result = { payload, protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: decrypted.key }; + } + return result; +} diff --git a/dist/node/esm/jwt/encrypt.js b/dist/node/esm/jwt/encrypt.js new file mode 100644 index 0000000000..15252957ae --- /dev/null +++ b/dist/node/esm/jwt/encrypt.js @@ -0,0 +1,68 @@ +import { CompactEncrypt } from '../jwe/compact/encrypt.js'; +import { encoder } from '../lib/buffer_utils.js'; +import { ProduceJWT } from './produce.js'; +export class EncryptJWT extends ProduceJWT { + setProtectedHeader(protectedHeader) { + if (this._protectedHeader) { + throw new TypeError('setProtectedHeader can only be called once'); + } + this._protectedHeader = protectedHeader; + return this; + } + setKeyManagementParameters(parameters) { + if (this._keyManagementParameters) { + throw new TypeError('setKeyManagementParameters can only be called once'); + } + this._keyManagementParameters = parameters; + return this; + } + setContentEncryptionKey(cek) { + if (this._cek) { + throw new TypeError('setContentEncryptionKey can only be called once'); + } + this._cek = cek; + return this; + } + setInitializationVector(iv) { + if (this._iv) { + throw new TypeError('setInitializationVector can only be called once'); + } + this._iv = iv; + return this; + } + replicateIssuerAsHeader() { + this._replicateIssuerAsHeader = true; + return this; + } + replicateSubjectAsHeader() { + this._replicateSubjectAsHeader = true; + return this; + } + replicateAudienceAsHeader() { + this._replicateAudienceAsHeader = true; + return this; + } + async encrypt(key, options) { + const enc = new CompactEncrypt(encoder.encode(JSON.stringify(this._payload))); + if (this._replicateIssuerAsHeader) { + this._protectedHeader = { ...this._protectedHeader, iss: this._payload.iss }; + } + if (this._replicateSubjectAsHeader) { + this._protectedHeader = { ...this._protectedHeader, sub: this._payload.sub }; + } + if (this._replicateAudienceAsHeader) { + this._protectedHeader = { ...this._protectedHeader, aud: this._payload.aud }; + } + enc.setProtectedHeader(this._protectedHeader); + if (this._iv) { + enc.setInitializationVector(this._iv); + } + if (this._cek) { + enc.setContentEncryptionKey(this._cek); + } + if (this._keyManagementParameters) { + enc.setKeyManagementParameters(this._keyManagementParameters); + } + return enc.encrypt(key, options); + } +} diff --git a/dist/node/esm/jwt/produce.js b/dist/node/esm/jwt/produce.js new file mode 100644 index 0000000000..31c929a07c --- /dev/null +++ b/dist/node/esm/jwt/produce.js @@ -0,0 +1,54 @@ +import epoch from '../lib/epoch.js'; +import isObject from '../lib/is_object.js'; +import secs from '../lib/secs.js'; +export class ProduceJWT { + constructor(payload) { + if (!isObject(payload)) { + throw new TypeError('JWT Claims Set MUST be an object'); + } + this._payload = payload; + } + setIssuer(issuer) { + this._payload = { ...this._payload, iss: issuer }; + return this; + } + setSubject(subject) { + this._payload = { ...this._payload, sub: subject }; + return this; + } + setAudience(audience) { + this._payload = { ...this._payload, aud: audience }; + return this; + } + setJti(jwtId) { + this._payload = { ...this._payload, jti: jwtId }; + return this; + } + setNotBefore(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, nbf: input }; + } + else { + this._payload = { ...this._payload, nbf: epoch(new Date()) + secs(input) }; + } + return this; + } + setExpirationTime(input) { + if (typeof input === 'number') { + this._payload = { ...this._payload, exp: input }; + } + else { + this._payload = { ...this._payload, exp: epoch(new Date()) + secs(input) }; + } + return this; + } + setIssuedAt(input) { + if (typeof input === 'undefined') { + this._payload = { ...this._payload, iat: epoch(new Date()) }; + } + else { + this._payload = { ...this._payload, iat: input }; + } + return this; + } +} diff --git a/dist/node/esm/jwt/sign.js b/dist/node/esm/jwt/sign.js new file mode 100644 index 0000000000..62352fbfa2 --- /dev/null +++ b/dist/node/esm/jwt/sign.js @@ -0,0 +1,21 @@ +import { CompactSign } from '../jws/compact/sign.js'; +import { JWTInvalid } from '../util/errors.js'; +import { encoder } from '../lib/buffer_utils.js'; +import { ProduceJWT } from './produce.js'; +export class SignJWT extends ProduceJWT { + setProtectedHeader(protectedHeader) { + this._protectedHeader = protectedHeader; + return this; + } + async sign(key, options) { + var _a; + const sig = new CompactSign(encoder.encode(JSON.stringify(this._payload))); + sig.setProtectedHeader(this._protectedHeader); + if (Array.isArray((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.crit) && + this._protectedHeader.crit.includes('b64') && + this._protectedHeader.b64 === false) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + return sig.sign(key, options); + } +} diff --git a/dist/node/esm/jwt/unsecured.js b/dist/node/esm/jwt/unsecured.js new file mode 100644 index 0000000000..41c7c33bb5 --- /dev/null +++ b/dist/node/esm/jwt/unsecured.js @@ -0,0 +1,32 @@ +import * as base64url from '../runtime/base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import { JWTInvalid } from '../util/errors.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { ProduceJWT } from './produce.js'; +export class UnsecuredJWT extends ProduceJWT { + encode() { + const header = base64url.encode(JSON.stringify({ alg: 'none' })); + const payload = base64url.encode(JSON.stringify(this._payload)); + return `${header}.${payload}.`; + } + static decode(jwt, options) { + if (typeof jwt !== 'string') { + throw new JWTInvalid('Unsecured JWT must be a string'); + } + const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.'); + if (length !== 3 || signature !== '') { + throw new JWTInvalid('Invalid Unsecured JWT'); + } + let header; + try { + header = JSON.parse(decoder.decode(base64url.decode(encodedHeader))); + if (header.alg !== 'none') + throw new Error(); + } + catch { + throw new JWTInvalid('Invalid Unsecured JWT'); + } + const payload = jwtPayload(header, base64url.decode(encodedPayload), options); + return { payload, header }; + } +} diff --git a/dist/node/esm/jwt/verify.js b/dist/node/esm/jwt/verify.js new file mode 100644 index 0000000000..89571c1847 --- /dev/null +++ b/dist/node/esm/jwt/verify.js @@ -0,0 +1,16 @@ +import { compactVerify } from '../jws/compact/verify.js'; +import jwtPayload from '../lib/jwt_claims_set.js'; +import { JWTInvalid } from '../util/errors.js'; +export async function jwtVerify(jwt, key, options) { + var _a; + const verified = await compactVerify(jwt, key, options); + if (((_a = verified.protectedHeader.crit) === null || _a === void 0 ? void 0 : _a.includes('b64')) && verified.protectedHeader.b64 === false) { + throw new JWTInvalid('JWTs MUST NOT use unencoded payload'); + } + const payload = jwtPayload(verified.protectedHeader, verified.payload, options); + const result = { payload, protectedHeader: verified.protectedHeader }; + if (typeof key === 'function') { + return { ...result, key: verified.key }; + } + return result; +} diff --git a/dist/node/esm/key/export.js b/dist/node/esm/key/export.js new file mode 100644 index 0000000000..e4017047cd --- /dev/null +++ b/dist/node/esm/key/export.js @@ -0,0 +1,12 @@ +import { toSPKI as exportPublic } from '../runtime/asn1.js'; +import { toPKCS8 as exportPrivate } from '../runtime/asn1.js'; +import keyToJWK from '../runtime/key_to_jwk.js'; +export async function exportSPKI(key) { + return exportPublic(key); +} +export async function exportPKCS8(key) { + return exportPrivate(key); +} +export async function exportJWK(key) { + return keyToJWK(key); +} diff --git a/dist/node/esm/key/generate_key_pair.js b/dist/node/esm/key/generate_key_pair.js new file mode 100644 index 0000000000..03b9ee54cd --- /dev/null +++ b/dist/node/esm/key/generate_key_pair.js @@ -0,0 +1,4 @@ +import { generateKeyPair as generate } from '../runtime/generate.js'; +export async function generateKeyPair(alg, options) { + return generate(alg, options); +} diff --git a/dist/node/esm/key/generate_secret.js b/dist/node/esm/key/generate_secret.js new file mode 100644 index 0000000000..58f308a543 --- /dev/null +++ b/dist/node/esm/key/generate_secret.js @@ -0,0 +1,4 @@ +import { generateSecret as generate } from '../runtime/generate.js'; +export async function generateSecret(alg, options) { + return generate(alg, options); +} diff --git a/dist/node/esm/key/import.js b/dist/node/esm/key/import.js new file mode 100644 index 0000000000..25bb0e1659 --- /dev/null +++ b/dist/node/esm/key/import.js @@ -0,0 +1,50 @@ +import { decode as decodeBase64URL } from '../runtime/base64url.js'; +import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.js'; +import asKeyObject from '../runtime/jwk_to_key.js'; +import { JOSENotSupported } from '../util/errors.js'; +import isObject from '../lib/is_object.js'; +export async function importSPKI(spki, alg, options) { + if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) { + throw new TypeError('"spki" must be SPKI formatted string'); + } + return fromSPKI(spki, alg, options); +} +export async function importX509(x509, alg, options) { + if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) { + throw new TypeError('"x509" must be X.509 formatted string'); + } + return fromX509(x509, alg, options); +} +export async function importPKCS8(pkcs8, alg, options) { + if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) { + throw new TypeError('"pkcs8" must be PKCS#8 formatted string'); + } + return fromPKCS8(pkcs8, alg, options); +} +export async function importJWK(jwk, alg, octAsKeyObject) { + var _a; + if (!isObject(jwk)) { + throw new TypeError('JWK must be an object'); + } + alg || (alg = jwk.alg); + switch (jwk.kty) { + case 'oct': + if (typeof jwk.k !== 'string' || !jwk.k) { + throw new TypeError('missing "k" (Key Value) Parameter value'); + } + octAsKeyObject !== null && octAsKeyObject !== void 0 ? octAsKeyObject : (octAsKeyObject = jwk.ext !== true); + if (octAsKeyObject) { + return asKeyObject({ ...jwk, alg, ext: (_a = jwk.ext) !== null && _a !== void 0 ? _a : false }); + } + return decodeBase64URL(jwk.k); + case 'RSA': + if (jwk.oth !== undefined) { + throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported'); + } + case 'EC': + case 'OKP': + return asKeyObject({ ...jwk, alg }); + default: + throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value'); + } +} diff --git a/dist/node/esm/lib/aesgcmkw.js b/dist/node/esm/lib/aesgcmkw.js new file mode 100644 index 0000000000..de3f4f91df --- /dev/null +++ b/dist/node/esm/lib/aesgcmkw.js @@ -0,0 +1,14 @@ +import encrypt from '../runtime/encrypt.js'; +import decrypt from '../runtime/decrypt.js'; +import generateIv from './iv.js'; +import { encode as base64url } from '../runtime/base64url.js'; +export async function wrap(alg, key, cek, iv) { + const jweAlgorithm = alg.slice(0, 7); + iv || (iv = generateIv(jweAlgorithm)); + const { ciphertext: encryptedKey, tag } = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0)); + return { encryptedKey, iv: base64url(iv), tag: base64url(tag) }; +} +export async function unwrap(alg, key, encryptedKey, iv, tag) { + const jweAlgorithm = alg.slice(0, 7); + return decrypt(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0)); +} diff --git a/dist/node/esm/lib/buffer_utils.js b/dist/node/esm/lib/buffer_utils.js new file mode 100644 index 0000000000..5a1a7b334d --- /dev/null +++ b/dist/node/esm/lib/buffer_utils.js @@ -0,0 +1,51 @@ +import digest from '../runtime/digest.js'; +export const encoder = new TextEncoder(); +export const decoder = new TextDecoder(); +const MAX_INT32 = 2 ** 32; +export function concat(...buffers) { + const size = buffers.reduce((acc, { length }) => acc + length, 0); + const buf = new Uint8Array(size); + let i = 0; + buffers.forEach((buffer) => { + buf.set(buffer, i); + i += buffer.length; + }); + return buf; +} +export function p2s(alg, p2sInput) { + return concat(encoder.encode(alg), new Uint8Array([0]), p2sInput); +} +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 & 0xff], offset); +} +export 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; +} +export function uint32be(value) { + const buf = new Uint8Array(4); + writeUInt32BE(buf, value); + return buf; +} +export function lengthAndInput(input) { + return concat(uint32be(input.length), input); +} +export async function concatKdf(secret, bits, value) { + const iterations = Math.ceil((bits >> 3) / 32); + const res = new Uint8Array(iterations * 32); + for (let iter = 0; iter < iterations; iter++) { + const buf = new Uint8Array(4 + secret.length + value.length); + buf.set(uint32be(iter + 1)); + buf.set(secret, 4); + buf.set(value, 4 + secret.length); + res.set(await digest('sha256', buf), iter * 32); + } + return res.slice(0, bits >> 3); +} diff --git a/dist/node/esm/lib/cek.js b/dist/node/esm/lib/cek.js new file mode 100644 index 0000000000..34697d3ac2 --- /dev/null +++ b/dist/node/esm/lib/cek.js @@ -0,0 +1,20 @@ +import { JOSENotSupported } from '../util/errors.js'; +import random from '../runtime/random.js'; +export function bitLength(alg) { + switch (alg) { + case 'A128GCM': + return 128; + case 'A192GCM': + return 192; + case 'A256GCM': + case 'A128CBC-HS256': + return 256; + case 'A192CBC-HS384': + return 384; + case 'A256CBC-HS512': + return 512; + default: + throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`); + } +} +export default (alg) => random(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/node/esm/lib/check_iv_length.js b/dist/node/esm/lib/check_iv_length.js new file mode 100644 index 0000000000..dcd28d3a9d --- /dev/null +++ b/dist/node/esm/lib/check_iv_length.js @@ -0,0 +1,8 @@ +import { JWEInvalid } from '../util/errors.js'; +import { bitLength } from './iv.js'; +const checkIvLength = (enc, iv) => { + if (iv.length << 3 !== bitLength(enc)) { + throw new JWEInvalid('Invalid Initialization Vector length'); + } +}; +export default checkIvLength; diff --git a/dist/node/esm/lib/check_key_type.js b/dist/node/esm/lib/check_key_type.js new file mode 100644 index 0000000000..43f3dcbf14 --- /dev/null +++ b/dist/node/esm/lib/check_key_type.js @@ -0,0 +1,45 @@ +import { withAlg as invalidKeyInput } from './invalid_key_input.js'; +import isKeyLike, { types } from '../runtime/is_key_like.js'; +const symmetricTypeCheck = (alg, key) => { + if (key instanceof Uint8Array) + return; + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array')); + } + if (key.type !== 'secret') { + throw new TypeError(`${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`); + } +}; +const asymmetricTypeCheck = (alg, key, usage) => { + if (!isKeyLike(key)) { + throw new TypeError(invalidKeyInput(alg, key, ...types)); + } + if (key.type === 'secret') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`); + } + if (usage === 'sign' && key.type === 'public') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm signing must be of type "private"`); + } + if (usage === 'decrypt' && key.type === 'public') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm decryption must be of type "private"`); + } + if (key.algorithm && usage === 'verify' && key.type === 'private') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm verifying must be of type "public"`); + } + if (key.algorithm && usage === 'encrypt' && key.type === 'private') { + throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithm encryption must be of type "public"`); + } +}; +const checkKeyType = (alg, key, usage) => { + const symmetric = alg.startsWith('HS') || + alg === 'dir' || + alg.startsWith('PBES2') || + /^A\d{3}(?:GCM)?KW$/.test(alg); + if (symmetric) { + symmetricTypeCheck(alg, key); + } + else { + asymmetricTypeCheck(alg, key, usage); + } +}; +export default checkKeyType; diff --git a/dist/node/esm/lib/check_p2s.js b/dist/node/esm/lib/check_p2s.js new file mode 100644 index 0000000000..a65289fa7a --- /dev/null +++ b/dist/node/esm/lib/check_p2s.js @@ -0,0 +1,6 @@ +import { JWEInvalid } from '../util/errors.js'; +export default function checkP2s(p2s) { + if (!(p2s instanceof Uint8Array) || p2s.length < 8) { + throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets'); + } +} diff --git a/dist/node/esm/lib/crypto_key.js b/dist/node/esm/lib/crypto_key.js new file mode 100644 index 0000000000..4405b1915c --- /dev/null +++ b/dist/node/esm/lib/crypto_key.js @@ -0,0 +1,158 @@ +import { isCloudflareWorkers } from '../runtime/env.js'; +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 getNamedCurve(alg) { + switch (alg) { + case 'ES256': + return 'P-256'; + case 'ES384': + return 'P-384'; + case 'ES512': + return 'P-521'; + default: + throw new Error('unreachable'); + } +} +function checkUsage(key, usages) { + if (usages.length && !usages.some((expected) => key.usages.includes(expected))) { + let msg = 'CryptoKey does not support this operation, its usages must include '; + if (usages.length > 2) { + const last = usages.pop(); + msg += `one of ${usages.join(', ')}, or ${last}.`; + } + else if (usages.length === 2) { + msg += `one of ${usages[0]} or ${usages[1]}.`; + } + else { + msg += `${usages[0]}.`; + } + throw new TypeError(msg); + } +} +export function checkSigCryptoKey(key, alg, ...usages) { + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': { + if (!isAlgorithm(key.algorithm, 'HMAC')) + throw unusable('HMAC'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'RS256': + case 'RS384': + case 'RS512': { + if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5')) + throw unusable('RSASSA-PKCS1-v1_5'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'PS256': + case 'PS384': + case 'PS512': { + if (!isAlgorithm(key.algorithm, 'RSA-PSS')) + throw unusable('RSA-PSS'); + const expected = parseInt(alg.slice(2), 10); + const actual = getHashLength(key.algorithm.hash); + if (actual !== expected) + throw unusable(`SHA-${expected}`, 'algorithm.hash'); + break; + } + case 'EdDSA': { + if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') { + if (isCloudflareWorkers()) { + if (isAlgorithm(key.algorithm, 'NODE-ED25519')) + break; + throw unusable('Ed25519, Ed448, or NODE-ED25519'); + } + throw unusable('Ed25519 or Ed448'); + } + break; + } + case 'ES256': + case 'ES384': + case 'ES512': { + if (!isAlgorithm(key.algorithm, 'ECDSA')) + throw unusable('ECDSA'); + const expected = getNamedCurve(alg); + const actual = key.algorithm.namedCurve; + if (actual !== expected) + throw unusable(expected, 'algorithm.namedCurve'); + break; + } + default: + throw new TypeError('CryptoKey does not support this operation'); + } + checkUsage(key, usages); +} +export function checkEncCryptoKey(key, alg, ...usages) { + 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': + case 'X448': + break; + default: + throw unusable('ECDH, X25519, or X448'); + } + 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, usages); +} diff --git a/dist/node/esm/lib/decrypt_key_management.js b/dist/node/esm/lib/decrypt_key_management.js new file mode 100644 index 0000000000..87890a4fa0 --- /dev/null +++ b/dist/node/esm/lib/decrypt_key_management.js @@ -0,0 +1,98 @@ +import { unwrap as aesKw } from '../runtime/aeskw.js'; +import * as ECDH from '../runtime/ecdhes.js'; +import { decrypt as pbes2Kw } from '../runtime/pbes2kw.js'; +import { decrypt as rsaEs } from '../runtime/rsaes.js'; +import { decode as base64url } from '../runtime/base64url.js'; +import { JOSENotSupported, JWEInvalid } from '../util/errors.js'; +import { bitLength as cekLength } from '../lib/cek.js'; +import { importJWK } from '../key/import.js'; +import checkKeyType from './check_key_type.js'; +import isObject from './is_object.js'; +import { unwrap as aesGcmKw } from './aesgcmkw.js'; +async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) { + checkKeyType(alg, key, 'decrypt'); + switch (alg) { + case 'dir': { + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key'); + return key; + } + case 'ECDH-ES': + if (encryptedKey !== undefined) + throw new JWEInvalid('Encountered unexpected JWE Encrypted Key'); + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!isObject(joseHeader.epk)) + throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`); + if (!ECDH.ecdhAllowed(key)) + throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + const epk = await importJWK(joseHeader.epk, alg); + let partyUInfo; + let partyVInfo; + if (joseHeader.apu !== undefined) { + if (typeof joseHeader.apu !== 'string') + throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`); + partyUInfo = base64url(joseHeader.apu); + } + if (joseHeader.apv !== undefined) { + if (typeof joseHeader.apv !== 'string') + throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`); + partyVInfo = base64url(joseHeader.apv); + } + const sharedSecret = await ECDH.deriveKey(epk, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, alg === 'ECDH-ES' ? cekLength(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo); + if (alg === 'ECDH-ES') + return sharedSecret; + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return aesKw(alg.slice(-6), sharedSecret, encryptedKey); + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return rsaEs(alg, key, encryptedKey); + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.p2c !== 'number') + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`); + const p2cLimit = (options === null || options === void 0 ? void 0 : options.maxPBES2Count) || 10000; + if (joseHeader.p2c > p2cLimit) + throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`); + if (typeof joseHeader.p2s !== 'string') + throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`); + return pbes2Kw(alg, key, encryptedKey, joseHeader.p2c, base64url(joseHeader.p2s)); + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + return aesKw(alg, key, encryptedKey); + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + if (encryptedKey === undefined) + throw new JWEInvalid('JWE Encrypted Key missing'); + if (typeof joseHeader.iv !== 'string') + throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`); + if (typeof joseHeader.tag !== 'string') + throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`); + const iv = base64url(joseHeader.iv); + const tag = base64url(joseHeader.tag); + return aesGcmKw(alg, key, encryptedKey, iv, tag); + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } +} +export default decryptKeyManagement; diff --git a/dist/node/esm/lib/encrypt_key_management.js b/dist/node/esm/lib/encrypt_key_management.js new file mode 100644 index 0000000000..eb9022c63e --- /dev/null +++ b/dist/node/esm/lib/encrypt_key_management.js @@ -0,0 +1,87 @@ +import { wrap as aesKw } from '../runtime/aeskw.js'; +import * as ECDH from '../runtime/ecdhes.js'; +import { encrypt as pbes2Kw } from '../runtime/pbes2kw.js'; +import { encrypt as rsaEs } from '../runtime/rsaes.js'; +import { encode as base64url } from '../runtime/base64url.js'; +import generateCek, { bitLength as cekLength } from '../lib/cek.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { exportJWK } from '../key/export.js'; +import checkKeyType from './check_key_type.js'; +import { wrap as aesGcmKw } from './aesgcmkw.js'; +async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) { + let encryptedKey; + let parameters; + let cek; + checkKeyType(alg, key, 'encrypt'); + switch (alg) { + case 'dir': { + cek = key; + break; + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': { + if (!ECDH.ecdhAllowed(key)) { + throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime'); + } + const { apu, apv } = providedParameters; + let { epk: ephemeralKey } = providedParameters; + ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey); + const { x, y, crv, kty } = await exportJWK(ephemeralKey); + const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv); + parameters = { epk: { x, crv, kty } }; + if (kty === 'EC') + parameters.epk.y = y; + if (apu) + parameters.apu = base64url(apu); + if (apv) + parameters.apv = base64url(apv); + if (alg === 'ECDH-ES') { + cek = sharedSecret; + break; + } + cek = providedCek || generateCek(enc); + const kwAlg = alg.slice(-6); + encryptedKey = await aesKw(kwAlg, sharedSecret, cek); + break; + } + case 'RSA1_5': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': { + cek = providedCek || generateCek(enc); + encryptedKey = await rsaEs(alg, key, cek); + break; + } + case 'PBES2-HS256+A128KW': + case 'PBES2-HS384+A192KW': + case 'PBES2-HS512+A256KW': { + cek = providedCek || generateCek(enc); + const { p2c, p2s } = providedParameters; + ({ encryptedKey, ...parameters } = await pbes2Kw(alg, key, cek, p2c, p2s)); + break; + } + case 'A128KW': + case 'A192KW': + case 'A256KW': { + cek = providedCek || generateCek(enc); + encryptedKey = await aesKw(alg, key, cek); + break; + } + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': { + cek = providedCek || generateCek(enc); + const { iv } = providedParameters; + ({ encryptedKey, ...parameters } = await aesGcmKw(alg, key, cek, iv)); + break; + } + default: { + throw new JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value'); + } + } + return { cek, encryptedKey, parameters }; +} +export default encryptKeyManagement; diff --git a/dist/node/esm/lib/epoch.js b/dist/node/esm/lib/epoch.js new file mode 100644 index 0000000000..e405e4b2df --- /dev/null +++ b/dist/node/esm/lib/epoch.js @@ -0,0 +1 @@ +export default (date) => Math.floor(date.getTime() / 1000); diff --git a/dist/node/esm/lib/invalid_key_input.js b/dist/node/esm/lib/invalid_key_input.js new file mode 100644 index 0000000000..049e66ece0 --- /dev/null +++ b/dist/node/esm/lib/invalid_key_input.js @@ -0,0 +1,30 @@ +function message(msg, actual, ...types) { + 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 && actual.constructor.name) { + msg += ` Received an instance of ${actual.constructor.name}`; + } + } + return msg; +} +export default (actual, ...types) => { + return message('Key must be ', actual, ...types); +}; +export function withAlg(alg, actual, ...types) { + return message(`Key for the ${alg} algorithm must be `, actual, ...types); +} diff --git a/dist/node/esm/lib/is_disjoint.js b/dist/node/esm/lib/is_disjoint.js new file mode 100644 index 0000000000..6f643502dc --- /dev/null +++ b/dist/node/esm/lib/is_disjoint.js @@ -0,0 +1,22 @@ +const isDisjoint = (...headers) => { + const sources = headers.filter(Boolean); + if (sources.length === 0 || sources.length === 1) { + return true; + } + let acc; + for (const header of sources) { + const parameters = Object.keys(header); + if (!acc || acc.size === 0) { + acc = new Set(parameters); + continue; + } + for (const parameter of parameters) { + if (acc.has(parameter)) { + return false; + } + acc.add(parameter); + } + } + return true; +}; +export default isDisjoint; diff --git a/dist/node/esm/lib/is_object.js b/dist/node/esm/lib/is_object.js new file mode 100644 index 0000000000..4955e93225 --- /dev/null +++ b/dist/node/esm/lib/is_object.js @@ -0,0 +1,16 @@ +function isObjectLike(value) { + return typeof value === 'object' && value !== null; +} +export default function isObject(input) { + if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') { + return false; + } + if (Object.getPrototypeOf(input) === null) { + return true; + } + let proto = input; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(input) === proto; +} diff --git a/dist/node/esm/lib/iv.js b/dist/node/esm/lib/iv.js new file mode 100644 index 0000000000..cab2a12729 --- /dev/null +++ b/dist/node/esm/lib/iv.js @@ -0,0 +1,20 @@ +import { JOSENotSupported } from '../util/errors.js'; +import random from '../runtime/random.js'; +export 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}`); + } +} +export default (alg) => random(new Uint8Array(bitLength(alg) >> 3)); diff --git a/dist/node/esm/lib/jwt_claims_set.js b/dist/node/esm/lib/jwt_claims_set.js new file mode 100644 index 0000000000..fc4193ad90 --- /dev/null +++ b/dist/node/esm/lib/jwt_claims_set.js @@ -0,0 +1,91 @@ +import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'; +import { decoder } from './buffer_utils.js'; +import epoch from './epoch.js'; +import secs from './secs.js'; +import isObject from './is_object.js'; +const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, ''); +const checkAudiencePresence = (audPayload, audOption) => { + if (typeof audPayload === 'string') { + return audOption.includes(audPayload); + } + if (Array.isArray(audPayload)) { + return audOption.some(Set.prototype.has.bind(new Set(audPayload))); + } + return false; +}; +export default (protectedHeader, encodedPayload, options = {}) => { + const { typ } = options; + if (typ && + (typeof protectedHeader.typ !== 'string' || + normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) { + throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', 'typ', 'check_failed'); + } + let payload; + try { + payload = JSON.parse(decoder.decode(encodedPayload)); + } + catch { + } + if (!isObject(payload)) { + throw new JWTInvalid('JWT Claims Set must be a top-level JSON object'); + } + const { issuer } = options; + if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) { + throw new JWTClaimValidationFailed('unexpected "iss" claim value', 'iss', 'check_failed'); + } + const { subject } = options; + if (subject && payload.sub !== subject) { + throw new JWTClaimValidationFailed('unexpected "sub" claim value', 'sub', 'check_failed'); + } + const { audience } = options; + if (audience && + !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) { + throw new JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed'); + } + let tolerance; + switch (typeof options.clockTolerance) { + case 'string': + tolerance = secs(options.clockTolerance); + break; + case 'number': + tolerance = options.clockTolerance; + break; + case 'undefined': + tolerance = 0; + break; + default: + throw new TypeError('Invalid clockTolerance option type'); + } + const { currentDate } = options; + const now = epoch(currentDate || new Date()); + if ((payload.iat !== undefined || options.maxTokenAge) && typeof payload.iat !== 'number') { + throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); + } + if (payload.nbf !== undefined) { + if (typeof payload.nbf !== 'number') { + throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); + } + if (payload.nbf > now + tolerance) { + throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', 'nbf', 'check_failed'); + } + } + if (payload.exp !== undefined) { + if (typeof payload.exp !== 'number') { + throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); + } + if (payload.exp <= now - tolerance) { + throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed'); + } + } + if (options.maxTokenAge) { + const age = now - payload.iat; + const max = typeof options.maxTokenAge === 'number' ? options.maxTokenAge : secs(options.maxTokenAge); + if (age - tolerance > max) { + throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed'); + } + if (age < 0 - tolerance) { + throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); + } + } + return payload; +}; diff --git a/dist/node/esm/lib/secs.js b/dist/node/esm/lib/secs.js new file mode 100644 index 0000000000..cf470ed8ad --- /dev/null +++ b/dist/node/esm/lib/secs.js @@ -0,0 +1,44 @@ +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const year = day * 365.25; +const REGEX = /^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i; +export default (str) => { + const matched = REGEX.exec(str); + if (!matched) { + throw new TypeError('Invalid time period format'); + } + const value = parseFloat(matched[1]); + const unit = matched[2].toLowerCase(); + switch (unit) { + case 'sec': + case 'secs': + case 'second': + case 'seconds': + case 's': + return Math.round(value); + case 'minute': + case 'minutes': + case 'min': + case 'mins': + case 'm': + return Math.round(value * minute); + case 'hour': + case 'hours': + case 'hr': + case 'hrs': + case 'h': + return Math.round(value * hour); + case 'day': + case 'days': + case 'd': + return Math.round(value * day); + case 'week': + case 'weeks': + case 'w': + return Math.round(value * week); + default: + return Math.round(value * year); + } +}; diff --git a/dist/node/esm/lib/validate_algorithms.js b/dist/node/esm/lib/validate_algorithms.js new file mode 100644 index 0000000000..a6a7918571 --- /dev/null +++ b/dist/node/esm/lib/validate_algorithms.js @@ -0,0 +1,11 @@ +const validateAlgorithms = (option, algorithms) => { + if (algorithms !== undefined && + (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) { + throw new TypeError(`"${option}" option must be an array of strings`); + } + if (!algorithms) { + return undefined; + } + return new Set(algorithms); +}; +export default validateAlgorithms; diff --git a/dist/node/esm/lib/validate_crit.js b/dist/node/esm/lib/validate_crit.js new file mode 100644 index 0000000000..68c69f18f5 --- /dev/null +++ b/dist/node/esm/lib/validate_crit.js @@ -0,0 +1,34 @@ +import { JOSENotSupported } from '../util/errors.js'; +function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { + if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { + throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); + } + if (!protectedHeader || protectedHeader.crit === undefined) { + return new Set(); + } + if (!Array.isArray(protectedHeader.crit) || + protectedHeader.crit.length === 0 || + protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) { + throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); + } + let recognized; + if (recognizedOption !== undefined) { + recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); + } + else { + recognized = recognizedDefault; + } + for (const parameter of protectedHeader.crit) { + if (!recognized.has(parameter)) { + throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); + } + if (joseHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" is missing`); + } + else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) { + throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); + } + } + return new Set(protectedHeader.crit); +} +export default validateCrit; diff --git a/dist/node/esm/package.json b/dist/node/esm/package.json new file mode 100644 index 0000000000..6990891ff3 --- /dev/null +++ b/dist/node/esm/package.json @@ -0,0 +1 @@ +{"type": "module"} diff --git a/dist/node/esm/runtime/aeskw.js b/dist/node/esm/runtime/aeskw.js new file mode 100644 index 0000000000..722b8b37e5 --- /dev/null +++ b/dist/node/esm/runtime/aeskw.js @@ -0,0 +1,50 @@ +import { Buffer } from 'buffer'; +import { KeyObject, createDecipheriv, createCipheriv, createSecretKey } from 'crypto'; +import { JOSENotSupported } from '../util/errors.js'; +import { concat } from '../lib/buffer_utils.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import supported from './ciphers.js'; +import { types } from './is_key_like.js'; +function checkKeySize(key, alg) { + if (key.symmetricKeySize << 3 !== parseInt(alg.slice(1, 4), 10)) { + throw new TypeError(`Invalid key size for alg: ${alg}`); + } +} +function ensureKeyObject(key, alg, usage) { + if (isKeyObject(key)) { + return key; + } + if (key instanceof Uint8Array) { + return createSecretKey(key); + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, usage); + return KeyObject.from(key); + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} +export const wrap = (alg, key, cek) => { + const size = parseInt(alg.slice(1, 4), 10); + const algorithm = `aes${size}-wrap`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + const keyObject = ensureKeyObject(key, alg, 'wrapKey'); + checkKeySize(keyObject, alg); + const cipher = createCipheriv(algorithm, keyObject, Buffer.alloc(8, 0xa6)); + return concat(cipher.update(cek), cipher.final()); +}; +export const unwrap = (alg, key, encryptedKey) => { + const size = parseInt(alg.slice(1, 4), 10); + const algorithm = `aes${size}-wrap`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } + const keyObject = ensureKeyObject(key, alg, 'unwrapKey'); + checkKeySize(keyObject, alg); + const cipher = createDecipheriv(algorithm, keyObject, Buffer.alloc(8, 0xa6)); + return concat(cipher.update(encryptedKey), cipher.final()); +}; diff --git a/dist/node/esm/runtime/asn1.js b/dist/node/esm/runtime/asn1.js new file mode 100644 index 0000000000..d6f447eac0 --- /dev/null +++ b/dist/node/esm/runtime/asn1.js @@ -0,0 +1,46 @@ +import { createPrivateKey, createPublicKey, KeyObject } from 'crypto'; +import { Buffer } from 'buffer'; +import { isCryptoKey } from './webcrypto.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +const genericExport = (keyType, keyFormat, key) => { + let keyObject; + if (isCryptoKey(key)) { + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable'); + } + keyObject = KeyObject.from(key); + } + else if (isKeyObject(key)) { + keyObject = key; + } + else { + throw new TypeError(invalidKeyInput(key, ...types)); + } + if (keyObject.type !== keyType) { + throw new TypeError(`key is not a ${keyType} key`); + } + return keyObject.export({ format: 'pem', type: keyFormat }); +}; +export const toSPKI = (key) => { + return genericExport('public', 'spki', key); +}; +export const toPKCS8 = (key) => { + return genericExport('private', 'pkcs8', key); +}; +export const fromPKCS8 = (pem) => createPrivateKey({ + key: Buffer.from(pem.replace(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, ''), 'base64'), + type: 'pkcs8', + format: 'der', +}); +export const fromSPKI = (pem) => createPublicKey({ + key: Buffer.from(pem.replace(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, ''), 'base64'), + type: 'spki', + format: 'der', +}); +export const fromX509 = (pem) => createPublicKey({ + key: pem, + type: 'spki', + format: 'pem', +}); diff --git a/dist/node/esm/runtime/asn1_sequence_decoder.js b/dist/node/esm/runtime/asn1_sequence_decoder.js new file mode 100644 index 0000000000..61ba4c3116 --- /dev/null +++ b/dist/node/esm/runtime/asn1_sequence_decoder.js @@ -0,0 +1,44 @@ +const tagInteger = 0x02; +const tagSequence = 0x30; +export default class Asn1SequenceDecoder { + constructor(buffer) { + if (buffer[0] !== tagSequence) { + throw new TypeError(); + } + this.buffer = buffer; + this.offset = 1; + const len = this.decodeLength(); + if (len !== buffer.length - this.offset) { + throw new TypeError(); + } + } + decodeLength() { + let length = this.buffer[this.offset++]; + if (length & 0x80) { + const nBytes = length & ~0x80; + length = 0; + for (let i = 0; i < nBytes; i++) + length = (length << 8) | this.buffer[this.offset + i]; + this.offset += nBytes; + } + return length; + } + unsignedInteger() { + if (this.buffer[this.offset++] !== tagInteger) { + throw new TypeError(); + } + let length = this.decodeLength(); + if (this.buffer[this.offset] === 0) { + this.offset++; + length--; + } + const result = this.buffer.slice(this.offset, this.offset + length); + this.offset += length; + return result; + } + end() { + if (this.offset !== this.buffer.length) { + throw new TypeError(); + } + } +} diff --git a/dist/node/esm/runtime/asn1_sequence_encoder.js b/dist/node/esm/runtime/asn1_sequence_encoder.js new file mode 100644 index 0000000000..ac672776a4 --- /dev/null +++ b/dist/node/esm/runtime/asn1_sequence_encoder.js @@ -0,0 +1,88 @@ +import { Buffer } from 'buffer'; +import { JOSENotSupported } from '../util/errors.js'; +const tagInteger = 0x02; +const tagBitStr = 0x03; +const tagOctStr = 0x04; +const tagSequence = 0x30; +const bZero = Buffer.from([0x00]); +const bTagInteger = Buffer.from([tagInteger]); +const bTagBitStr = Buffer.from([tagBitStr]); +const bTagSequence = Buffer.from([tagSequence]); +const bTagOctStr = Buffer.from([tagOctStr]); +const encodeLength = (len) => { + if (len < 128) + return Buffer.from([len]); + const buffer = Buffer.alloc(5); + buffer.writeUInt32BE(len, 1); + let offset = 1; + while (buffer[offset] === 0) + offset++; + buffer[offset - 1] = 0x80 | (5 - offset); + return buffer.slice(offset - 1); +}; +const oids = new Map([ + ['P-256', Buffer.from('06 08 2A 86 48 CE 3D 03 01 07'.replace(/ /g, ''), 'hex')], + ['secp256k1', Buffer.from('06 05 2B 81 04 00 0A'.replace(/ /g, ''), 'hex')], + ['P-384', Buffer.from('06 05 2B 81 04 00 22'.replace(/ /g, ''), 'hex')], + ['P-521', Buffer.from('06 05 2B 81 04 00 23'.replace(/ /g, ''), 'hex')], + ['ecPublicKey', Buffer.from('06 07 2A 86 48 CE 3D 02 01'.replace(/ /g, ''), 'hex')], + ['X25519', Buffer.from('06 03 2B 65 6E'.replace(/ /g, ''), 'hex')], + ['X448', Buffer.from('06 03 2B 65 6F'.replace(/ /g, ''), 'hex')], + ['Ed25519', Buffer.from('06 03 2B 65 70'.replace(/ /g, ''), 'hex')], + ['Ed448', Buffer.from('06 03 2B 65 71'.replace(/ /g, ''), 'hex')], +]); +export default class DumbAsn1Encoder { + constructor() { + this.length = 0; + this.elements = []; + } + oidFor(oid) { + const bOid = oids.get(oid); + if (!bOid) { + throw new JOSENotSupported('Invalid or unsupported OID'); + } + this.elements.push(bOid); + this.length += bOid.length; + } + zero() { + this.elements.push(bTagInteger, Buffer.from([0x01]), bZero); + this.length += 3; + } + one() { + this.elements.push(bTagInteger, Buffer.from([0x01]), Buffer.from([0x01])); + this.length += 3; + } + unsignedInteger(integer) { + if (integer[0] & 0x80) { + const len = encodeLength(integer.length + 1); + this.elements.push(bTagInteger, len, bZero, integer); + this.length += 2 + len.length + integer.length; + } + else { + let i = 0; + while (integer[i] === 0 && (integer[i + 1] & 0x80) === 0) + i++; + const len = encodeLength(integer.length - i); + this.elements.push(bTagInteger, encodeLength(integer.length - i), integer.slice(i)); + this.length += 1 + len.length + integer.length - i; + } + } + octStr(octStr) { + const len = encodeLength(octStr.length); + this.elements.push(bTagOctStr, encodeLength(octStr.length), octStr); + this.length += 1 + len.length + octStr.length; + } + bitStr(bitS) { + const len = encodeLength(bitS.length + 1); + this.elements.push(bTagBitStr, encodeLength(bitS.length + 1), bZero, bitS); + this.length += 1 + len.length + bitS.length + 1; + } + add(seq) { + this.elements.push(seq); + this.length += seq.length; + } + end(tag = bTagSequence) { + const len = encodeLength(this.length); + return Buffer.concat([tag, len, ...this.elements], 1 + len.length + this.length); + } +} diff --git a/dist/node/esm/runtime/base64url.js b/dist/node/esm/runtime/base64url.js new file mode 100644 index 0000000000..1579359fdf --- /dev/null +++ b/dist/node/esm/runtime/base64url.js @@ -0,0 +1,20 @@ +import { Buffer } from 'buffer'; +import { decoder } from '../lib/buffer_utils.js'; +let encode; +function normalize(input) { + let encoded = input; + if (encoded instanceof Uint8Array) { + encoded = decoder.decode(encoded); + } + return encoded; +} +if (Buffer.isEncoding('base64url')) { + encode = (input) => Buffer.from(input).toString('base64url'); +} +else { + encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); +} +export const decodeBase64 = (input) => Buffer.from(input, 'base64'); +export const encodeBase64 = (input) => Buffer.from(input).toString('base64'); +export { encode }; +export const decode = (input) => Buffer.from(normalize(input), 'base64'); diff --git a/dist/node/esm/runtime/cbc_tag.js b/dist/node/esm/runtime/cbc_tag.js new file mode 100644 index 0000000000..fa58c38b6c --- /dev/null +++ b/dist/node/esm/runtime/cbc_tag.js @@ -0,0 +1,8 @@ +import { createHmac } from 'crypto'; +import { concat, uint64be } from '../lib/buffer_utils.js'; +export default function cbcTag(aad, iv, ciphertext, macSize, macKey, keySize) { + const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)); + const hmac = createHmac(`sha${macSize}`, macKey); + hmac.update(macData); + return hmac.digest().slice(0, keySize >> 3); +} diff --git a/dist/node/esm/runtime/check_cek_length.js b/dist/node/esm/runtime/check_cek_length.js new file mode 100644 index 0000000000..60884c0ea5 --- /dev/null +++ b/dist/node/esm/runtime/check_cek_length.js @@ -0,0 +1,35 @@ +import { JWEInvalid, JOSENotSupported } from '../util/errors.js'; +import isKeyObject from './is_key_object.js'; +const checkCekLength = (enc, cek) => { + let expected; + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + expected = parseInt(enc.slice(-3), 10); + break; + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + expected = parseInt(enc.slice(1, 4), 10); + break; + default: + throw new JOSENotSupported(`Content Encryption Algorithm ${enc} is not supported either by JOSE or your javascript runtime`); + } + if (cek instanceof Uint8Array) { + const actual = cek.byteLength << 3; + if (actual !== expected) { + throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } + return; + } + if (isKeyObject(cek) && cek.type === 'secret') { + const actual = cek.symmetricKeySize << 3; + if (actual !== expected) { + throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`); + } + return; + } + throw new TypeError('Invalid Content Encryption Key type'); +}; +export default checkCekLength; diff --git a/dist/node/esm/runtime/check_modulus_length.js b/dist/node/esm/runtime/check_modulus_length.js new file mode 100644 index 0000000000..690f809602 --- /dev/null +++ b/dist/node/esm/runtime/check_modulus_length.js @@ -0,0 +1,48 @@ +export const weakMap = new WeakMap(); +const getLength = (buf, index) => { + let len = buf.readUInt8(1); + if ((len & 0x80) === 0) { + if (index === 0) { + return len; + } + return getLength(buf.subarray(2 + len), index - 1); + } + const num = len & 0x7f; + len = 0; + for (let i = 0; i < num; i++) { + len <<= 8; + const j = buf.readUInt8(2 + i); + len |= j; + } + if (index === 0) { + return len; + } + return getLength(buf.subarray(2 + len), index - 1); +}; +const getLengthOfSeqIndex = (sequence, index) => { + const len = sequence.readUInt8(1); + if ((len & 0x80) === 0) { + return getLength(sequence.subarray(2), index); + } + const num = len & 0x7f; + return getLength(sequence.subarray(2 + num), index); +}; +const getModulusLength = (key) => { + var _a, _b; + if (weakMap.has(key)) { + return weakMap.get(key); + } + const modulusLength = (_b = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.modulusLength) !== null && _b !== void 0 ? _b : (getLengthOfSeqIndex(key.export({ format: 'der', type: 'pkcs1' }), key.type === 'private' ? 1 : 0) - + 1) << + 3; + weakMap.set(key, modulusLength); + return modulusLength; +}; +export const setModulusLength = (keyObject, modulusLength) => { + weakMap.set(keyObject, modulusLength); +}; +export default (key, alg) => { + if (getModulusLength(key) < 2048) { + throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); + } +}; diff --git a/dist/node/esm/runtime/ciphers.js b/dist/node/esm/runtime/ciphers.js new file mode 100644 index 0000000000..daded1d503 --- /dev/null +++ b/dist/node/esm/runtime/ciphers.js @@ -0,0 +1,6 @@ +import { getCiphers } from 'crypto'; +let ciphers; +export default (algorithm) => { + ciphers || (ciphers = new Set(getCiphers())); + return ciphers.has(algorithm); +}; diff --git a/dist/node/esm/runtime/decrypt.js b/dist/node/esm/runtime/decrypt.js new file mode 100644 index 0000000000..8a611d174b --- /dev/null +++ b/dist/node/esm/runtime/decrypt.js @@ -0,0 +1,95 @@ +import { createDecipheriv, KeyObject } from 'crypto'; +import checkIvLength from '../lib/check_iv_length.js'; +import checkCekLength from './check_cek_length.js'; +import { concat } from '../lib/buffer_utils.js'; +import { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.js'; +import timingSafeEqual from './timing_safe_equal.js'; +import cbcTag from './cbc_tag.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import supported from './ciphers.js'; +import { types } from './is_key_like.js'; +function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + if (isKeyObject(cek)) { + cek = cek.export(); + } + const encKey = cek.subarray(keySize >> 3); + const macKey = cek.subarray(0, keySize >> 3); + const macSize = parseInt(enc.slice(-3), 10); + const algorithm = `aes-${keySize}-cbc`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const expectedTag = cbcTag(aad, iv, ciphertext, macSize, macKey, keySize); + let macCheckPassed; + try { + macCheckPassed = timingSafeEqual(tag, expectedTag); + } + catch { + } + if (!macCheckPassed) { + throw new JWEDecryptionFailed(); + } + let plaintext; + try { + const decipher = createDecipheriv(algorithm, encKey, iv); + plaintext = concat(decipher.update(ciphertext), decipher.final()); + } + catch { + } + if (!plaintext) { + throw new JWEDecryptionFailed(); + } + return plaintext; +} +function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + const algorithm = `aes-${keySize}-gcm`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + try { + const decipher = createDecipheriv(algorithm, cek, iv, { authTagLength: 16 }); + decipher.setAuthTag(tag); + if (aad.byteLength) { + decipher.setAAD(aad, { plaintextLength: ciphertext.length }); + } + const plaintext = decipher.update(ciphertext); + decipher.final(); + return plaintext; + } + catch { + throw new JWEDecryptionFailed(); + } +} +const decrypt = (enc, cek, ciphertext, iv, tag, aad) => { + let key; + if (isCryptoKey(cek)) { + checkEncCryptoKey(cek, enc, 'decrypt'); + key = KeyObject.from(cek); + } + else if (cek instanceof Uint8Array || isKeyObject(cek)) { + key = cek; + } + else { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')); + } + checkCekLength(enc, key); + checkIvLength(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + return cbcDecrypt(enc, key, ciphertext, iv, tag, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + return gcmDecrypt(enc, key, ciphertext, iv, tag, aad); + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +export default decrypt; diff --git a/dist/node/esm/runtime/digest.js b/dist/node/esm/runtime/digest.js new file mode 100644 index 0000000000..0facde6c29 --- /dev/null +++ b/dist/node/esm/runtime/digest.js @@ -0,0 +1,3 @@ +import { createHash } from 'crypto'; +const digest = (algorithm, data) => createHash(algorithm).update(data).digest(); +export default digest; diff --git a/dist/node/esm/runtime/dsa_digest.js b/dist/node/esm/runtime/dsa_digest.js new file mode 100644 index 0000000000..ce7215bd67 --- /dev/null +++ b/dist/node/esm/runtime/dsa_digest.js @@ -0,0 +1,22 @@ +import { JOSENotSupported } from '../util/errors.js'; +export default function dsaDigest(alg) { + switch (alg) { + case 'PS256': + case 'RS256': + case 'ES256': + case 'ES256K': + return 'sha256'; + case 'PS384': + case 'RS384': + case 'ES384': + return 'sha384'; + case 'PS512': + case 'RS512': + case 'ES512': + return 'sha512'; + case 'EdDSA': + return undefined; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} diff --git a/dist/node/esm/runtime/ecdhes.js b/dist/node/esm/runtime/ecdhes.js new file mode 100644 index 0000000000..d3faef4a05 --- /dev/null +++ b/dist/node/esm/runtime/ecdhes.js @@ -0,0 +1,64 @@ +import { diffieHellman, generateKeyPair as generateKeyPairCb, KeyObject } from 'crypto'; +import { promisify } from 'util'; +import getNamedCurve from './get_named_curve.js'; +import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +const generateKeyPair = promisify(generateKeyPairCb); +export async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { + let publicKey; + if (isCryptoKey(publicKee)) { + checkEncCryptoKey(publicKee, 'ECDH'); + publicKey = KeyObject.from(publicKee); + } + else if (isKeyObject(publicKee)) { + publicKey = publicKee; + } + else { + throw new TypeError(invalidKeyInput(publicKee, ...types)); + } + let privateKey; + if (isCryptoKey(privateKee)) { + checkEncCryptoKey(privateKee, 'ECDH', 'deriveBits'); + privateKey = KeyObject.from(privateKee); + } + else if (isKeyObject(privateKee)) { + privateKey = privateKee; + } + else { + throw new TypeError(invalidKeyInput(privateKee, ...types)); + } + const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); + const sharedSecret = diffieHellman({ privateKey, publicKey }); + return concatKdf(sharedSecret, keyLength, value); +} +export async function generateEpk(kee) { + let key; + if (isCryptoKey(kee)) { + key = KeyObject.from(kee); + } + else if (isKeyObject(kee)) { + key = kee; + } + else { + throw new TypeError(invalidKeyInput(kee, ...types)); + } + switch (key.asymmetricKeyType) { + case 'x25519': + return generateKeyPair('x25519'); + case 'x448': { + return generateKeyPair('x448'); + } + case 'ec': { + const namedCurve = getNamedCurve(key); + return generateKeyPair('ec', { namedCurve }); + } + default: + throw new JOSENotSupported('Invalid or unsupported EPK'); + } +} +export const ecdhAllowed = (key) => ['P-256', 'P-384', 'P-521', 'X25519', 'X448'].includes(getNamedCurve(key)); diff --git a/dist/node/esm/runtime/encrypt.js b/dist/node/esm/runtime/encrypt.js new file mode 100644 index 0000000000..8dfb87b10c --- /dev/null +++ b/dist/node/esm/runtime/encrypt.js @@ -0,0 +1,72 @@ +import { createCipheriv, KeyObject } from 'crypto'; +import checkIvLength from '../lib/check_iv_length.js'; +import checkCekLength from './check_cek_length.js'; +import { concat } from '../lib/buffer_utils.js'; +import cbcTag from './cbc_tag.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { JOSENotSupported } from '../util/errors.js'; +import supported from './ciphers.js'; +import { types } from './is_key_like.js'; +function cbcEncrypt(enc, plaintext, cek, iv, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + if (isKeyObject(cek)) { + cek = cek.export(); + } + const encKey = cek.subarray(keySize >> 3); + const macKey = cek.subarray(0, keySize >> 3); + const algorithm = `aes-${keySize}-cbc`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const cipher = createCipheriv(algorithm, encKey, iv); + const ciphertext = concat(cipher.update(plaintext), cipher.final()); + const macSize = parseInt(enc.slice(-3), 10); + const tag = cbcTag(aad, iv, ciphertext, macSize, macKey, keySize); + return { ciphertext, tag }; +} +function gcmEncrypt(enc, plaintext, cek, iv, aad) { + const keySize = parseInt(enc.slice(1, 4), 10); + const algorithm = `aes-${keySize}-gcm`; + if (!supported(algorithm)) { + throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`); + } + const cipher = createCipheriv(algorithm, cek, iv, { authTagLength: 16 }); + if (aad.byteLength) { + cipher.setAAD(aad, { plaintextLength: plaintext.length }); + } + const ciphertext = cipher.update(plaintext); + cipher.final(); + const tag = cipher.getAuthTag(); + return { ciphertext, tag }; +} +const encrypt = (enc, plaintext, cek, iv, aad) => { + let key; + if (isCryptoKey(cek)) { + checkEncCryptoKey(cek, enc, 'encrypt'); + key = KeyObject.from(cek); + } + else if (cek instanceof Uint8Array || isKeyObject(cek)) { + key = cek; + } + else { + throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array')); + } + checkCekLength(enc, key); + checkIvLength(enc, iv); + switch (enc) { + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + return cbcEncrypt(enc, plaintext, key, iv, aad); + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + return gcmEncrypt(enc, plaintext, key, iv, aad); + default: + throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm'); + } +}; +export default encrypt; diff --git a/dist/node/esm/runtime/env.js b/dist/node/esm/runtime/env.js new file mode 100644 index 0000000000..f55de30dc1 --- /dev/null +++ b/dist/node/esm/runtime/env.js @@ -0,0 +1,3 @@ +export function isCloudflareWorkers() { + return false; +} diff --git a/dist/node/esm/runtime/fetch_jwks.js b/dist/node/esm/runtime/fetch_jwks.js new file mode 100644 index 0000000000..5853424ec1 --- /dev/null +++ b/dist/node/esm/runtime/fetch_jwks.js @@ -0,0 +1,43 @@ +import * as http from 'http'; +import * as https from 'https'; +import { once } from 'events'; +import { JOSEError, JWKSTimeout } from '../util/errors.js'; +import { concat, decoder } from '../lib/buffer_utils.js'; +const fetchJwks = async (url, timeout, options) => { + let get; + switch (url.protocol) { + case 'https:': + get = https.get; + break; + case 'http:': + get = http.get; + break; + default: + throw new TypeError('Unsupported URL protocol.'); + } + const { agent, headers } = options; + const req = get(url.href, { + agent, + timeout, + headers, + }); + const [response] = (await Promise.race([once(req, 'response'), once(req, 'timeout')])); + if (!response) { + req.destroy(); + throw new JWKSTimeout(); + } + if (response.statusCode !== 200) { + throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response'); + } + const parts = []; + for await (const part of response) { + parts.push(part); + } + try { + return JSON.parse(decoder.decode(concat(...parts))); + } + catch { + throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON'); + } +}; +export default fetchJwks; diff --git a/dist/node/esm/runtime/flags.js b/dist/node/esm/runtime/flags.js new file mode 100644 index 0000000000..390299593f --- /dev/null +++ b/dist/node/esm/runtime/flags.js @@ -0,0 +1,5 @@ +const [major, minor] = process.versions.node.split('.').map((str) => parseInt(str, 10)); +export const oneShotCallback = major >= 16 || (major === 15 && minor >= 13); +export const rsaPssParams = !('electron' in process.versions) && (major >= 17 || (major === 16 && minor >= 9)); +export const jwkExport = major >= 16 || (major === 15 && minor >= 9); +export const jwkImport = major >= 16 || (major === 15 && minor >= 12); diff --git a/dist/node/esm/runtime/generate.js b/dist/node/esm/runtime/generate.js new file mode 100644 index 0000000000..64f76962f1 --- /dev/null +++ b/dist/node/esm/runtime/generate.js @@ -0,0 +1,100 @@ +import { createSecretKey, generateKeyPair as generateKeyPairCb } from 'crypto'; +import { promisify } from 'util'; +import random from './random.js'; +import { setModulusLength } from './check_modulus_length.js'; +import { JOSENotSupported } from '../util/errors.js'; +const generate = promisify(generateKeyPairCb); +export async function generateSecret(alg, options) { + let length; + switch (alg) { + case 'HS256': + case 'HS384': + case 'HS512': + case 'A128CBC-HS256': + case 'A192CBC-HS384': + case 'A256CBC-HS512': + length = parseInt(alg.slice(-3), 10); + break; + case 'A128KW': + case 'A192KW': + case 'A256KW': + case 'A128GCMKW': + case 'A192GCMKW': + case 'A256GCMKW': + case 'A128GCM': + case 'A192GCM': + case 'A256GCM': + length = parseInt(alg.slice(1, 4), 10); + break; + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } + return createSecretKey(random(new Uint8Array(length >> 3))); +} +export async function generateKeyPair(alg, options) { + var _a, _b; + switch (alg) { + case 'RS256': + case 'RS384': + case 'RS512': + case 'PS256': + case 'PS384': + case 'PS512': + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + case 'RSA1_5': { + const modulusLength = (_a = options === null || options === void 0 ? void 0 : options.modulusLength) !== null && _a !== void 0 ? _a : 2048; + if (typeof modulusLength !== 'number' || modulusLength < 2048) { + throw new JOSENotSupported('Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used'); + } + const keypair = await generate('rsa', { + modulusLength, + publicExponent: 0x10001, + }); + setModulusLength(keypair.privateKey, modulusLength); + setModulusLength(keypair.publicKey, modulusLength); + return keypair; + } + case 'ES256': + return generate('ec', { namedCurve: 'P-256' }); + case 'ES256K': + return generate('ec', { namedCurve: 'secp256k1' }); + case 'ES384': + return generate('ec', { namedCurve: 'P-384' }); + case 'ES512': + return generate('ec', { namedCurve: 'P-521' }); + case 'EdDSA': { + switch (options === null || options === void 0 ? void 0 : options.crv) { + case undefined: + case 'Ed25519': + return generate('ed25519'); + case 'Ed448': + return generate('ed448'); + default: + throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are Ed25519 and Ed448'); + } + } + case 'ECDH-ES': + case 'ECDH-ES+A128KW': + case 'ECDH-ES+A192KW': + case 'ECDH-ES+A256KW': + const crv = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : 'P-256'; + switch (crv) { + case undefined: + case 'P-256': + case 'P-384': + case 'P-521': + return generate('ec', { namedCurve: crv }); + case 'X25519': + return generate('x25519'); + case 'X448': + return generate('x448'); + default: + throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448'); + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); + } +} diff --git a/dist/node/esm/runtime/get_named_curve.js b/dist/node/esm/runtime/get_named_curve.js new file mode 100644 index 0000000000..e453a6660e --- /dev/null +++ b/dist/node/esm/runtime/get_named_curve.js @@ -0,0 +1,91 @@ +import { Buffer } from 'buffer'; +import { createPublicKey, KeyObject } from 'crypto'; +import { JOSENotSupported } from '../util/errors.js'; +import { isCryptoKey } from './webcrypto.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +const p256 = Buffer.from([42, 134, 72, 206, 61, 3, 1, 7]); +const p384 = Buffer.from([43, 129, 4, 0, 34]); +const p521 = Buffer.from([43, 129, 4, 0, 35]); +const secp256k1 = Buffer.from([43, 129, 4, 0, 10]); +export const weakMap = new WeakMap(); +const namedCurveToJOSE = (namedCurve) => { + switch (namedCurve) { + case 'prime256v1': + return 'P-256'; + case 'secp384r1': + return 'P-384'; + case 'secp521r1': + return 'P-521'; + case 'secp256k1': + return 'secp256k1'; + default: + throw new JOSENotSupported('Unsupported key curve for this operation'); + } +}; +const getNamedCurve = (kee, raw) => { + var _a; + let key; + if (isCryptoKey(kee)) { + key = KeyObject.from(kee); + } + else if (isKeyObject(kee)) { + key = kee; + } + else { + throw new TypeError(invalidKeyInput(kee, ...types)); + } + if (key.type === 'secret') { + throw new TypeError('only "private" or "public" type keys can be used for this operation'); + } + switch (key.asymmetricKeyType) { + case 'ed25519': + case 'ed448': + return `Ed${key.asymmetricKeyType.slice(2)}`; + case 'x25519': + case 'x448': + return `X${key.asymmetricKeyType.slice(1)}`; + case 'ec': { + if (weakMap.has(key)) { + return weakMap.get(key); + } + let namedCurve = (_a = key.asymmetricKeyDetails) === null || _a === void 0 ? void 0 : _a.namedCurve; + if (!namedCurve && key.type === 'private') { + namedCurve = getNamedCurve(createPublicKey(key), true); + } + else if (!namedCurve) { + const buf = key.export({ format: 'der', type: 'spki' }); + const i = buf[1] < 128 ? 14 : 15; + const len = buf[i]; + const curveOid = buf.slice(i + 1, i + 1 + len); + if (curveOid.equals(p256)) { + namedCurve = 'prime256v1'; + } + else if (curveOid.equals(p384)) { + namedCurve = 'secp384r1'; + } + else if (curveOid.equals(p521)) { + namedCurve = 'secp521r1'; + } + else if (curveOid.equals(secp256k1)) { + namedCurve = 'secp256k1'; + } + else { + throw new JOSENotSupported('Unsupported key curve for this operation'); + } + } + if (raw) + return namedCurve; + const curve = namedCurveToJOSE(namedCurve); + weakMap.set(key, curve); + return curve; + } + default: + throw new TypeError('Invalid asymmetric key type for this operation'); + } +}; +export function setCurve(keyObject, curve) { + weakMap.set(keyObject, curve); +} +export default getNamedCurve; diff --git a/dist/node/esm/runtime/get_sign_verify_key.js b/dist/node/esm/runtime/get_sign_verify_key.js new file mode 100644 index 0000000000..46aa2b4551 --- /dev/null +++ b/dist/node/esm/runtime/get_sign_verify_key.js @@ -0,0 +1,21 @@ +import { KeyObject, createSecretKey } from 'crypto'; +import { isCryptoKey } from './webcrypto.js'; +import { checkSigCryptoKey } from '../lib/crypto_key.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +export default function getSignVerifyKey(alg, key, usage) { + if (key instanceof Uint8Array) { + if (!alg.startsWith('HS')) { + throw new TypeError(invalidKeyInput(key, ...types)); + } + return createSecretKey(key); + } + if (key instanceof KeyObject) { + return key; + } + if (isCryptoKey(key)) { + checkSigCryptoKey(key, alg, usage); + return KeyObject.from(key); + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} diff --git a/dist/node/esm/runtime/hmac_digest.js b/dist/node/esm/runtime/hmac_digest.js new file mode 100644 index 0000000000..29d55c3e9c --- /dev/null +++ b/dist/node/esm/runtime/hmac_digest.js @@ -0,0 +1,13 @@ +import { JOSENotSupported } from '../util/errors.js'; +export default function hmacDigest(alg) { + switch (alg) { + case 'HS256': + return 'sha256'; + case 'HS384': + return 'sha384'; + case 'HS512': + return 'sha512'; + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} diff --git a/dist/node/esm/runtime/is_key_like.js b/dist/node/esm/runtime/is_key_like.js new file mode 100644 index 0000000000..af0d0a0725 --- /dev/null +++ b/dist/node/esm/runtime/is_key_like.js @@ -0,0 +1,8 @@ +import webcrypto, { isCryptoKey } from './webcrypto.js'; +import isKeyObject from './is_key_object.js'; +export default (key) => isKeyObject(key) || isCryptoKey(key); +const types = ['KeyObject']; +if (globalThis.CryptoKey || (webcrypto === null || webcrypto === void 0 ? void 0 : webcrypto.CryptoKey)) { + types.push('CryptoKey'); +} +export { types }; diff --git a/dist/node/esm/runtime/is_key_object.js b/dist/node/esm/runtime/is_key_object.js new file mode 100644 index 0000000000..3a28cd80eb --- /dev/null +++ b/dist/node/esm/runtime/is_key_object.js @@ -0,0 +1,5 @@ +import { KeyObject } from 'crypto'; +import * as util from 'util'; +export default util.types.isKeyObject + ? (obj) => util.types.isKeyObject(obj) + : (obj) => obj != null && obj instanceof KeyObject; diff --git a/dist/node/esm/runtime/jwk_to_key.js b/dist/node/esm/runtime/jwk_to_key.js new file mode 100644 index 0000000000..8cb31d6bf9 --- /dev/null +++ b/dist/node/esm/runtime/jwk_to_key.js @@ -0,0 +1,116 @@ +import { Buffer } from 'buffer'; +import { createPrivateKey, createPublicKey, createSecretKey } from 'crypto'; +import { decode as base64url } from './base64url.js'; +import { JOSENotSupported } from '../util/errors.js'; +import { setCurve } from './get_named_curve.js'; +import { setModulusLength } from './check_modulus_length.js'; +import Asn1SequenceEncoder from './asn1_sequence_encoder.js'; +import { jwkImport } from './flags.js'; +const parse = (jwk) => { + if (jwkImport && jwk.kty !== 'oct') { + return jwk.d + ? createPrivateKey({ format: 'jwk', key: jwk }) + : createPublicKey({ format: 'jwk', key: jwk }); + } + switch (jwk.kty) { + case 'oct': { + return createSecretKey(base64url(jwk.k)); + } + case 'RSA': { + const enc = new Asn1SequenceEncoder(); + const isPrivate = jwk.d !== undefined; + const modulus = Buffer.from(jwk.n, 'base64'); + const exponent = Buffer.from(jwk.e, 'base64'); + if (isPrivate) { + enc.zero(); + enc.unsignedInteger(modulus); + enc.unsignedInteger(exponent); + enc.unsignedInteger(Buffer.from(jwk.d, 'base64')); + enc.unsignedInteger(Buffer.from(jwk.p, 'base64')); + enc.unsignedInteger(Buffer.from(jwk.q, 'base64')); + enc.unsignedInteger(Buffer.from(jwk.dp, 'base64')); + enc.unsignedInteger(Buffer.from(jwk.dq, 'base64')); + enc.unsignedInteger(Buffer.from(jwk.qi, 'base64')); + } + else { + enc.unsignedInteger(modulus); + enc.unsignedInteger(exponent); + } + const der = enc.end(); + const createInput = { + key: der, + format: 'der', + type: 'pkcs1', + }; + const keyObject = isPrivate ? createPrivateKey(createInput) : createPublicKey(createInput); + setModulusLength(keyObject, modulus.length << 3); + return keyObject; + } + case 'EC': { + const enc = new Asn1SequenceEncoder(); + const isPrivate = jwk.d !== undefined; + const pub = Buffer.concat([ + Buffer.alloc(1, 4), + Buffer.from(jwk.x, 'base64'), + Buffer.from(jwk.y, 'base64'), + ]); + if (isPrivate) { + enc.zero(); + const enc$1 = new Asn1SequenceEncoder(); + enc$1.oidFor('ecPublicKey'); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + const enc$2 = new Asn1SequenceEncoder(); + enc$2.one(); + enc$2.octStr(Buffer.from(jwk.d, 'base64')); + const enc$3 = new Asn1SequenceEncoder(); + enc$3.bitStr(pub); + const f2 = enc$3.end(Buffer.from([0xa1])); + enc$2.add(f2); + const f = enc$2.end(); + const enc$4 = new Asn1SequenceEncoder(); + enc$4.add(f); + const f3 = enc$4.end(Buffer.from([0x04])); + enc.add(f3); + const der = enc.end(); + const keyObject = createPrivateKey({ key: der, format: 'der', type: 'pkcs8' }); + setCurve(keyObject, jwk.crv); + return keyObject; + } + const enc$1 = new Asn1SequenceEncoder(); + enc$1.oidFor('ecPublicKey'); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + enc.bitStr(pub); + const der = enc.end(); + const keyObject = createPublicKey({ key: der, format: 'der', type: 'spki' }); + setCurve(keyObject, jwk.crv); + return keyObject; + } + case 'OKP': { + const enc = new Asn1SequenceEncoder(); + const isPrivate = jwk.d !== undefined; + if (isPrivate) { + enc.zero(); + const enc$1 = new Asn1SequenceEncoder(); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + const enc$2 = new Asn1SequenceEncoder(); + enc$2.octStr(Buffer.from(jwk.d, 'base64')); + const f = enc$2.end(Buffer.from([0x04])); + enc.add(f); + const der = enc.end(); + return createPrivateKey({ key: der, format: 'der', type: 'pkcs8' }); + } + const enc$1 = new Asn1SequenceEncoder(); + enc$1.oidFor(jwk.crv); + enc.add(enc$1.end()); + enc.bitStr(Buffer.from(jwk.x, 'base64')); + const der = enc.end(); + return createPublicKey({ key: der, format: 'der', type: 'spki' }); + } + default: + throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); + } +}; +export default parse; diff --git a/dist/node/esm/runtime/key_to_jwk.js b/dist/node/esm/runtime/key_to_jwk.js new file mode 100644 index 0000000000..755564cbb3 --- /dev/null +++ b/dist/node/esm/runtime/key_to_jwk.js @@ -0,0 +1,158 @@ +import { KeyObject, createPublicKey } from 'crypto'; +import { encode as base64url } from './base64url.js'; +import Asn1SequenceDecoder from './asn1_sequence_decoder.js'; +import { JOSENotSupported } from '../util/errors.js'; +import getNamedCurve from './get_named_curve.js'; +import { isCryptoKey } from './webcrypto.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +import { jwkExport } from './flags.js'; +const keyToJWK = (key) => { + let keyObject; + if (isCryptoKey(key)) { + if (!key.extractable) { + throw new TypeError('CryptoKey is not extractable'); + } + keyObject = KeyObject.from(key); + } + else if (isKeyObject(key)) { + keyObject = key; + } + else if (key instanceof Uint8Array) { + return { + kty: 'oct', + k: base64url(key), + }; + } + else { + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); + } + if (jwkExport) { + if (keyObject.type !== 'secret' && + !['rsa', 'ec', 'ed25519', 'x25519', 'ed448', 'x448'].includes(keyObject.asymmetricKeyType)) { + throw new JOSENotSupported('Unsupported key asymmetricKeyType'); + } + return keyObject.export({ format: 'jwk' }); + } + switch (keyObject.type) { + case 'secret': + return { + kty: 'oct', + k: base64url(keyObject.export()), + }; + case 'private': + case 'public': { + switch (keyObject.asymmetricKeyType) { + case 'rsa': { + const der = keyObject.export({ format: 'der', type: 'pkcs1' }); + const dec = new Asn1SequenceDecoder(der); + if (keyObject.type === 'private') { + dec.unsignedInteger(); + } + const n = base64url(dec.unsignedInteger()); + const e = base64url(dec.unsignedInteger()); + let jwk; + if (keyObject.type === 'private') { + jwk = { + d: base64url(dec.unsignedInteger()), + p: base64url(dec.unsignedInteger()), + q: base64url(dec.unsignedInteger()), + dp: base64url(dec.unsignedInteger()), + dq: base64url(dec.unsignedInteger()), + qi: base64url(dec.unsignedInteger()), + }; + } + dec.end(); + return { kty: 'RSA', n, e, ...jwk }; + } + case 'ec': { + const crv = getNamedCurve(keyObject); + let len; + let offset; + let correction; + switch (crv) { + case 'secp256k1': + len = 64; + offset = 31 + 2; + correction = -1; + break; + case 'P-256': + len = 64; + offset = 34 + 2; + correction = -1; + break; + case 'P-384': + len = 96; + offset = 33 + 2; + correction = -3; + break; + case 'P-521': + len = 132; + offset = 33 + 2; + correction = -3; + break; + default: + throw new JOSENotSupported('Unsupported curve'); + } + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'EC', + crv, + x: base64url(der.subarray(-len, -len / 2)), + y: base64url(der.subarray(-len / 2)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + if (der.length < 100) { + offset += correction; + } + return { + ...keyToJWK(createPublicKey(keyObject)), + d: base64url(der.subarray(offset, offset + len / 2)), + }; + } + case 'ed25519': + case 'x25519': { + const crv = getNamedCurve(keyObject); + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'OKP', + crv, + x: base64url(der.subarray(-32)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + return { + ...keyToJWK(createPublicKey(keyObject)), + d: base64url(der.subarray(-32)), + }; + } + case 'ed448': + case 'x448': { + const crv = getNamedCurve(keyObject); + if (keyObject.type === 'public') { + const der = keyObject.export({ type: 'spki', format: 'der' }); + return { + kty: 'OKP', + crv, + x: base64url(der.subarray(crv === 'Ed448' ? -57 : -56)), + }; + } + const der = keyObject.export({ type: 'pkcs8', format: 'der' }); + return { + ...keyToJWK(createPublicKey(keyObject)), + d: base64url(der.subarray(crv === 'Ed448' ? -57 : -56)), + }; + } + default: + throw new JOSENotSupported('Unsupported key asymmetricKeyType'); + } + } + default: + throw new JOSENotSupported('Unsupported key type'); + } +}; +export default keyToJWK; diff --git a/dist/node/esm/runtime/node_key.js b/dist/node/esm/runtime/node_key.js new file mode 100644 index 0000000000..3535dd8d67 --- /dev/null +++ b/dist/node/esm/runtime/node_key.js @@ -0,0 +1,75 @@ +import { constants } from 'crypto'; +import getNamedCurve from './get_named_curve.js'; +import { JOSENotSupported } from '../util/errors.js'; +import checkModulusLength from './check_modulus_length.js'; +import { rsaPssParams } from './flags.js'; +const PSS = { + padding: constants.RSA_PKCS1_PSS_PADDING, + saltLength: constants.RSA_PSS_SALTLEN_DIGEST, +}; +const ecCurveAlgMap = new Map([ + ['ES256', 'P-256'], + ['ES256K', 'secp256k1'], + ['ES384', 'P-384'], + ['ES512', 'P-521'], +]); +export default function keyForCrypto(alg, key) { + switch (alg) { + case 'EdDSA': + if (!['ed25519', 'ed448'].includes(key.asymmetricKeyType)) { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448'); + } + return key; + case 'RS256': + case 'RS384': + case 'RS512': + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + checkModulusLength(key, alg); + return key; + case rsaPssParams && 'PS256': + case rsaPssParams && 'PS384': + case rsaPssParams && 'PS512': + if (key.asymmetricKeyType === 'rsa-pss') { + const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails; + const length = parseInt(alg.slice(-3), 10); + if (hashAlgorithm !== undefined && + (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) { + throw new TypeError(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${alg}`); + } + if (saltLength !== undefined && saltLength > length >> 3) { + throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`); + } + } + else if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss'); + } + checkModulusLength(key, alg); + return { key, ...PSS }; + case !rsaPssParams && 'PS256': + case !rsaPssParams && 'PS384': + case !rsaPssParams && 'PS512': + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + checkModulusLength(key, alg); + return { key, ...PSS }; + case 'ES256': + case 'ES256K': + case 'ES384': + case 'ES512': { + if (key.asymmetricKeyType !== 'ec') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ec'); + } + const actual = getNamedCurve(key); + const expected = ecCurveAlgMap.get(alg); + if (actual !== expected) { + throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`); + } + return { dsaEncoding: 'ieee-p1363', key }; + } + default: + throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`); + } +} diff --git a/dist/node/esm/runtime/pbes2kw.js b/dist/node/esm/runtime/pbes2kw.js new file mode 100644 index 0000000000..63122e04ef --- /dev/null +++ b/dist/node/esm/runtime/pbes2kw.js @@ -0,0 +1,43 @@ +import { promisify } from 'util'; +import { KeyObject, pbkdf2 as pbkdf2cb } from 'crypto'; +import random from './random.js'; +import { p2s as concatSalt } from '../lib/buffer_utils.js'; +import { encode as base64url } from './base64url.js'; +import { wrap, unwrap } from './aeskw.js'; +import checkP2s from '../lib/check_p2s.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +const pbkdf2 = promisify(pbkdf2cb); +function getPassword(key, alg) { + if (isKeyObject(key)) { + return key.export(); + } + if (key instanceof Uint8Array) { + return key; + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, 'deriveBits', 'deriveKey'); + return KeyObject.from(key).export(); + } + throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array')); +} +export const encrypt = async (alg, key, cek, p2c = 2048, p2s = random(new Uint8Array(16))) => { + checkP2s(p2s); + const salt = concatSalt(alg, p2s); + const keylen = parseInt(alg.slice(13, 16), 10) >> 3; + const password = getPassword(key, alg); + const derivedKey = await pbkdf2(password, salt, p2c, keylen, `sha${alg.slice(8, 11)}`); + const encryptedKey = await wrap(alg.slice(-6), derivedKey, cek); + return { encryptedKey, p2c, p2s: base64url(p2s) }; +}; +export const decrypt = async (alg, key, encryptedKey, p2c, p2s) => { + checkP2s(p2s); + const salt = concatSalt(alg, p2s); + const keylen = parseInt(alg.slice(13, 16), 10) >> 3; + const password = getPassword(key, alg); + const derivedKey = await pbkdf2(password, salt, p2c, keylen, `sha${alg.slice(8, 11)}`); + return unwrap(alg.slice(-6), derivedKey, encryptedKey); +}; diff --git a/dist/node/esm/runtime/random.js b/dist/node/esm/runtime/random.js new file mode 100644 index 0000000000..80478ea946 --- /dev/null +++ b/dist/node/esm/runtime/random.js @@ -0,0 +1 @@ +export { randomFillSync as default } from 'crypto'; diff --git a/dist/node/esm/runtime/rsaes.js b/dist/node/esm/runtime/rsaes.js new file mode 100644 index 0000000000..3556fe3436 --- /dev/null +++ b/dist/node/esm/runtime/rsaes.js @@ -0,0 +1,64 @@ +import { KeyObject, publicEncrypt, constants, privateDecrypt } from 'crypto'; +import checkModulusLength from './check_modulus_length.js'; +import { isCryptoKey } from './webcrypto.js'; +import { checkEncCryptoKey } from '../lib/crypto_key.js'; +import isKeyObject from './is_key_object.js'; +import invalidKeyInput from '../lib/invalid_key_input.js'; +import { types } from './is_key_like.js'; +const checkKey = (key, alg) => { + if (key.asymmetricKeyType !== 'rsa') { + throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa'); + } + checkModulusLength(key, alg); +}; +const resolvePadding = (alg) => { + switch (alg) { + case 'RSA-OAEP': + case 'RSA-OAEP-256': + case 'RSA-OAEP-384': + case 'RSA-OAEP-512': + return constants.RSA_PKCS1_OAEP_PADDING; + case 'RSA1_5': + return constants.RSA_PKCS1_PADDING; + default: + return undefined; + } +}; +const resolveOaepHash = (alg) => { + switch (alg) { + case 'RSA-OAEP': + return 'sha1'; + case 'RSA-OAEP-256': + return 'sha256'; + case 'RSA-OAEP-384': + return 'sha384'; + case 'RSA-OAEP-512': + return 'sha512'; + default: + return undefined; + } +}; +function ensureKeyObject(key, alg, ...usages) { + if (isKeyObject(key)) { + return key; + } + if (isCryptoKey(key)) { + checkEncCryptoKey(key, alg, ...usages); + return KeyObject.from(key); + } + throw new TypeError(invalidKeyInput(key, ...types)); +} +export const encrypt = (alg, key, cek) => { + const padding = resolvePadding(alg); + const oaepHash = resolveOaepHash(alg); + const keyObject = ensureKeyObject(key, alg, 'wrapKey', 'encrypt'); + checkKey(keyObject, alg); + return publicEncrypt({ key: keyObject, oaepHash, padding }, cek); +}; +export const decrypt = (alg, key, encryptedKey) => { + const padding = resolvePadding(alg); + const oaepHash = resolveOaepHash(alg); + const keyObject = ensureKeyObject(key, alg, 'unwrapKey', 'decrypt'); + checkKey(keyObject, alg); + return privateDecrypt({ key: keyObject, oaepHash, padding }, encryptedKey); +}; diff --git a/dist/node/esm/runtime/sign.js b/dist/node/esm/runtime/sign.js new file mode 100644 index 0000000000..8902d4151c --- /dev/null +++ b/dist/node/esm/runtime/sign.js @@ -0,0 +1,23 @@ +import * as crypto from 'crypto'; +import { promisify } from 'util'; +import nodeDigest from './dsa_digest.js'; +import hmacDigest from './hmac_digest.js'; +import nodeKey from './node_key.js'; +import getSignKey from './get_sign_verify_key.js'; +let oneShotSign; +if (crypto.sign.length > 3) { + oneShotSign = promisify(crypto.sign); +} +else { + oneShotSign = crypto.sign; +} +const sign = async (alg, key, data) => { + const keyObject = getSignKey(alg, key, 'sign'); + if (alg.startsWith('HS')) { + const hmac = crypto.createHmac(hmacDigest(alg), keyObject); + hmac.update(data); + return hmac.digest(); + } + return oneShotSign(nodeDigest(alg), data, nodeKey(alg, keyObject)); +}; +export default sign; diff --git a/dist/node/esm/runtime/timing_safe_equal.js b/dist/node/esm/runtime/timing_safe_equal.js new file mode 100644 index 0000000000..901ae38bdd --- /dev/null +++ b/dist/node/esm/runtime/timing_safe_equal.js @@ -0,0 +1,3 @@ +import { timingSafeEqual as impl } from 'crypto'; +const timingSafeEqual = impl; +export default timingSafeEqual; diff --git a/dist/node/esm/runtime/verify.js b/dist/node/esm/runtime/verify.js new file mode 100644 index 0000000000..e514e1c452 --- /dev/null +++ b/dist/node/esm/runtime/verify.js @@ -0,0 +1,36 @@ +import * as crypto from 'crypto'; +import { promisify } from 'util'; +import nodeDigest from './dsa_digest.js'; +import nodeKey from './node_key.js'; +import sign from './sign.js'; +import getVerifyKey from './get_sign_verify_key.js'; +import { oneShotCallback } from './flags.js'; +let oneShotVerify; +if (crypto.verify.length > 4 && oneShotCallback) { + oneShotVerify = promisify(crypto.verify); +} +else { + oneShotVerify = crypto.verify; +} +const verify = async (alg, key, signature, data) => { + const keyObject = getVerifyKey(alg, key, 'verify'); + if (alg.startsWith('HS')) { + const expected = await sign(alg, keyObject, data); + const actual = signature; + try { + return crypto.timingSafeEqual(actual, expected); + } + catch { + return false; + } + } + const algorithm = nodeDigest(alg); + const keyInput = nodeKey(alg, keyObject); + try { + return await oneShotVerify(algorithm, data, keyInput, signature); + } + catch { + return false; + } +}; +export default verify; diff --git a/dist/node/esm/runtime/webcrypto.js b/dist/node/esm/runtime/webcrypto.js new file mode 100644 index 0000000000..a2fdb2a7a1 --- /dev/null +++ b/dist/node/esm/runtime/webcrypto.js @@ -0,0 +1,8 @@ +import * as crypto from 'crypto'; +import * as util from 'util'; +const webcrypto = crypto.webcrypto; +export default webcrypto; +export const isCryptoKey = util.types.isCryptoKey + ? (key) => util.types.isCryptoKey(key) + : + (key) => false; diff --git a/dist/node/esm/runtime/zlib.js b/dist/node/esm/runtime/zlib.js new file mode 100644 index 0000000000..8bb72cbf18 --- /dev/null +++ b/dist/node/esm/runtime/zlib.js @@ -0,0 +1,6 @@ +import { promisify } from 'util'; +import { inflateRaw as inflateRawCb, deflateRaw as deflateRawCb } from 'zlib'; +const inflateRaw = promisify(inflateRawCb); +const deflateRaw = promisify(deflateRawCb); +export const inflate = (input) => inflateRaw(input); +export const deflate = (input) => deflateRaw(input); diff --git a/dist/node/esm/util/base64url.js b/dist/node/esm/util/base64url.js new file mode 100644 index 0000000000..88ce7556d6 --- /dev/null +++ b/dist/node/esm/util/base64url.js @@ -0,0 +1,3 @@ +import * as base64url from '../runtime/base64url.js'; +export const encode = base64url.encode; +export const decode = base64url.decode; diff --git a/dist/node/esm/util/decode_jwt.js b/dist/node/esm/util/decode_jwt.js new file mode 100644 index 0000000000..0b824ed580 --- /dev/null +++ b/dist/node/esm/util/decode_jwt.js @@ -0,0 +1,32 @@ +import { decode as base64url } from './base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +import { JWTInvalid } from './errors.js'; +export function decodeJwt(jwt) { + if (typeof jwt !== 'string') + throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string'); + const { 1: payload, length } = jwt.split('.'); + if (length === 5) + throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded'); + if (length !== 3) + throw new JWTInvalid('Invalid JWT'); + if (!payload) + throw new JWTInvalid('JWTs must contain a payload'); + let decoded; + try { + decoded = base64url(payload); + } + catch { + throw new JWTInvalid('Failed to parse the base64url encoded payload'); + } + let result; + try { + result = JSON.parse(decoder.decode(decoded)); + } + catch { + throw new JWTInvalid('Failed to parse the decoded payload as JSON'); + } + if (!isObject(result)) + throw new JWTInvalid('Invalid JWT Claims Set'); + return result; +} diff --git a/dist/node/esm/util/decode_protected_header.js b/dist/node/esm/util/decode_protected_header.js new file mode 100644 index 0000000000..04be31d8f2 --- /dev/null +++ b/dist/node/esm/util/decode_protected_header.js @@ -0,0 +1,34 @@ +import { decode as base64url } from './base64url.js'; +import { decoder } from '../lib/buffer_utils.js'; +import isObject from '../lib/is_object.js'; +export function decodeProtectedHeader(token) { + let protectedB64u; + if (typeof token === 'string') { + const parts = token.split('.'); + if (parts.length === 3 || parts.length === 5) { + ; + [protectedB64u] = parts; + } + } + else if (typeof token === 'object' && token) { + if ('protected' in token) { + protectedB64u = token.protected; + } + else { + throw new TypeError('Token does not contain a Protected Header'); + } + } + try { + if (typeof protectedB64u !== 'string' || !protectedB64u) { + throw new Error(); + } + const result = JSON.parse(decoder.decode(base64url(protectedB64u))); + if (!isObject(result)) { + throw new Error(); + } + return result; + } + catch { + throw new TypeError('Invalid Token or Protected Header formatting'); + } +} diff --git a/dist/node/esm/util/errors.js b/dist/node/esm/util/errors.js new file mode 100644 index 0000000000..13a36f58fa --- /dev/null +++ b/dist/node/esm/util/errors.js @@ -0,0 +1,148 @@ +export class JOSEError extends Error { + static get code() { + return 'ERR_JOSE_GENERIC'; + } + constructor(message) { + var _a; + super(message); + this.code = 'ERR_JOSE_GENERIC'; + this.name = this.constructor.name; + (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor); + } +} +export class JWTClaimValidationFailed extends JOSEError { + static get code() { + return 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + this.claim = claim; + this.reason = reason; + } +} +export class JWTExpired extends JOSEError { + static get code() { + return 'ERR_JWT_EXPIRED'; + } + constructor(message, claim = 'unspecified', reason = 'unspecified') { + super(message); + this.code = 'ERR_JWT_EXPIRED'; + this.claim = claim; + this.reason = reason; + } +} +export class JOSEAlgNotAllowed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_ALG_NOT_ALLOWED'; + } + static get code() { + return 'ERR_JOSE_ALG_NOT_ALLOWED'; + } +} +export class JOSENotSupported extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JOSE_NOT_SUPPORTED'; + } + static get code() { + return 'ERR_JOSE_NOT_SUPPORTED'; + } +} +export class JWEDecryptionFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_DECRYPTION_FAILED'; + this.message = 'decryption operation failed'; + } + static get code() { + return 'ERR_JWE_DECRYPTION_FAILED'; + } +} +export class JWEInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWE_INVALID'; + } + static get code() { + return 'ERR_JWE_INVALID'; + } +} +export class JWSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_INVALID'; + } + static get code() { + return 'ERR_JWS_INVALID'; + } +} +export class JWTInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWT_INVALID'; + } + static get code() { + return 'ERR_JWT_INVALID'; + } +} +export class JWKInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWK_INVALID'; + } + static get code() { + return 'ERR_JWK_INVALID'; + } +} +export class JWKSInvalid extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_INVALID'; + } + static get code() { + return 'ERR_JWKS_INVALID'; + } +} +export class JWKSNoMatchingKey extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_NO_MATCHING_KEY'; + this.message = 'no applicable key found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_NO_MATCHING_KEY'; + } +} +export class JWKSMultipleMatchingKeys extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + this.message = 'multiple matching keys found in the JSON Web Key Set'; + } + static get code() { + return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + } +} +Symbol.asyncIterator; +export class JWKSTimeout extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWKS_TIMEOUT'; + this.message = 'request timed out'; + } + static get code() { + return 'ERR_JWKS_TIMEOUT'; + } +} +export class JWSSignatureVerificationFailed extends JOSEError { + constructor() { + super(...arguments); + this.code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + this.message = 'signature verification failed'; + } + static get code() { + return 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + } +} diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts new file mode 100644 index 0000000000..6ce928e73a --- /dev/null +++ b/dist/types/index.d.ts @@ -0,0 +1,47 @@ +export { compactDecrypt } from './jwe/compact/decrypt'; +export type { CompactDecryptGetKey } from './jwe/compact/decrypt'; +export { flattenedDecrypt } from './jwe/flattened/decrypt'; +export type { FlattenedDecryptGetKey } from './jwe/flattened/decrypt'; +export { generalDecrypt } from './jwe/general/decrypt'; +export type { GeneralDecryptGetKey } from './jwe/general/decrypt'; +export { GeneralEncrypt } from './jwe/general/encrypt'; +export type { Recipient } from './jwe/general/encrypt'; +export { compactVerify } from './jws/compact/verify'; +export type { CompactVerifyGetKey } from './jws/compact/verify'; +export { flattenedVerify } from './jws/flattened/verify'; +export type { FlattenedVerifyGetKey } from './jws/flattened/verify'; +export { generalVerify } from './jws/general/verify'; +export type { GeneralVerifyGetKey } from './jws/general/verify'; +export { jwtVerify } from './jwt/verify'; +export type { JWTVerifyOptions, JWTVerifyGetKey } from './jwt/verify'; +export { jwtDecrypt } from './jwt/decrypt'; +export type { JWTDecryptOptions, JWTDecryptGetKey } from './jwt/decrypt'; +export type { ProduceJWT } from './jwt/produce'; +export { CompactEncrypt } from './jwe/compact/encrypt'; +export { FlattenedEncrypt } from './jwe/flattened/encrypt'; +export { CompactSign } from './jws/compact/sign'; +export { FlattenedSign } from './jws/flattened/sign'; +export { GeneralSign } from './jws/general/sign'; +export type { Signature } from './jws/general/sign'; +export { SignJWT } from './jwt/sign'; +export { EncryptJWT } from './jwt/encrypt'; +export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint'; +export { EmbeddedJWK } from './jwk/embedded'; +export { createLocalJWKSet } from './jwks/local'; +export { createRemoteJWKSet } from './jwks/remote'; +export type { RemoteJWKSetOptions } from './jwks/remote'; +export { UnsecuredJWT } from './jwt/unsecured'; +export type { UnsecuredResult } from './jwt/unsecured'; +export { exportPKCS8, exportSPKI, exportJWK } from './key/export'; +export { importSPKI, importPKCS8, importX509, importJWK } from './key/import'; +export type { PEMImportOptions } from './key/import'; +export { decodeProtectedHeader } from './util/decode_protected_header'; +export { decodeJwt } from './util/decode_jwt'; +export type { ProtectedHeaderParameters } from './util/decode_protected_header'; +export * as errors from './util/errors'; +export { generateKeyPair } from './key/generate_key_pair'; +export type { GenerateKeyPairResult, GenerateKeyPairOptions } from './key/generate_key_pair'; +export { generateSecret } from './key/generate_secret'; +export type { GenerateSecretOptions } from './key/generate_secret'; +export * as base64url from './util/base64url'; +export type { KeyLike, JWK, FlattenedJWSInput, GeneralJWSInput, FlattenedJWS, GeneralJWS, JoseHeaderParameters, JWSHeaderParameters, JWEKeyManagementHeaderParameters, FlattenedJWE, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption, DecryptOptions, EncryptOptions, JWTClaimVerificationOptions, VerifyOptions, SignOptions, JWTPayload, DeflateFunction, InflateFunction, FlattenedDecryptResult, GeneralDecryptResult, CompactDecryptResult, FlattenedVerifyResult, GeneralVerifyResult, CompactVerifyResult, JWTVerifyResult, JWTDecryptResult, ResolvedKey, CompactJWEHeaderParameters, CompactJWSHeaderParameters, JWTHeaderParameters, JSONWebKeySet, } from './types'; diff --git a/dist/types/jwe/compact/decrypt.d.ts b/dist/types/jwe/compact/decrypt.d.ts new file mode 100644 index 0000000000..14b2680c66 --- /dev/null +++ b/dist/types/jwe/compact/decrypt.d.ts @@ -0,0 +1,23 @@ +import type { KeyLike, DecryptOptions, CompactJWEHeaderParameters, GetKeyFunction, FlattenedJWE, CompactDecryptResult, ResolvedKey } from '../../types'; +/** + * Interface for Compact JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface CompactDecryptGetKey extends GetKeyFunction { +} +/** + * Decrypts a Compact JWE. + * + * @param jwe Compact JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function compactDecrypt(jwe: string | Uint8Array, key: KeyLike | Uint8Array, options?: DecryptOptions): Promise; +/** + * @param jwe Compact JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function compactDecrypt(jwe: string | Uint8Array, getKey: CompactDecryptGetKey, options?: DecryptOptions): Promise>; diff --git a/dist/types/jwe/compact/encrypt.d.ts b/dist/types/jwe/compact/encrypt.d.ts new file mode 100644 index 0000000000..7b93d5eef9 --- /dev/null +++ b/dist/types/jwe/compact/encrypt.d.ts @@ -0,0 +1,50 @@ +import type { KeyLike, JWEKeyManagementHeaderParameters, CompactJWEHeaderParameters, EncryptOptions } from '../../types'; +/** + * The CompactEncrypt class is used to build and encrypt Compact JWE strings. + * + */ +export declare class CompactEncrypt { + private _flattened; + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array); + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array): this; + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array): this; + /** + * Sets the JWE Protected Header on the CompactEncrypt object. + * + * @param protectedHeader JWE Protected Header object. + */ + setProtectedHeader(protectedHeader: CompactJWEHeaderParameters): this; + /** + * Sets the JWE Key Management parameters to be used when encrypting the Content Encryption Key. + * You do not need to invoke this method, it is only really intended for test and vector + * validation purposes. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this; + /** + * Encrypts and resolves the value of the Compact JWE string. + * + * @param key Public Key or Secret to encrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions): Promise; +} diff --git a/dist/types/jwe/flattened/decrypt.d.ts b/dist/types/jwe/flattened/decrypt.d.ts new file mode 100644 index 0000000000..8726df6879 --- /dev/null +++ b/dist/types/jwe/flattened/decrypt.d.ts @@ -0,0 +1,23 @@ +import type { FlattenedDecryptResult, KeyLike, FlattenedJWE, JWEHeaderParameters, DecryptOptions, GetKeyFunction, ResolvedKey } from '../../types'; +/** + * Interface for Flattened JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface FlattenedDecryptGetKey extends GetKeyFunction { +} +/** + * Decrypts a Flattened JWE. + * + * @param jwe Flattened JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function flattenedDecrypt(jwe: FlattenedJWE, key: KeyLike | Uint8Array, options?: DecryptOptions): Promise; +/** + * @param jwe Flattened JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function flattenedDecrypt(jwe: FlattenedJWE, getKey: FlattenedDecryptGetKey, options?: DecryptOptions): Promise>; diff --git a/dist/types/jwe/flattened/encrypt.d.ts b/dist/types/jwe/flattened/encrypt.d.ts new file mode 100644 index 0000000000..1ebf73f28b --- /dev/null +++ b/dist/types/jwe/flattened/encrypt.d.ts @@ -0,0 +1,78 @@ +import type { KeyLike, FlattenedJWE, JWEHeaderParameters, JWEKeyManagementHeaderParameters, EncryptOptions } from '../../types'; +/** @private */ +export declare const unprotected: unique symbol; +/** + * The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. + * + */ +export declare class FlattenedEncrypt { + private _plaintext; + private _protectedHeader; + private _sharedUnprotectedHeader; + private _unprotectedHeader; + private _aad; + private _cek; + private _iv; + private _keyManagementParameters; + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array); + /** + * Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is + * really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or + * Agreement PartyVInfo parameters. Other parameters will always be randomly generated when needed + * and missing. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this; + /** + * Sets the JWE Protected Header on the FlattenedEncrypt object. + * + * @param protectedHeader JWE Protected Header. + */ + setProtectedHeader(protectedHeader: JWEHeaderParameters): this; + /** + * Sets the JWE Shared Unprotected Header on the FlattenedEncrypt object. + * + * @param sharedUnprotectedHeader JWE Shared Unprotected Header. + */ + setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this; + /** + * Sets the JWE Per-Recipient Unprotected Header on the FlattenedEncrypt object. + * + * @param unprotectedHeader JWE Per-Recipient Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): this; + /** + * Sets the Additional Authenticated Data on the FlattenedEncrypt object. + * + * @param aad Additional Authenticated Data. + */ + setAdditionalAuthenticatedData(aad: Uint8Array): this; + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array): this; + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array): this; + /** + * Encrypts and resolves the value of the Flattened JWE object. + * + * @param key Public Key or Secret to encrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions): Promise; +} diff --git a/dist/types/jwe/general/decrypt.d.ts b/dist/types/jwe/general/decrypt.d.ts new file mode 100644 index 0000000000..26fc8b6135 --- /dev/null +++ b/dist/types/jwe/general/decrypt.d.ts @@ -0,0 +1,23 @@ +import type { KeyLike, DecryptOptions, JWEHeaderParameters, GetKeyFunction, FlattenedJWE, GeneralJWE, GeneralDecryptResult, ResolvedKey } from '../../types'; +/** + * Interface for General JWE Decryption dynamic key resolution. No token components have been + * verified at the time of this function call. + */ +export interface GeneralDecryptGetKey extends GetKeyFunction { +} +/** + * Decrypts a General JWE. + * + * @param jwe General JWE. + * @param key Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function generalDecrypt(jwe: GeneralJWE, key: KeyLike | Uint8Array, options?: DecryptOptions): Promise; +/** + * @param jwe General JWE. + * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Decryption options. + */ +export declare function generalDecrypt(jwe: GeneralJWE, getKey: GeneralDecryptGetKey, options?: DecryptOptions): Promise>; diff --git a/dist/types/jwe/general/encrypt.d.ts b/dist/types/jwe/general/encrypt.d.ts new file mode 100644 index 0000000000..8cecd3a0f8 --- /dev/null +++ b/dist/types/jwe/general/encrypt.d.ts @@ -0,0 +1,60 @@ +import type { KeyLike, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption } from '../../types'; +export interface Recipient { + /** + * Sets the JWE Per-Recipient Unprotected Header on the Recipient object. + * + * @param unprotectedHeader JWE Per-Recipient Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): Recipient; + /** A shorthand for calling addRecipient() on the enclosing GeneralEncrypt instance */ + addRecipient(...args: Parameters): Recipient; + /** A shorthand for calling encrypt() on the enclosing GeneralEncrypt instance */ + encrypt(...args: Parameters): Promise; + /** Returns the enclosing GeneralEncrypt */ + done(): GeneralEncrypt; +} +/** + * The GeneralEncrypt class is used to build and encrypt General JWE objects. + * + */ +export declare class GeneralEncrypt { + private _plaintext; + private _recipients; + private _protectedHeader; + private _unprotectedHeader; + private _aad; + /** @param plaintext Binary representation of the plaintext to encrypt. */ + constructor(plaintext: Uint8Array); + /** + * Adds an additional recipient for the General JWE object. + * + * @param key Public Key or Secret to encrypt the Content Encryption Key for the recipient with. + * See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + addRecipient(key: KeyLike | Uint8Array, options?: CritOption): Recipient; + /** + * Sets the JWE Protected Header on the GeneralEncrypt object. + * + * @param protectedHeader JWE Protected Header object. + */ + setProtectedHeader(protectedHeader: JWEHeaderParameters): this; + /** + * Sets the JWE Shared Unprotected Header on the GeneralEncrypt object. + * + * @param sharedUnprotectedHeader JWE Shared Unprotected Header object. + */ + setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this; + /** + * Sets the Additional Authenticated Data on the GeneralEncrypt object. + * + * @param aad Additional Authenticated Data. + */ + setAdditionalAuthenticatedData(aad: Uint8Array): this; + /** + * Encrypts and resolves the value of the General JWE object. + * + * @param options JWE Encryption options. + */ + encrypt(options?: DeflateOption): Promise; +} diff --git a/dist/types/jwk/embedded.d.ts b/dist/types/jwk/embedded.d.ts new file mode 100644 index 0000000000..e655097e73 --- /dev/null +++ b/dist/types/jwk/embedded.d.ts @@ -0,0 +1,9 @@ +import type { KeyLike, FlattenedJWSInput, JWSHeaderParameters } from '../types'; +/** + * EmbeddedJWK is an implementation of a GetKeyFunction intended to be used with the JWS/JWT verify + * operations whenever you need to opt-in to verify signatures with a public key embedded in the + * token's "jwk" (JSON Web Key) Header Parameter. It is recommended to combine this with the verify + * function's `algorithms` option to define accepted JWS "alg" (Algorithm) Header Parameter values. + * + */ +export declare function EmbeddedJWK(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise; diff --git a/dist/types/jwk/thumbprint.d.ts b/dist/types/jwk/thumbprint.d.ts new file mode 100644 index 0000000000..ae6d482ba5 --- /dev/null +++ b/dist/types/jwk/thumbprint.d.ts @@ -0,0 +1,19 @@ +import type { JWK } from '../types'; +/** + * Calculates a base64url-encoded JSON Web Key (JWK) Thumbprint + * + * @param jwk JSON Web Key. + * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is + * "sha256". + * @see {@link https://www.rfc-editor.org/rfc/rfc7638 RFC7638} + */ +export declare function calculateJwkThumbprint(jwk: JWK, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512'): Promise; +/** + * Calculates a JSON Web Key (JWK) Thumbprint URI + * + * @param jwk JSON Web Key. + * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is + * "sha256". + * @see {@link https://www.rfc-editor.org/rfc/rfc9278 RFC9278} + */ +export declare function calculateJwkThumbprintUri(jwk: JWK, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512'): Promise; diff --git a/dist/types/jwks/local.d.ts b/dist/types/jwks/local.d.ts new file mode 100644 index 0000000000..475ae402ae --- /dev/null +++ b/dist/types/jwks/local.d.ts @@ -0,0 +1,26 @@ +import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput } from '../types'; +/** @private */ +export declare function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet; +/** @private */ +export declare class LocalJWKSet { + protected _jwks?: JSONWebKeySet; + private _cached; + constructor(jwks: unknown); + getKey(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise; +} +/** + * Returns a function that resolves to a key object from a locally stored, or otherwise available, + * JSON Web Key Set. + * + * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), + * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if + * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key + * Operations) Parameters (if they are present on the JWK). + * + * Only a single public key must match the selection process. As shown in the example below when + * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt + * verification in an iterative manner. + * + * @param jwks JSON Web Key Set formatted object. + */ +export declare function createLocalJWKSet(jwks: JSONWebKeySet): (protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput) => Promise; diff --git a/dist/types/jwks/remote.d.ts b/dist/types/jwks/remote.d.ts new file mode 100644 index 0000000000..7fd5fe1d85 --- /dev/null +++ b/dist/types/jwks/remote.d.ts @@ -0,0 +1,49 @@ +import type { KeyLike, JWSHeaderParameters, FlattenedJWSInput } from '../types'; +/** Options for the remote JSON Web Key Set. */ +export interface RemoteJWKSetOptions { + /** + * Timeout (in milliseconds) for the HTTP request. When reached the request will be aborted and + * the verification will fail. Default is 5000 (5 seconds). + */ + timeoutDuration?: number; + /** + * Duration (in milliseconds) for which no more HTTP requests will be triggered after a previous + * successful fetch. Default is 30000 (30 seconds). + */ + cooldownDuration?: number; + /** + * Maximum time (in milliseconds) between successful HTTP requests. Default is 600000 (10 + * minutes). + */ + cacheMaxAge?: number | typeof Infinity; + /** + * An instance of {@link https://nodejs.org/api/http.html#class-httpagent http.Agent} or + * {@link https://nodejs.org/api/https.html#class-httpsagent https.Agent} to pass to the + * {@link https://nodejs.org/api/http.html#httpgetoptions-callback http.get} or + * {@link https://nodejs.org/api/https.html#httpsgetoptions-callback https.get} method's options. + * Use when behind an http(s) proxy. This is a Node.js runtime specific option, it is ignored when + * used outside of Node.js runtime. + */ + agent?: any; + /** Optional headers to be sent with the HTTP request. */ + headers?: Record; +} +/** + * Returns a function that resolves to a key object downloaded from a remote endpoint returning a + * JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC jwks_uri. The JSON Web Key Set is + * fetched when no key matches the selection process but only as frequently as the + * `cooldownDuration` option allows to prevent abuse. + * + * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), + * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if + * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key + * Operations) Parameters (if they are present on the JWK). + * + * Only a single public key must match the selection process. As shown in the example below when + * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt + * verification in an iterative manner. + * + * @param url URL to fetch the JSON Web Key Set from. + * @param options Options for the remote JSON Web Key Set. + */ +export declare function createRemoteJWKSet(url: URL, options?: RemoteJWKSetOptions): (protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput) => Promise; diff --git a/dist/types/jws/compact/sign.d.ts b/dist/types/jws/compact/sign.d.ts new file mode 100644 index 0000000000..9e2ecb8e37 --- /dev/null +++ b/dist/types/jws/compact/sign.d.ts @@ -0,0 +1,24 @@ +import type { CompactJWSHeaderParameters, KeyLike, SignOptions } from '../../types'; +/** + * The CompactSign class is used to build and sign Compact JWS strings. + * + */ +export declare class CompactSign { + private _flattened; + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array); + /** + * Sets the JWS Protected Header on the Sign object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: CompactJWSHeaderParameters): this; + /** + * Signs and resolves the value of the Compact JWS string. + * + * @param key Private Key or Secret to sign the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise; +} diff --git a/dist/types/jws/compact/verify.d.ts b/dist/types/jws/compact/verify.d.ts new file mode 100644 index 0000000000..89e784f58a --- /dev/null +++ b/dist/types/jws/compact/verify.d.ts @@ -0,0 +1,25 @@ +import type { CompactVerifyResult, FlattenedJWSInput, GetKeyFunction, CompactJWSHeaderParameters, KeyLike, VerifyOptions, ResolvedKey } from '../../types'; +/** + * Interface for Compact JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface CompactVerifyGetKey extends GetKeyFunction { +} +/** + * Verifies the signature and format of and afterwards decodes the Compact JWS. + * + * @param jws Compact JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function compactVerify(jws: string | Uint8Array, key: KeyLike | Uint8Array, options?: VerifyOptions): Promise; +/** + * @param jws Compact JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function compactVerify(jws: string | Uint8Array, getKey: CompactVerifyGetKey, options?: VerifyOptions): Promise>; diff --git a/dist/types/jws/flattened/sign.d.ts b/dist/types/jws/flattened/sign.d.ts new file mode 100644 index 0000000000..09f12421ad --- /dev/null +++ b/dist/types/jws/flattened/sign.d.ts @@ -0,0 +1,32 @@ +import type { KeyLike, FlattenedJWS, JWSHeaderParameters, SignOptions } from '../../types'; +/** + * The FlattenedSign class is used to build and sign Flattened JWS objects. + * + */ +export declare class FlattenedSign { + private _payload; + private _protectedHeader; + private _unprotectedHeader; + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array); + /** + * Sets the JWS Protected Header on the FlattenedSign object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: JWSHeaderParameters): this; + /** + * Sets the JWS Unprotected Header on the FlattenedSign object. + * + * @param unprotectedHeader JWS Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters): this; + /** + * Signs and resolves the value of the Flattened JWS object. + * + * @param key Private Key or Secret to sign the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise; +} diff --git a/dist/types/jws/flattened/verify.d.ts b/dist/types/jws/flattened/verify.d.ts new file mode 100644 index 0000000000..161b32234a --- /dev/null +++ b/dist/types/jws/flattened/verify.d.ts @@ -0,0 +1,25 @@ +import type { FlattenedVerifyResult, KeyLike, FlattenedJWSInput, JWSHeaderParameters, VerifyOptions, GetKeyFunction, ResolvedKey } from '../../types'; +/** + * Interface for Flattened JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface FlattenedVerifyGetKey extends GetKeyFunction { +} +/** + * Verifies the signature and format of and afterwards decodes the Flattened JWS. + * + * @param jws Flattened JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function flattenedVerify(jws: FlattenedJWSInput, key: KeyLike | Uint8Array, options?: VerifyOptions): Promise; +/** + * @param jws Flattened JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function flattenedVerify(jws: FlattenedJWSInput, getKey: FlattenedVerifyGetKey, options?: VerifyOptions): Promise>; diff --git a/dist/types/jws/general/sign.d.ts b/dist/types/jws/general/sign.d.ts new file mode 100644 index 0000000000..e79ca7f45c --- /dev/null +++ b/dist/types/jws/general/sign.d.ts @@ -0,0 +1,41 @@ +import type { KeyLike, GeneralJWS, JWSHeaderParameters, SignOptions } from '../../types'; +export interface Signature { + /** + * Sets the JWS Protected Header on the Signature object. + * + * @param protectedHeader JWS Protected Header. + */ + setProtectedHeader(protectedHeader: JWSHeaderParameters): Signature; + /** + * Sets the JWS Unprotected Header on the Signature object. + * + * @param unprotectedHeader JWS Unprotected Header. + */ + setUnprotectedHeader(unprotectedHeader: JWSHeaderParameters): Signature; + /** A shorthand for calling addSignature() on the enclosing GeneralSign instance */ + addSignature(...args: Parameters): Signature; + /** A shorthand for calling encrypt() on the enclosing GeneralSign instance */ + sign(...args: Parameters): Promise; + /** Returns the enclosing GeneralSign */ + done(): GeneralSign; +} +/** + * The GeneralSign class is used to build and sign General JWS objects. + * + */ +export declare class GeneralSign { + private _payload; + private _signatures; + /** @param payload Binary representation of the payload to sign. */ + constructor(payload: Uint8Array); + /** + * Adds an additional signature for the General JWS object. + * + * @param key Private Key or Secret to sign the individual JWS signature with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Sign options. + */ + addSignature(key: KeyLike | Uint8Array, options?: SignOptions): Signature; + /** Signs and resolves the value of the General JWS object. */ + sign(): Promise; +} diff --git a/dist/types/jws/general/verify.d.ts b/dist/types/jws/general/verify.d.ts new file mode 100644 index 0000000000..1d4eaed740 --- /dev/null +++ b/dist/types/jws/general/verify.d.ts @@ -0,0 +1,25 @@ +import type { GeneralJWSInput, GeneralVerifyResult, FlattenedJWSInput, GetKeyFunction, JWSHeaderParameters, KeyLike, VerifyOptions, ResolvedKey } from '../../types'; +/** + * Interface for General JWS Verification dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface GeneralVerifyGetKey extends GetKeyFunction { +} +/** + * Verifies the signature and format of and afterwards decodes the General JWS. + * + * @param jws General JWS. + * @param key Key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function generalVerify(jws: GeneralJWSInput, key: KeyLike | Uint8Array, options?: VerifyOptions): Promise; +/** + * @param jws General JWS. + * @param getKey Function resolving a key to verify the JWS with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWS Verify options. + */ +export declare function generalVerify(jws: GeneralJWSInput, getKey: GeneralVerifyGetKey, options?: VerifyOptions): Promise>; diff --git a/dist/types/jwt/decrypt.d.ts b/dist/types/jwt/decrypt.d.ts new file mode 100644 index 0000000000..ae163fe91f --- /dev/null +++ b/dist/types/jwt/decrypt.d.ts @@ -0,0 +1,27 @@ +import type { KeyLike, DecryptOptions, JWTClaimVerificationOptions, GetKeyFunction, CompactJWEHeaderParameters, FlattenedJWE, JWTDecryptResult, ResolvedKey } from '../types'; +/** Combination of JWE Decryption options and JWT Claims Set verification options. */ +export interface JWTDecryptOptions extends DecryptOptions, JWTClaimVerificationOptions { +} +/** + * Interface for JWT Decryption dynamic key resolution. No token components have been verified at + * the time of this function call. + */ +export interface JWTDecryptGetKey extends GetKeyFunction { +} +/** + * Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT + * Claims Set. + * + * @param jwt JSON Web Token value (encoded as JWE). + * @param key Private Key or Secret to decrypt and verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export declare function jwtDecrypt(jwt: string | Uint8Array, key: KeyLike | Uint8Array, options?: JWTDecryptOptions): Promise; +/** + * @param jwt JSON Web Token value (encoded as JWE). + * @param getKey Function resolving Private Key or Secret to decrypt and verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export declare function jwtDecrypt(jwt: string | Uint8Array, getKey: JWTDecryptGetKey, options?: JWTDecryptOptions): Promise>; diff --git a/dist/types/jwt/encrypt.d.ts b/dist/types/jwt/encrypt.d.ts new file mode 100644 index 0000000000..5610cc5b76 --- /dev/null +++ b/dist/types/jwt/encrypt.d.ts @@ -0,0 +1,75 @@ +import type { EncryptOptions, CompactJWEHeaderParameters, JWEKeyManagementHeaderParameters, KeyLike } from '../types'; +import { ProduceJWT } from './produce'; +/** + * The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. + * + */ +export declare class EncryptJWT extends ProduceJWT { + private _cek; + private _iv; + private _keyManagementParameters; + private _protectedHeader; + private _replicateIssuerAsHeader; + private _replicateSubjectAsHeader; + private _replicateAudienceAsHeader; + /** + * Sets the JWE Protected Header on the EncryptJWT object. + * + * @param protectedHeader JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc" + * (JWE Encryption Algorithm) properties. + */ + setProtectedHeader(protectedHeader: CompactJWEHeaderParameters): this; + /** + * Sets the JWE Key Management parameters to be used when encrypting. Use of this is method is + * really only needed for ECDH based algorithms when utilizing the Agreement PartyUInfo or + * Agreement PartyVInfo parameters. Other parameters will always be randomly generated when needed + * and missing. + * + * @param parameters JWE Key Management parameters. + */ + setKeyManagementParameters(parameters: JWEKeyManagementHeaderParameters): this; + /** + * Sets a content encryption key to use, by default a random suitable one is generated for the JWE + * enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param cek JWE Content Encryption Key. + */ + setContentEncryptionKey(cek: Uint8Array): this; + /** + * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable + * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. + * + * @deprecated You should not use this method. It is only really intended for test and vector + * validation purposes. + * @param iv JWE Initialization Vector. + */ + setInitializationVector(iv: Uint8Array): this; + /** + * Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateIssuerAsHeader(): this; + /** + * Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateSubjectAsHeader(): this; + /** + * Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} + */ + replicateAudienceAsHeader(): this; + /** + * Encrypts and returns the JWT. + * + * @param key Public Key or Secret to encrypt the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. + * @param options JWE Encryption options. + */ + encrypt(key: KeyLike | Uint8Array, options?: EncryptOptions): Promise; +} diff --git a/dist/types/jwt/produce.d.ts b/dist/types/jwt/produce.d.ts new file mode 100644 index 0000000000..5563a062e5 --- /dev/null +++ b/dist/types/jwt/produce.d.ts @@ -0,0 +1,54 @@ +import type { JWTPayload } from '../types'; +/** Generic class for JWT producing. */ +export declare class ProduceJWT { + protected _payload: JWTPayload; + /** @param payload The JWT Claims Set object. */ + constructor(payload: JWTPayload); + /** + * Set "iss" (Issuer) Claim. + * + * @param issuer "Issuer" Claim value to set on the JWT Claims Set. + */ + setIssuer(issuer: string): this; + /** + * Set "sub" (Subject) Claim. + * + * @param subject "sub" (Subject) Claim value to set on the JWT Claims Set. + */ + setSubject(subject: string): this; + /** + * Set "aud" (Audience) Claim. + * + * @param audience "aud" (Audience) Claim value to set on the JWT Claims Set. + */ + setAudience(audience: string | string[]): this; + /** + * Set "jti" (JWT ID) Claim. + * + * @param jwtId "jti" (JWT ID) Claim value to set on the JWT Claims Set. + */ + setJti(jwtId: string): this; + /** + * Set "nbf" (Not Before) Claim. + * + * @param input "nbf" (Not Before) Claim value to set on the JWT Claims Set. When number is passed + * that is used as a value, when string is passed it is resolved to a time span and added to the + * current timestamp. + */ + setNotBefore(input: number | string): this; + /** + * Set "exp" (Expiration Time) Claim. + * + * @param input "exp" (Expiration Time) Claim value to set on the JWT Claims Set. When number is + * passed that is used as a value, when string is passed it is resolved to a time span and added + * to the current timestamp. + */ + setExpirationTime(input: number | string): this; + /** + * Set "iat" (Issued At) Claim. + * + * @param input "iat" (Issued At) Claim value to set on the JWT Claims Set. Default is current + * timestamp. + */ + setIssuedAt(input?: number): this; +} diff --git a/dist/types/jwt/sign.d.ts b/dist/types/jwt/sign.d.ts new file mode 100644 index 0000000000..f11661d270 --- /dev/null +++ b/dist/types/jwt/sign.d.ts @@ -0,0 +1,23 @@ +import type { JWTHeaderParameters, KeyLike, SignOptions } from '../types'; +import { ProduceJWT } from './produce'; +/** + * The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. + * + */ +export declare class SignJWT extends ProduceJWT { + private _protectedHeader; + /** + * Sets the JWS Protected Header on the SignJWT object. + * + * @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property. + */ + setProtectedHeader(protectedHeader: JWTHeaderParameters): this; + /** + * Signs and returns the JWT. + * + * @param key Private Key or Secret to sign the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Sign options. + */ + sign(key: KeyLike | Uint8Array, options?: SignOptions): Promise; +} diff --git a/dist/types/jwt/unsecured.d.ts b/dist/types/jwt/unsecured.d.ts new file mode 100644 index 0000000000..14fe856a1f --- /dev/null +++ b/dist/types/jwt/unsecured.d.ts @@ -0,0 +1,21 @@ +import type { JWSHeaderParameters, JWTClaimVerificationOptions, JWTPayload } from '../types'; +import { ProduceJWT } from './produce'; +export interface UnsecuredResult { + payload: JWTPayload; + header: JWSHeaderParameters; +} +/** + * The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. + * + */ +export declare class UnsecuredJWT extends ProduceJWT { + /** Encodes the Unsecured JWT. */ + encode(): string; + /** + * Decodes an unsecured JWT. + * + * @param jwt Unsecured JWT to decode the payload of. + * @param options JWT Claims Set validation options. + */ + static decode(jwt: string, options?: JWTClaimVerificationOptions): UnsecuredResult; +} diff --git a/dist/types/jwt/verify.d.ts b/dist/types/jwt/verify.d.ts new file mode 100644 index 0000000000..1d98ebe7fd --- /dev/null +++ b/dist/types/jwt/verify.d.ts @@ -0,0 +1,29 @@ +import type { KeyLike, VerifyOptions, JWTClaimVerificationOptions, JWTHeaderParameters, GetKeyFunction, FlattenedJWSInput, JWTVerifyResult, ResolvedKey } from '../types'; +/** Combination of JWS Verification options and JWT Claims Set verification options. */ +export interface JWTVerifyOptions extends VerifyOptions, JWTClaimVerificationOptions { +} +/** + * Interface for JWT Verification dynamic key resolution. No token components have been verified at + * the time of this function call. + * + * @see [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. + */ +export interface JWTVerifyGetKey extends GetKeyFunction { +} +/** + * Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the + * JWT Claims Set. + * + * @param jwt JSON Web Token value (encoded as JWS). + * @param key Key to verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export declare function jwtVerify(jwt: string | Uint8Array, key: KeyLike | Uint8Array, options?: JWTVerifyOptions): Promise; +/** + * @param jwt JSON Web Token value (encoded as JWS). + * @param getKey Function resolving a key to verify the JWT with. See + * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. + * @param options JWT Decryption and JWT Claims Set validation options. + */ +export declare function jwtVerify(jwt: string | Uint8Array, getKey: JWTVerifyGetKey, options?: JWTVerifyOptions): Promise>; diff --git a/dist/types/key/export.d.ts b/dist/types/key/export.d.ts new file mode 100644 index 0000000000..32cd6c0926 --- /dev/null +++ b/dist/types/key/export.d.ts @@ -0,0 +1,21 @@ +import type { JWK, KeyLike } from '../types'; +/** + * Exports a runtime-specific public key representation (KeyObject or CryptoKey) to a PEM-encoded + * SPKI string format. + * + * @param key Key representation to transform to a PEM-encoded SPKI string format. + */ +export declare function exportSPKI(key: KeyLike): Promise; +/** + * Exports a runtime-specific private key representation (KeyObject or CryptoKey) to a PEM-encoded + * PKCS8 string format. + * + * @param key Key representation to transform to a PEM-encoded PKCS8 string format. + */ +export declare function exportPKCS8(key: KeyLike): Promise; +/** + * Exports a runtime-specific key representation (KeyLike) to a JWK. + * + * @param key Key representation to export as JWK. + */ +export declare function exportJWK(key: KeyLike | Uint8Array): Promise; diff --git a/dist/types/key/generate_key_pair.d.ts b/dist/types/key/generate_key_pair.d.ts new file mode 100644 index 0000000000..73849ce026 --- /dev/null +++ b/dist/types/key/generate_key_pair.d.ts @@ -0,0 +1,36 @@ +import type { KeyLike } from '../types'; +export interface GenerateKeyPairResult { + /** The generated Private Key. */ + privateKey: T; + /** Public Key corresponding to the generated Private Key. */ + publicKey: T; +} +export interface GenerateKeyPairOptions { + /** + * The EC "crv" (Curve) or OKP "crv" (Subtype of Key Pair) value to generate. The curve must be + * both supported on the runtime as well as applicable for the given JWA algorithm identifier. + */ + crv?: string; + /** + * A hint for RSA algorithms to generate an RSA key of a given `modulusLength` (Key size in bits). + * JOSE requires 2048 bits or larger. Default is 2048. + */ + modulusLength?: number; + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey SubtleCrypto.generateKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean; +} +/** + * Generates a private and a public key for a given JWA algorithm identifier. This can only generate + * asymmetric key pairs. For symmetric secrets use the `generateSecret` function. + * + * Note: Under Web Crypto API runtime the `privateKey` is generated with `extractable` set to + * `false` by default. + * + * @param alg JWA Algorithm Identifier to be used with the generated key pair. + * @param options Additional options passed down to the key pair generation. + */ +export declare function generateKeyPair(alg: string, options?: GenerateKeyPairOptions): Promise>; diff --git a/dist/types/key/generate_secret.d.ts b/dist/types/key/generate_secret.d.ts new file mode 100644 index 0000000000..fc498b94b5 --- /dev/null +++ b/dist/types/key/generate_secret.d.ts @@ -0,0 +1,19 @@ +import type { KeyLike } from '../types'; +export interface GenerateSecretOptions { + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey SubtleCrypto.generateKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean; +} +/** + * Generates a symmetric secret key for a given JWA algorithm identifier. + * + * Note: Under Web Crypto API runtime the secret key is generated with `extractable` set to `false` + * by default. + * + * @param alg JWA Algorithm Identifier to be used with the generated secret. + * @param options Additional options passed down to the secret generation. + */ +export declare function generateSecret(alg: string, options?: GenerateSecretOptions): Promise; diff --git a/dist/types/key/import.d.ts b/dist/types/key/import.d.ts new file mode 100644 index 0000000000..02275f33f6 --- /dev/null +++ b/dist/types/key/import.d.ts @@ -0,0 +1,54 @@ +import type { JWK, KeyLike } from '../types'; +export interface PEMImportOptions { + /** + * (Only effective in Web Crypto API runtimes) The value to use as + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey SubtleCrypto.importKey()} + * `extractable` argument. Default is false. + */ + extractable?: boolean; +} +/** + * Imports a PEM-encoded SPKI string as a runtime-specific public key representation (KeyObject or + * CryptoKey). + * + * @param pem PEM-encoded SPKI string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export declare function importSPKI(spki: string, alg: string, options?: PEMImportOptions): Promise; +/** + * Imports the SPKI from an X.509 string certificate as a runtime-specific public key representation + * (KeyObject or CryptoKey). + * + * @param pem X.509 certificate string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export declare function importX509(x509: string, alg: string, options?: PEMImportOptions): Promise; +/** + * Imports a PEM-encoded PKCS#8 string as a runtime-specific private key representation (KeyObject + * or CryptoKey). + * + * @param pem PEM-encoded PKCS#8 string + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key, its presence is only enforced in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + */ +export declare function importPKCS8(pkcs8: string, alg: string, options?: PEMImportOptions): Promise; +/** + * Imports a JWK to a runtime-specific key representation (KeyLike). Either JWK "alg" (Algorithm) + * Parameter must be present or the optional "alg" argument. When running on a runtime using + * {@link https://www.w3.org/TR/WebCryptoAPI/ Web Cryptography API} the jwk parameters "use", + * "key_ops", and "ext" are also used in the resulting `CryptoKey`. + * + * @param jwk JSON Web Key. + * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used + * with the imported key. Default is the "alg" property on the JWK, its presence is only enforced + * in Web Crypto API runtimes. See + * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. + * @param octAsKeyObject Forces a symmetric key to be imported to a KeyObject or CryptoKey. Default + * is true unless JWK "ext" (Extractable) is true. + */ +export declare function importJWK(jwk: JWK, alg?: string, octAsKeyObject?: boolean): Promise; diff --git a/dist/types/types.d.ts b/dist/types/types.d.ts new file mode 100644 index 0000000000..f5b6820701 --- /dev/null +++ b/dist/types/types.d.ts @@ -0,0 +1,572 @@ +/** + * KeyLike are runtime-specific classes representing asymmetric keys or symmetric secrets. These are + * instances of {@link https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey CryptoKey} and + * additionally {@link https://nodejs.org/api/crypto.html#class-keyobject KeyObject} in Node.js + * runtime. + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array Uint8Array} + * instances are also accepted as symmetric secret representation only. + * + * [Key Import Functions](../modules/key_import.md) can be used to import PEM, or JWK formatted + * asymmetric keys and certificates to these runtime-specific representations. + * + * In Node.js the {@link https://nodejs.org/api/buffer.html#buffer Buffer} class is a subclass of + * Uint8Array and so Buffer can be provided for symmetric secrets as well. + * + * {@link https://nodejs.org/api/crypto.html#class-keyobject KeyObject} is a representation of a + * key/secret available in the Node.js runtime. In addition to the import functions of this library + * you may use the runtime APIs + * {@link https://nodejs.org/api/crypto.html#cryptocreatepublickeykey crypto.createPublicKey}, + * {@link https://nodejs.org/api/crypto.html#cryptocreateprivatekeykey crypto.createPrivateKey}, and + * {@link https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding crypto.createSecretKey} + * to obtain a `KeyObject` from your existing key material. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey CryptoKey} is a representation + * of a key/secret available in the Browser and Web-interoperable runtimes. In addition to the + * import functions of this library you may use the + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey SubtleCrypto.importKey} + * API to obtain a CryptoKey from your existing key material. + * + */ +export type KeyLike = { type: string } + +/** + * JSON Web Key ({@link https://www.rfc-editor.org/rfc/rfc7517 JWK}). "RSA", "EC", "OKP", and "oct" + * key types are supported. + */ +export interface JWK { + /** JWK "alg" (Algorithm) Parameter. */ + alg?: string + crv?: string + d?: string + dp?: string + dq?: string + e?: string + /** JWK "ext" (Extractable) Parameter. */ + ext?: boolean + k?: string + /** JWK "key_ops" (Key Operations) Parameter. */ + key_ops?: string[] + /** JWK "kid" (Key ID) Parameter. */ + kid?: string + /** JWK "kty" (Key Type) Parameter. */ + kty?: string + n?: string + oth?: Array<{ + d?: string + r?: string + t?: string + }> + p?: string + q?: string + qi?: string + /** JWK "use" (Public Key Use) Parameter. */ + use?: string + x?: string + y?: string + /** JWK "x5c" (X.509 Certificate Chain) Parameter. */ + x5c?: string[] + /** JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter. */ + x5t?: string + /** "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter. */ + 'x5t#S256'?: string + /** JWK "x5u" (X.509 URL) Parameter. */ + x5u?: string + + [propName: string]: unknown +} + +/** + * Generic Interface for consuming operations dynamic key resolution. No token components have been + * verified at the time of this function call. + * + * If you cannot match a key suitable for the token, throw an error instead. + * + * @param protectedHeader JWE or JWS Protected Header. + * @param token The consumed JWE or JWS token. + */ +export interface GetKeyFunction { + (protectedHeader: T, token: T2): Promise | KeyLike | Uint8Array +} + +/** + * Flattened JWS definition for verify function inputs, allows payload as Uint8Array for detached + * signature validation. + */ +export interface FlattenedJWSInput { + /** + * The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS + * Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented + * as an unencoded JSON object, rather than as a string. These Header Parameter values are not + * integrity protected. + */ + header?: JWSHeaderParameters + + /** + * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 + * "b64": false is used the value passed may also be a Uint8Array. + */ + payload: string | Uint8Array + + /** + * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected + * Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These + * Header Parameter values are integrity protected. + */ + protected?: string + + /** The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). */ + signature: string +} + +/** + * General JWS definition for verify function inputs, allows payload as Uint8Array for detached + * signature validation. + */ +export interface GeneralJWSInput { + /** + * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when + * JWS Unencoded Payload ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) "b64": false is + * used the value passed may also be a Uint8Array. + */ + payload: string | Uint8Array + + /** + * The "signatures" member value MUST be an array of JSON objects. Each object represents a + * signature or MAC over the JWS Payload and the JWS Protected Header. + */ + signatures: Omit[] +} + +/** + * Flattened JWS definition. Payload is returned as an empty string when JWS Unencoded Payload + * ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. + */ +export interface FlattenedJWS extends Partial { + payload: string + signature: string +} + +/** + * General JWS definition. Payload is returned as an empty string when JWS Unencoded Payload + * ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. + */ +export interface GeneralJWS { + payload: string + signatures: Omit[] +} + +export interface JoseHeaderParameters { + /** "kid" (Key ID) Header Parameter. */ + kid?: string + + /** "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter. */ + x5t?: string + + /** "x5c" (X.509 Certificate Chain) Header Parameter. */ + x5c?: string[] + + /** "x5u" (X.509 URL) Header Parameter. */ + x5u?: string + + /** "jku" (JWK Set URL) Header Parameter. */ + jku?: string + + /** "jwk" (JSON Web Key) Header Parameter. */ + jwk?: Pick + + /** "typ" (Type) Header Parameter. */ + typ?: string + + /** "cty" (Content Type) Header Parameter. */ + cty?: string +} + +/** Recognized JWS Header Parameters, any other Header Members may also be present. */ +export interface JWSHeaderParameters extends JoseHeaderParameters { + /** JWS "alg" (Algorithm) Header Parameter. */ + alg?: string + + /** + * This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing + * Input computation as per {@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}. + */ + b64?: boolean + + /** JWS "crit" (Critical) Header Parameter. */ + crit?: string[] + + /** Any other JWS Header member. */ + [propName: string]: unknown +} + +/** Recognized JWE Key Management-related Header Parameters. */ +export interface JWEKeyManagementHeaderParameters { + apu?: Uint8Array + apv?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + p2c?: number + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + p2s?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + iv?: Uint8Array + /** + * @deprecated You should not use this parameter. It is only really intended for test and vector + * validation purposes. + */ + epk?: KeyLike +} + +/** Flattened JWE definition. */ +export interface FlattenedJWE { + /** + * The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD + * value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a + * base64url-encoded value to be integrity protected but not encrypted. + */ + aad?: string + + /** The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). */ + ciphertext: string + + /** + * The "encrypted_key" member MUST be present and contain the value BASE64URL(JWE Encrypted Key) + * when the JWE Encrypted Key value is non-empty; otherwise, it MUST be absent. + */ + encrypted_key?: string + + /** + * The "header" member MUST be present and contain the value JWE Per- Recipient Unprotected Header + * when the JWE Per-Recipient Unprotected Header value is non-empty; otherwise, it MUST be absent. + * This value is represented as an unencoded JSON object, rather than as a string. These Header + * Parameter values are not integrity protected. + */ + header?: JWEHeaderParameters + + /** + * The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when + * the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent. + */ + iv: string + + /** + * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected + * Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These + * Header Parameter values are integrity protected. + */ + protected?: string + + /** + * The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when + * the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent. + */ + tag: string + + /** + * The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header + * when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This + * value is represented as an unencoded JSON object, rather than as a string. These Header + * Parameter values are not integrity protected. + */ + unprotected?: JWEHeaderParameters +} + +export interface GeneralJWE extends Omit { + recipients: Pick[] +} + +/** Recognized JWE Header Parameters, any other Header members may also be present. */ +export interface JWEHeaderParameters extends JoseHeaderParameters { + /** JWE "alg" (Algorithm) Header Parameter. */ + alg?: string + + /** JWE "enc" (Encryption Algorithm) Header Parameter. */ + enc?: string + + /** JWE "crit" (Critical) Header Parameter. */ + crit?: string[] + + /** JWE "zip" (Compression Algorithm) Header Parameter. */ + zip?: string + + /** Any other JWE Header member. */ + [propName: string]: unknown +} + +/** Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. */ +export interface CritOption { + /** + * An object with keys representing recognized "crit" (Critical) Header Parameter names. The value + * for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity + * protected, `false` when it's irrelevant. + * + * This makes the "Extension Header Parameter "..." is not recognized" error go away. + * + * Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" + * (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically + * correct when provided and that it is optionally integrity protected. It will not process the + * Header Parameter in any way or reject the operation if it is missing. You MUST still verify the + * Header Parameter was present and process it according to the profile's validation steps after + * the operation succeeds. + * + * The JWS extension Header Parameter `b64` is always recognized and processed properly. No other + * registered Header Parameters that need this kind of default built-in treatment are currently + * available. + */ + crit?: { + [propName: string]: boolean + } +} + +/** JWE Decryption options. */ +export interface DecryptOptions extends CritOption { + /** A list of accepted JWE "alg" (Algorithm) Header Parameter values. */ + keyManagementAlgorithms?: string[] + + /** + * A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all + * "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed. + */ + contentEncryptionAlgorithms?: string[] + + /** + * In a browser runtime you have to provide an implementation for Inflate Raw when you expect JWEs + * with compressed plaintext. + */ + inflateRaw?: InflateFunction + + /** + * (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter + * value. The PBKDF2 iteration count defines the algorithm's computational expense. By default + * this value is set to 10000. + */ + maxPBES2Count?: number +} + +/** JWE Deflate option. */ +export interface DeflateOption { + /** + * In a browser runtime you have to provide an implementation for Deflate Raw when you will be + * producing JWEs with compressed plaintext. + */ + deflateRaw?: DeflateFunction +} + +/** JWE Encryption options. */ +export interface EncryptOptions extends CritOption, DeflateOption {} + +/** JWT Claims Set verification options. */ +export interface JWTClaimVerificationOptions { + /** Expected JWT "aud" (Audience) Claim value(s). */ + audience?: string | string[] + + /** + * Expected clock tolerance + * + * - In seconds when number (e.g. 5) + * - Parsed as seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). + */ + clockTolerance?: string | number + + /** Expected JWT "iss" (Issuer) Claim value(s). */ + issuer?: string | string[] + + /** + * Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. + * + * - In seconds when number (e.g. 5) + * - Parsed as seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). + */ + maxTokenAge?: string | number + + /** Expected JWT "sub" (Subject) Claim value. */ + subject?: string + + /** Expected JWT "typ" (Type) Header Parameter value. */ + typ?: string + + /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */ + currentDate?: Date +} + +/** JWS Verification options. */ +export interface VerifyOptions extends CritOption { + /** + * A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg" + * (Algorithm) values applicable for the used key/secret are allowed. Note: "none" is never + * accepted. + */ + algorithms?: string[] +} + +/** JWS Signing options. */ +export interface SignOptions extends CritOption {} + +/** Recognized JWT Claims Set members, any other members may also be present. */ +export interface JWTPayload { + /** + * JWT Issuer + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1 RFC7519#section-4.1.1} + */ + iss?: string + + /** + * JWT Subject + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2 RFC7519#section-4.1.2} + */ + sub?: string + + /** + * JWT Audience + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3 RFC7519#section-4.1.3} + */ + aud?: string | string[] + + /** + * JWT ID + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7 RFC7519#section-4.1.7} + */ + jti?: string + + /** + * JWT Not Before + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5 RFC7519#section-4.1.5} + */ + nbf?: number + + /** + * JWT Expiration Time + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4 RFC7519#section-4.1.4} + */ + exp?: number + + /** + * JWT Issued At + * + * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 RFC7519#section-4.1.6} + */ + iat?: number + + /** Any other JWT Claim Set member. */ + [propName: string]: unknown +} + +/** + * Deflate Raw implementation, e.g. promisified + * {@link https://nodejs.org/api/zlib.html#zlibdeflaterawbuffer-options-callback zlib.deflateRaw}. + */ +export interface DeflateFunction { + (input: Uint8Array): Promise +} + +/** + * Inflate Raw implementation, e.g. promisified + * {@link https://nodejs.org/api/zlib.html#zlibinflaterawbuffer-options-callback zlib.inflateRaw}. + */ +export interface InflateFunction { + (input: Uint8Array): Promise +} + +export interface FlattenedDecryptResult { + /** JWE AAD. */ + additionalAuthenticatedData?: Uint8Array + + /** Plaintext. */ + plaintext: Uint8Array + + /** JWE Protected Header. */ + protectedHeader?: JWEHeaderParameters + + /** JWE Shared Unprotected Header. */ + sharedUnprotectedHeader?: JWEHeaderParameters + + /** JWE Per-Recipient Unprotected Header. */ + unprotectedHeader?: JWEHeaderParameters +} + +export interface GeneralDecryptResult extends FlattenedDecryptResult {} + +export interface CompactDecryptResult { + /** Plaintext. */ + plaintext: Uint8Array + + /** JWE Protected Header. */ + protectedHeader: CompactJWEHeaderParameters +} + +export interface FlattenedVerifyResult { + /** JWS Payload. */ + payload: Uint8Array + + /** JWS Protected Header. */ + protectedHeader?: JWSHeaderParameters + + /** JWS Unprotected Header. */ + unprotectedHeader?: JWSHeaderParameters +} + +export interface GeneralVerifyResult extends FlattenedVerifyResult {} + +export interface CompactVerifyResult { + /** JWS Payload. */ + payload: Uint8Array + + /** JWS Protected Header. */ + protectedHeader: CompactJWSHeaderParameters +} + +export interface JWTVerifyResult { + /** JWT Claims Set. */ + payload: JWTPayload + + /** JWS Protected Header. */ + protectedHeader: JWTHeaderParameters +} + +export interface JWTDecryptResult { + /** JWT Claims Set. */ + payload: JWTPayload + + /** JWE Protected Header. */ + protectedHeader: CompactJWEHeaderParameters +} + +export interface ResolvedKey { + /** Key resolved from the key resolver function. */ + key: T | Uint8Array +} + +/** Recognized Compact JWS Header Parameters, any other Header Members may also be present. */ +export interface CompactJWSHeaderParameters extends JWSHeaderParameters { + alg: string +} + +/** Recognized Signed JWT Header Parameters, any other Header Members may also be present. */ +export interface JWTHeaderParameters extends CompactJWSHeaderParameters { + b64?: true +} + +/** Recognized Compact JWE Header Parameters, any other Header Members may also be present. */ +export interface CompactJWEHeaderParameters extends JWEHeaderParameters { + alg: string + enc: string +} + +/** JSON Web Key Set */ +export interface JSONWebKeySet { + keys: JWK[] +} diff --git a/dist/types/util/base64url.d.ts b/dist/types/util/base64url.d.ts new file mode 100644 index 0000000000..ec08361d2c --- /dev/null +++ b/dist/types/util/base64url.d.ts @@ -0,0 +1,19 @@ +/** + * Utility function to encode a string or Uint8Array as a base64url string. + * + * @param input Value that will be base64url-encoded. + */ +interface Base64UrlEncode { + (input: Uint8Array | string): string; +} +/** + * Utility function to decode a base64url encoded string. + * + * @param input Value that will be base64url-decoded. + */ +interface Base64UrlDecode { + (input: Uint8Array | string): Uint8Array; +} +export declare const encode: Base64UrlEncode; +export declare const decode: Base64UrlDecode; +export {}; diff --git a/dist/types/util/decode_jwt.d.ts b/dist/types/util/decode_jwt.d.ts new file mode 100644 index 0000000000..7ed63270db --- /dev/null +++ b/dist/types/util/decode_jwt.d.ts @@ -0,0 +1,10 @@ +import type { JWTPayload } from '../types'; +/** + * Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set types or + * values. This does not validate the JWS Signature. For a proper Signed JWT Claims Set validation + * and JWS signature verification use `jose.jwtVerify()`. For an encrypted JWT Claims Set validation + * and JWE decryption use `jose.jwtDecrypt()`. + * + * @param jwt JWT token in compact JWS serialization. + */ +export declare function decodeJwt(jwt: string): JWTPayload; diff --git a/dist/types/util/decode_protected_header.d.ts b/dist/types/util/decode_protected_header.d.ts new file mode 100644 index 0000000000..b29c4d7f7e --- /dev/null +++ b/dist/types/util/decode_protected_header.d.ts @@ -0,0 +1,8 @@ +import type { JWSHeaderParameters, JWEHeaderParameters } from '../types'; +export type ProtectedHeaderParameters = JWSHeaderParameters & JWEHeaderParameters; +/** + * Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization. + * + * @param token JWE/JWS/JWT token in any JOSE serialization. + */ +export declare function decodeProtectedHeader(token: string | object): ProtectedHeaderParameters; diff --git a/dist/types/util/errors.d.ts b/dist/types/util/errors.d.ts new file mode 100644 index 0000000000..033ea61a8d --- /dev/null +++ b/dist/types/util/errors.d.ts @@ -0,0 +1,142 @@ +import type { KeyLike } from '../types'; +/** + * A generic Error that all other JOSE specific Error subclasses extend. + * + */ +export declare class JOSEError extends Error { + /** A unique error code for the particular error subclass. */ + static get code(): string; + /** A unique error code for the particular error subclass. */ + code: string; + constructor(message?: string); +} +/** + * An error subclass thrown when a JWT Claim Set member validation fails. + * + */ +export declare class JWTClaimValidationFailed extends JOSEError { + static get code(): 'ERR_JWT_CLAIM_VALIDATION_FAILED'; + code: string; + /** The Claim for which the validation failed. */ + claim: string; + /** Reason code for the validation failure. */ + reason: string; + constructor(message: string, claim?: string, reason?: string); +} +/** + * An error subclass thrown when a JWT is expired. + * + */ +export declare class JWTExpired extends JOSEError implements JWTClaimValidationFailed { + static get code(): 'ERR_JWT_EXPIRED'; + code: string; + /** The Claim for which the validation failed. */ + claim: string; + /** Reason code for the validation failure. */ + reason: string; + constructor(message: string, claim?: string, reason?: string); +} +/** + * An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. + * + */ +export declare class JOSEAlgNotAllowed extends JOSEError { + static get code(): 'ERR_JOSE_ALG_NOT_ALLOWED'; + code: string; +} +/** + * An error subclass thrown when a particular feature or algorithm is not supported by this + * implementation or JOSE in general. + * + */ +export declare class JOSENotSupported extends JOSEError { + static get code(): 'ERR_JOSE_NOT_SUPPORTED'; + code: string; +} +/** + * An error subclass thrown when a JWE ciphertext decryption fails. + * + */ +export declare class JWEDecryptionFailed extends JOSEError { + static get code(): 'ERR_JWE_DECRYPTION_FAILED'; + code: string; + message: string; +} +/** + * An error subclass thrown when a JWE is invalid. + * + */ +export declare class JWEInvalid extends JOSEError { + static get code(): 'ERR_JWE_INVALID'; + code: string; +} +/** + * An error subclass thrown when a JWS is invalid. + * + */ +export declare class JWSInvalid extends JOSEError { + static get code(): 'ERR_JWS_INVALID'; + code: string; +} +/** + * An error subclass thrown when a JWT is invalid. + * + */ +export declare class JWTInvalid extends JOSEError { + static get code(): 'ERR_JWT_INVALID'; + code: string; +} +/** + * An error subclass thrown when a JWK is invalid. + * + */ +export declare class JWKInvalid extends JOSEError { + static get code(): 'ERR_JWK_INVALID'; + code: string; +} +/** + * An error subclass thrown when a JWKS is invalid. + * + */ +export declare class JWKSInvalid extends JOSEError { + static get code(): 'ERR_JWKS_INVALID'; + code: string; +} +/** + * An error subclass thrown when no keys match from a JWKS. + * + */ +export declare class JWKSNoMatchingKey extends JOSEError { + static get code(): 'ERR_JWKS_NO_MATCHING_KEY'; + code: string; + message: string; +} +/** + * An error subclass thrown when multiple keys match from a JWKS. + * + */ +export declare class JWKSMultipleMatchingKeys extends JOSEError { + /** @ignore */ + [Symbol.asyncIterator]: () => AsyncIterableIterator; + static get code(): 'ERR_JWKS_MULTIPLE_MATCHING_KEYS'; + code: string; + message: string; +} +/** + * Timeout was reached when retrieving the JWKS response. + * + */ +export declare class JWKSTimeout extends JOSEError { + static get code(): 'ERR_JWKS_TIMEOUT'; + code: string; + message: string; +} +/** + * An error subclass thrown when JWS signature verification fails. + * + */ +export declare class JWSSignatureVerificationFailed extends JOSEError { + static get code(): 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'; + code: string; + message: string; +} diff --git a/docs/classes/jwe_compact_encrypt.CompactEncrypt.md b/docs/classes/jwe_compact_encrypt.CompactEncrypt.md index 24dfdb4593..ee97221605 100644 --- a/docs/classes/jwe_compact_encrypt.CompactEncrypt.md +++ b/docs/classes/jwe_compact_encrypt.CompactEncrypt.md @@ -1,8 +1,12 @@ # Class: CompactEncrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The CompactEncrypt class is a utility for creating Compact JWE strings. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The CompactEncrypt class is used to build and encrypt Compact JWE strings. **`example`** Usage @@ -54,7 +58,7 @@ Encrypts and resolves the value of the Compact JWE string. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../interfaces/types.EncryptOptions.md) | JWE Encryption options. | #### Returns diff --git a/docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md b/docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md index ae31e915f5..d55e917489 100644 --- a/docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md +++ b/docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md @@ -1,8 +1,12 @@ # Class: FlattenedEncrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The FlattenedEncrypt class is a utility for creating Flattened JWE objects. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. **`example`** Usage @@ -58,7 +62,7 @@ Encrypts and resolves the value of the Flattened JWE object. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../interfaces/types.EncryptOptions.md) | JWE Encryption options. | #### Returns diff --git a/docs/classes/jwe_general_encrypt.GeneralEncrypt.md b/docs/classes/jwe_general_encrypt.GeneralEncrypt.md index 1db3d433bd..4c2a81f5cb 100644 --- a/docs/classes/jwe_general_encrypt.GeneralEncrypt.md +++ b/docs/classes/jwe_general_encrypt.GeneralEncrypt.md @@ -1,8 +1,12 @@ # Class: GeneralEncrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The GeneralEncrypt class is a utility for creating General JWE objects. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The GeneralEncrypt class is used to build and encrypt General JWE objects. **`example`** Usage @@ -58,7 +62,7 @@ Adds an additional recipient for the General JWE object. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the Content Encryption Key for the recipient with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the Content Encryption Key for the recipient with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`CritOption`](../interfaces/types.CritOption.md) | JWE Encryption options. | #### Returns diff --git a/docs/classes/jws_compact_sign.CompactSign.md b/docs/classes/jws_compact_sign.CompactSign.md index aabc4fb513..23a8247af5 100644 --- a/docs/classes/jws_compact_sign.CompactSign.md +++ b/docs/classes/jws_compact_sign.CompactSign.md @@ -1,8 +1,12 @@ # Class: CompactSign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The CompactSign class is a utility for creating Compact JWS strings. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The CompactSign class is used to build and sign Compact JWS strings. **`example`** Usage @@ -69,7 +73,7 @@ Signs and resolves the value of the Compact JWS string. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../interfaces/types.SignOptions.md) | JWS Sign options. | #### Returns diff --git a/docs/classes/jws_flattened_sign.FlattenedSign.md b/docs/classes/jws_flattened_sign.FlattenedSign.md index 4c275202d4..1ae06f2c7b 100644 --- a/docs/classes/jws_flattened_sign.FlattenedSign.md +++ b/docs/classes/jws_flattened_sign.FlattenedSign.md @@ -1,8 +1,12 @@ # Class: FlattenedSign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The FlattenedSign class is a utility for creating Flattened JWS objects. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The FlattenedSign class is used to build and sign Flattened JWS objects. **`example`** Usage @@ -88,7 +92,7 @@ Signs and resolves the value of the Flattened JWS object. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../interfaces/types.SignOptions.md) | JWS Sign options. | #### Returns diff --git a/docs/classes/jws_general_sign.GeneralSign.md b/docs/classes/jws_general_sign.GeneralSign.md index da0f654320..91905e578a 100644 --- a/docs/classes/jws_general_sign.GeneralSign.md +++ b/docs/classes/jws_general_sign.GeneralSign.md @@ -1,8 +1,12 @@ # Class: GeneralSign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The GeneralSign class is a utility for creating General JWS objects. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The GeneralSign class is used to build and sign General JWS objects. **`example`** Usage @@ -54,7 +58,7 @@ Adds an additional signature for the General JWS object. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the individual JWS signature with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the individual JWS signature with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../interfaces/types.SignOptions.md) | JWS Sign options. | #### Returns diff --git a/docs/classes/jwt_encrypt.EncryptJWT.md b/docs/classes/jwt_encrypt.EncryptJWT.md index 9cb2936a83..da0de59878 100644 --- a/docs/classes/jwt_encrypt.EncryptJWT.md +++ b/docs/classes/jwt_encrypt.EncryptJWT.md @@ -1,8 +1,12 @@ # Class: EncryptJWT -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The EncryptJWT class is a utility for creating Compact JWE formatted JWT strings. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. **`example`** Usage @@ -67,7 +71,7 @@ Encrypts and returns the JWT. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Public Key or Secret to encrypt the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../interfaces/types.EncryptOptions.md) | JWE Encryption options. | #### Returns @@ -80,8 +84,9 @@ ___ ▸ **replicateAudienceAsHeader**(): [`EncryptJWT`](jwt_encrypt.EncryptJWT.md) -Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter as per -[RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3). +Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. + +**`see`** [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) #### Returns @@ -93,8 +98,9 @@ ___ ▸ **replicateIssuerAsHeader**(): [`EncryptJWT`](jwt_encrypt.EncryptJWT.md) -Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter as per -[RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3). +Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. + +**`see`** [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) #### Returns @@ -106,8 +112,9 @@ ___ ▸ **replicateSubjectAsHeader**(): [`EncryptJWT`](jwt_encrypt.EncryptJWT.md) -Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter as per -[RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3). +Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. + +**`see`** [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) #### Returns diff --git a/docs/classes/jwt_produce.ProduceJWT.md b/docs/classes/jwt_produce.ProduceJWT.md index a091d7efa4..331fe558cc 100644 --- a/docs/classes/jwt_produce.ProduceJWT.md +++ b/docs/classes/jwt_produce.ProduceJWT.md @@ -1,6 +1,10 @@ # Class: ProduceJWT -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Generic class for JWT producing. diff --git a/docs/classes/jwt_sign.SignJWT.md b/docs/classes/jwt_sign.SignJWT.md index 650d00515b..6992d9214e 100644 --- a/docs/classes/jwt_sign.SignJWT.md +++ b/docs/classes/jwt_sign.SignJWT.md @@ -1,8 +1,12 @@ # Class: SignJWT -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -The SignJWT class is a utility for creating Compact JWS formatted JWT strings. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. **`example`** Usage with a symmetric secret @@ -282,7 +286,7 @@ Signs and returns the JWT. | Name | Type | Description | | :------ | :------ | :------ | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to sign the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../interfaces/types.SignOptions.md) | JWT Sign options. | #### Returns diff --git a/docs/classes/jwt_unsecured.UnsecuredJWT.md b/docs/classes/jwt_unsecured.UnsecuredJWT.md index b3f2925b4d..0e3a0b65d2 100644 --- a/docs/classes/jwt_unsecured.UnsecuredJWT.md +++ b/docs/classes/jwt_unsecured.UnsecuredJWT.md @@ -1,6 +1,10 @@ # Class: UnsecuredJWT -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. diff --git a/docs/classes/util_errors.JOSEAlgNotAllowed.md b/docs/classes/util_errors.JOSEAlgNotAllowed.md index 7e930a0bb6..3730ba7834 100644 --- a/docs/classes/util_errors.JOSEAlgNotAllowed.md +++ b/docs/classes/util_errors.JOSEAlgNotAllowed.md @@ -1,9 +1,29 @@ # Class: JOSEAlgNotAllowed -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JOSE_ALG_NOT_ALLOWED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JOSEAlgNotAllowed) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JOSEError.md b/docs/classes/util_errors.JOSEError.md index 1e84d7348f..7f4f84daf7 100644 --- a/docs/classes/util_errors.JOSEError.md +++ b/docs/classes/util_errors.JOSEError.md @@ -1,8 +1,20 @@ # Class: JOSEError -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -A generic Error subclass that all other specific JOSE Error subclasses inherit from. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +A generic Error that all other JOSE specific Error subclasses extend. + +**`example`** Checking thrown error is a JOSE one + +```js +if (err instanceof jose.errors.JOSEError) { + // ... +} +``` ## Table of contents diff --git a/docs/classes/util_errors.JOSENotSupported.md b/docs/classes/util_errors.JOSENotSupported.md index 0a07e13d5b..baa8ca5768 100644 --- a/docs/classes/util_errors.JOSENotSupported.md +++ b/docs/classes/util_errors.JOSENotSupported.md @@ -1,10 +1,30 @@ # Class: JOSENotSupported -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a particular feature or algorithm is not supported by this implementation or JOSE in general. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JOSE_NOT_SUPPORTED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JOSENotSupported) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWEDecryptionFailed.md b/docs/classes/util_errors.JWEDecryptionFailed.md index 29d0b935e7..7cf24af49d 100644 --- a/docs/classes/util_errors.JWEDecryptionFailed.md +++ b/docs/classes/util_errors.JWEDecryptionFailed.md @@ -1,9 +1,29 @@ # Class: JWEDecryptionFailed -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWE ciphertext decryption fails. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWE_DECRYPTION_FAILED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWEDecryptionFailed) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWEInvalid.md b/docs/classes/util_errors.JWEInvalid.md index dacbe0cf87..8236b25a33 100644 --- a/docs/classes/util_errors.JWEInvalid.md +++ b/docs/classes/util_errors.JWEInvalid.md @@ -1,9 +1,29 @@ # Class: JWEInvalid -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWE is invalid. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWE_INVALID') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWEInvalid) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWKInvalid.md b/docs/classes/util_errors.JWKInvalid.md index 13487480ed..2620e6643b 100644 --- a/docs/classes/util_errors.JWKInvalid.md +++ b/docs/classes/util_errors.JWKInvalid.md @@ -1,9 +1,29 @@ # Class: JWKInvalid -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWK is invalid. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWK_INVALID') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWKInvalid) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWKSInvalid.md b/docs/classes/util_errors.JWKSInvalid.md index 77aa849583..d624b107ed 100644 --- a/docs/classes/util_errors.JWKSInvalid.md +++ b/docs/classes/util_errors.JWKSInvalid.md @@ -1,9 +1,29 @@ # Class: JWKSInvalid -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWKS is invalid. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWKS_INVALID') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWKSInvalid) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWKSMultipleMatchingKeys.md b/docs/classes/util_errors.JWKSMultipleMatchingKeys.md index 57586df82e..201d2b440c 100644 --- a/docs/classes/util_errors.JWKSMultipleMatchingKeys.md +++ b/docs/classes/util_errors.JWKSMultipleMatchingKeys.md @@ -1,9 +1,29 @@ # Class: JWKSMultipleMatchingKeys -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when multiple keys match from a JWKS. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWKSMultipleMatchingKeys) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWKSNoMatchingKey.md b/docs/classes/util_errors.JWKSNoMatchingKey.md index 253fd8edaa..8707604372 100644 --- a/docs/classes/util_errors.JWKSNoMatchingKey.md +++ b/docs/classes/util_errors.JWKSNoMatchingKey.md @@ -1,9 +1,29 @@ # Class: JWKSNoMatchingKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when no keys match from a JWKS. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWKS_NO_MATCHING_KEY') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWKSNoMatchingKey) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWKSTimeout.md b/docs/classes/util_errors.JWKSTimeout.md index e7a81f3fbf..8e49ec2c8a 100644 --- a/docs/classes/util_errors.JWKSTimeout.md +++ b/docs/classes/util_errors.JWKSTimeout.md @@ -1,9 +1,29 @@ # Class: JWKSTimeout -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Timeout was reached when retrieving the JWKS response. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWKS_TIMEOUT') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWKSTimeout) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWSInvalid.md b/docs/classes/util_errors.JWSInvalid.md index 6195099864..b08cd69513 100644 --- a/docs/classes/util_errors.JWSInvalid.md +++ b/docs/classes/util_errors.JWSInvalid.md @@ -1,9 +1,29 @@ # Class: JWSInvalid -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWS is invalid. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWS_INVALID') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWSInvalid) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWSSignatureVerificationFailed.md b/docs/classes/util_errors.JWSSignatureVerificationFailed.md index 1fc39d843a..976b3b1ad9 100644 --- a/docs/classes/util_errors.JWSSignatureVerificationFailed.md +++ b/docs/classes/util_errors.JWSSignatureVerificationFailed.md @@ -1,9 +1,29 @@ # Class: JWSSignatureVerificationFailed -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when JWS signature verification fails. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWSSignatureVerificationFailed) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/classes/util_errors.JWTClaimValidationFailed.md b/docs/classes/util_errors.JWTClaimValidationFailed.md index 26a54893d8..97b0612b4d 100644 --- a/docs/classes/util_errors.JWTClaimValidationFailed.md +++ b/docs/classes/util_errors.JWTClaimValidationFailed.md @@ -1,9 +1,29 @@ # Class: JWTClaimValidationFailed -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWT Claim Set member validation fails. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWT_CLAIM_VALIDATION_FAILED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWTClaimValidationFailed) { + // ... +} +``` + ## Implemented by - [`JWTExpired`](util_errors.JWTExpired.md) diff --git a/docs/classes/util_errors.JWTExpired.md b/docs/classes/util_errors.JWTExpired.md index 8aff890b56..ba36ccd964 100644 --- a/docs/classes/util_errors.JWTExpired.md +++ b/docs/classes/util_errors.JWTExpired.md @@ -1,9 +1,29 @@ # Class: JWTExpired -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWT is expired. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWT_EXPIRED') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWTExpired) { + // ... +} +``` + ## Implements - [`JWTClaimValidationFailed`](util_errors.JWTClaimValidationFailed.md) diff --git a/docs/classes/util_errors.JWTInvalid.md b/docs/classes/util_errors.JWTInvalid.md index 6486b247b0..79a5f8e59f 100644 --- a/docs/classes/util_errors.JWTInvalid.md +++ b/docs/classes/util_errors.JWTInvalid.md @@ -1,9 +1,29 @@ # Class: JWTInvalid -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- An error subclass thrown when a JWT is invalid. +**`example`** Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWT_INVALID') { + // ... +} +``` + +**`example`** Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWTInvalid) { + // ... +} +``` + ## Table of contents ### Constructors diff --git a/docs/functions/jwe_compact_decrypt.compactDecrypt.md b/docs/functions/jwe_compact_decrypt.compactDecrypt.md index 4936467aff..dee3b36d12 100644 --- a/docs/functions/jwe_compact_decrypt.compactDecrypt.md +++ b/docs/functions/jwe_compact_decrypt.compactDecrypt.md @@ -1,6 +1,10 @@ # Function: compactDecrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **compactDecrypt**(`jwe`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`CompactDecryptResult`](../interfaces/types.CompactDecryptResult.md)\> @@ -23,7 +27,7 @@ console.log(new TextDecoder().decode(plaintext)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | Compact JWE. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns @@ -43,7 +47,7 @@ console.log(new TextDecoder().decode(plaintext)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | Compact JWE. | -| `getKey` | [`CompactDecryptGetKey`](../interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `getKey` | [`CompactDecryptGetKey`](../interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns diff --git a/docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md b/docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md index e3df48fda6..2488f806e1 100644 --- a/docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md +++ b/docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md @@ -1,6 +1,10 @@ # Function: flattenedDecrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **flattenedDecrypt**(`jwe`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`FlattenedDecryptResult`](../interfaces/types.FlattenedDecryptResult.md)\> @@ -33,7 +37,7 @@ console.log(decoder.decode(additionalAuthenticatedData)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | [`FlattenedJWE`](../interfaces/types.FlattenedJWE.md) | Flattened JWE. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns @@ -53,7 +57,7 @@ console.log(decoder.decode(additionalAuthenticatedData)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | [`FlattenedJWE`](../interfaces/types.FlattenedJWE.md) | Flattened JWE. | -| `getKey` | [`FlattenedDecryptGetKey`](../interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `getKey` | [`FlattenedDecryptGetKey`](../interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns diff --git a/docs/functions/jwe_general_decrypt.generalDecrypt.md b/docs/functions/jwe_general_decrypt.generalDecrypt.md index e110c302e2..2d3727be2b 100644 --- a/docs/functions/jwe_general_decrypt.generalDecrypt.md +++ b/docs/functions/jwe_general_decrypt.generalDecrypt.md @@ -1,6 +1,10 @@ # Function: generalDecrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **generalDecrypt**(`jwe`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`GeneralDecryptResult`](../interfaces/types.GeneralDecryptResult.md)\> @@ -37,7 +41,7 @@ console.log(decoder.decode(additionalAuthenticatedData)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | [`GeneralJWE`](../interfaces/types.GeneralJWE.md) | General JWE. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns @@ -57,7 +61,7 @@ console.log(decoder.decode(additionalAuthenticatedData)) | Name | Type | Description | | :------ | :------ | :------ | | `jwe` | [`GeneralJWE`](../interfaces/types.GeneralJWE.md) | General JWE. | -| `getKey` | [`GeneralDecryptGetKey`](../interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `getKey` | [`GeneralDecryptGetKey`](../interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../interfaces/types.DecryptOptions.md) | JWE Decryption options. | #### Returns diff --git a/docs/functions/jwk_embedded.EmbeddedJWK.md b/docs/functions/jwk_embedded.EmbeddedJWK.md index 65608b7a5a..b63d1d301a 100644 --- a/docs/functions/jwk_embedded.EmbeddedJWK.md +++ b/docs/functions/jwk_embedded.EmbeddedJWK.md @@ -1,6 +1,10 @@ # Function: EmbeddedJWK -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **EmbeddedJWK**<`T`\>(`protectedHeader?`, `token?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> diff --git a/docs/functions/jwk_thumbprint.calculateJwkThumbprint.md b/docs/functions/jwk_thumbprint.calculateJwkThumbprint.md index fa47b1b4ec..dc1acaa4ad 100644 --- a/docs/functions/jwk_thumbprint.calculateJwkThumbprint.md +++ b/docs/functions/jwk_thumbprint.calculateJwkThumbprint.md @@ -1,6 +1,10 @@ # Function: calculateJwkThumbprint -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **calculateJwkThumbprint**(`jwk`, `digestAlgorithm?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`string`\> diff --git a/docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md b/docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md index fe180ba828..0aa7e5dc5a 100644 --- a/docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md +++ b/docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md @@ -1,6 +1,10 @@ # Function: calculateJwkThumbprintUri -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **calculateJwkThumbprintUri**(`jwk`, `digestAlgorithm?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`string`\> @@ -16,7 +20,7 @@ const thumbprintUri = await jose.calculateJwkThumbprintUri({ y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww', }) -console.log(thumbprint) +console.log(thumbprintUri) // 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8' ``` diff --git a/docs/functions/jwks_local.createLocalJWKSet.md b/docs/functions/jwks_local.createLocalJWKSet.md index f65d1138ef..8a13ec22cf 100644 --- a/docs/functions/jwks_local.createLocalJWKSet.md +++ b/docs/functions/jwks_local.createLocalJWKSet.md @@ -1,6 +1,10 @@ # Function: createLocalJWKSet -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **createLocalJWKSet**<`T`\>(`jwks`): (`protectedHeader?`: [`JWSHeaderParameters`](../interfaces/types.JWSHeaderParameters.md), `token?`: [`FlattenedJWSInput`](../interfaces/types.FlattenedJWSInput.md)) => [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> diff --git a/docs/functions/jwks_remote.createRemoteJWKSet.md b/docs/functions/jwks_remote.createRemoteJWKSet.md index 41a2f66ad5..fd6796cc12 100644 --- a/docs/functions/jwks_remote.createRemoteJWKSet.md +++ b/docs/functions/jwks_remote.createRemoteJWKSet.md @@ -1,6 +1,10 @@ # Function: createRemoteJWKSet -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **createRemoteJWKSet**<`T`\>(`url`, `options?`): (`protectedHeader?`: [`JWSHeaderParameters`](../interfaces/types.JWSHeaderParameters.md), `token?`: [`FlattenedJWSInput`](../interfaces/types.FlattenedJWSInput.md)) => [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> diff --git a/docs/functions/jws_compact_verify.compactVerify.md b/docs/functions/jws_compact_verify.compactVerify.md index 5c128a9af5..0fcc16c204 100644 --- a/docs/functions/jws_compact_verify.compactVerify.md +++ b/docs/functions/jws_compact_verify.compactVerify.md @@ -1,6 +1,10 @@ # Function: compactVerify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **compactVerify**(`jws`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`CompactVerifyResult`](../interfaces/types.CompactVerifyResult.md)\> @@ -23,7 +27,7 @@ console.log(new TextDecoder().decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | Compact JWS. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns @@ -43,7 +47,7 @@ console.log(new TextDecoder().decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | Compact JWS. | -| `getKey` | [`CompactVerifyGetKey`](../interfaces/jws_compact_verify.CompactVerifyGetKey.md) | Function resolving a key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `getKey` | [`CompactVerifyGetKey`](../interfaces/jws_compact_verify.CompactVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns diff --git a/docs/functions/jws_flattened_verify.flattenedVerify.md b/docs/functions/jws_flattened_verify.flattenedVerify.md index 57356ef9d9..3533118849 100644 --- a/docs/functions/jws_flattened_verify.flattenedVerify.md +++ b/docs/functions/jws_flattened_verify.flattenedVerify.md @@ -1,6 +1,10 @@ # Function: flattenedVerify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **flattenedVerify**(`jws`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`FlattenedVerifyResult`](../interfaces/types.FlattenedVerifyResult.md)\> @@ -28,7 +32,7 @@ console.log(decoder.decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | [`FlattenedJWSInput`](../interfaces/types.FlattenedJWSInput.md) | Flattened JWS. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns @@ -48,7 +52,7 @@ console.log(decoder.decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | [`FlattenedJWSInput`](../interfaces/types.FlattenedJWSInput.md) | Flattened JWS. | -| `getKey` | [`FlattenedVerifyGetKey`](../interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md) | Function resolving a key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `getKey` | [`FlattenedVerifyGetKey`](../interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns diff --git a/docs/functions/jws_general_verify.generalVerify.md b/docs/functions/jws_general_verify.generalVerify.md index 125383ba73..141acb8f41 100644 --- a/docs/functions/jws_general_verify.generalVerify.md +++ b/docs/functions/jws_general_verify.generalVerify.md @@ -1,6 +1,10 @@ # Function: generalVerify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **generalVerify**(`jws`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`GeneralVerifyResult`](../interfaces/types.GeneralVerifyResult.md)\> @@ -31,7 +35,7 @@ console.log(new TextDecoder().decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | [`GeneralJWSInput`](../interfaces/types.GeneralJWSInput.md) | General JWS. | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns @@ -51,7 +55,7 @@ console.log(new TextDecoder().decode(payload)) | Name | Type | Description | | :------ | :------ | :------ | | `jws` | [`GeneralJWSInput`](../interfaces/types.GeneralJWSInput.md) | General JWS. | -| `getKey` | [`GeneralVerifyGetKey`](../interfaces/jws_general_verify.GeneralVerifyGetKey.md) | Function resolving a key to verify the JWS with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `getKey` | [`GeneralVerifyGetKey`](../interfaces/jws_general_verify.GeneralVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../interfaces/types.VerifyOptions.md) | JWS Verify options. | #### Returns diff --git a/docs/functions/jwt_decrypt.jwtDecrypt.md b/docs/functions/jwt_decrypt.jwtDecrypt.md index 8155a6cf11..88ed20935e 100644 --- a/docs/functions/jwt_decrypt.jwtDecrypt.md +++ b/docs/functions/jwt_decrypt.jwtDecrypt.md @@ -1,6 +1,10 @@ # Function: jwtDecrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **jwtDecrypt**(`jwt`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`JWTDecryptResult`](../interfaces/types.JWTDecryptResult.md)\> @@ -28,7 +32,7 @@ console.log(payload) | Name | Type | Description | | :------ | :------ | :------ | | `jwt` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | JSON Web Token value (encoded as JWE). | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt and verify the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Private Key or Secret to decrypt and verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`JWTDecryptOptions`](../interfaces/jwt_decrypt.JWTDecryptOptions.md) | JWT Decryption and JWT Claims Set validation options. | #### Returns @@ -48,7 +52,7 @@ console.log(payload) | Name | Type | Description | | :------ | :------ | :------ | | `jwt` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | JSON Web Token value (encoded as JWE). | -| `getKey` | [`JWTDecryptGetKey`](../interfaces/jwt_decrypt.JWTDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt and verify the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | +| `getKey` | [`JWTDecryptGetKey`](../interfaces/jwt_decrypt.JWTDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt and verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`JWTDecryptOptions`](../interfaces/jwt_decrypt.JWTDecryptOptions.md) | JWT Decryption and JWT Claims Set validation options. | #### Returns diff --git a/docs/functions/jwt_verify.jwtVerify.md b/docs/functions/jwt_verify.jwtVerify.md index aa2622857e..7b28f30153 100644 --- a/docs/functions/jwt_verify.jwtVerify.md +++ b/docs/functions/jwt_verify.jwtVerify.md @@ -1,6 +1,10 @@ # Function: jwtVerify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **jwtVerify**(`jwt`, `key`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`JWTVerifyResult`](../interfaces/types.JWTVerifyResult.md)\> @@ -78,7 +82,7 @@ console.log(payload) | Name | Type | Description | | :------ | :------ | :------ | | `jwt` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | JSON Web Token value (encoded as JWS). | -| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `key` | [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) \| [`KeyLike`](../types/types.KeyLike.md) | Key to verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`JWTVerifyOptions`](../interfaces/jwt_verify.JWTVerifyOptions.md) | JWT Decryption and JWT Claims Set validation options. | #### Returns @@ -111,7 +115,7 @@ console.log(payload) | Name | Type | Description | | :------ | :------ | :------ | | `jwt` | `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) | JSON Web Token value (encoded as JWS). | -| `getKey` | [`JWTVerifyGetKey`](../interfaces/jwt_verify.JWTVerifyGetKey.md) | Function resolving a key to verify the JWT with. See also [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | +| `getKey` | [`JWTVerifyGetKey`](../interfaces/jwt_verify.JWTVerifyGetKey.md) | Function resolving a key to verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`JWTVerifyOptions`](../interfaces/jwt_verify.JWTVerifyOptions.md) | JWT Decryption and JWT Claims Set validation options. | #### Returns diff --git a/docs/functions/key_export.exportJWK.md b/docs/functions/key_export.exportJWK.md index 7c20aa910e..d6ff91e212 100644 --- a/docs/functions/key_export.exportJWK.md +++ b/docs/functions/key_export.exportJWK.md @@ -1,6 +1,10 @@ # Function: exportJWK -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **exportJWK**(`key`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`JWK`](../interfaces/types.JWK.md)\> diff --git a/docs/functions/key_export.exportPKCS8.md b/docs/functions/key_export.exportPKCS8.md index 86a32ccf28..4c1877358b 100644 --- a/docs/functions/key_export.exportPKCS8.md +++ b/docs/functions/key_export.exportPKCS8.md @@ -1,6 +1,10 @@ # Function: exportPKCS8 -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **exportPKCS8**(`key`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`string`\> diff --git a/docs/functions/key_export.exportSPKI.md b/docs/functions/key_export.exportSPKI.md index 404b57135e..cb7a1a8ac3 100644 --- a/docs/functions/key_export.exportSPKI.md +++ b/docs/functions/key_export.exportSPKI.md @@ -1,6 +1,10 @@ # Function: exportSPKI -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **exportSPKI**(`key`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`string`\> diff --git a/docs/functions/key_generate_key_pair.generateKeyPair.md b/docs/functions/key_generate_key_pair.generateKeyPair.md index 90afde0bc7..0ff3f3b9cd 100644 --- a/docs/functions/key_generate_key_pair.generateKeyPair.md +++ b/docs/functions/key_generate_key_pair.generateKeyPair.md @@ -1,6 +1,10 @@ # Function: generateKeyPair -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **generateKeyPair**<`T`\>(`alg`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<[`GenerateKeyPairResult`](../interfaces/key_generate_key_pair.GenerateKeyPairResult.md)<`T`\>\> diff --git a/docs/functions/key_generate_secret.generateSecret.md b/docs/functions/key_generate_secret.generateSecret.md index 4d30935027..2823bb8b86 100644 --- a/docs/functions/key_generate_secret.generateSecret.md +++ b/docs/functions/key_generate_secret.generateSecret.md @@ -1,6 +1,10 @@ # Function: generateSecret -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **generateSecret**<`T`\>(`alg`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array )\> diff --git a/docs/functions/key_import.importJWK.md b/docs/functions/key_import.importJWK.md index 6d55cf3413..d8e515f035 100644 --- a/docs/functions/key_import.importJWK.md +++ b/docs/functions/key_import.importJWK.md @@ -1,15 +1,17 @@ # Function: importJWK -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **importJWK**<`T`\>(`jwk`, `alg?`, `octAsKeyObject?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array )\> Imports a JWK to a runtime-specific key representation (KeyLike). Either JWK "alg" (Algorithm) -Parameter must be present or the optional "alg" argument. When running on a runtime using [Web -Cryptography API](https://www.w3.org/TR/WebCryptoAPI/) the jwk parameters "use", "key_ops", and -"ext" are also used in the resulting `CryptoKey`. See [Algorithm Key -Requirements](https://github.com/panva/jose/issues/210) to learn about key to algorithm -requirements and mapping. +Parameter must be present or the optional "alg" argument. When running on a runtime using +[Web Cryptography API](https://www.w3.org/TR/WebCryptoAPI/) the jwk parameters "use", +"key_ops", and "ext" are also used in the resulting `CryptoKey`. **`example`** Usage @@ -45,7 +47,7 @@ const rsaPublicKey = await jose.importJWK( | Name | Type | Description | | :------ | :------ | :------ | | `jwk` | [`JWK`](../interfaces/types.JWK.md) | JSON Web Key. | -| `alg?` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key. Default is the "alg" property on the JWK, its presence is only enforced in Web Crypto API runtimes. | +| `alg?` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key. Default is the "alg" property on the JWK, its presence is only enforced in Web Crypto API runtimes. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `octAsKeyObject?` | `boolean` | Forces a symmetric key to be imported to a KeyObject or CryptoKey. Default is true unless JWK "ext" (Extractable) is true. | #### Returns diff --git a/docs/functions/key_import.importPKCS8.md b/docs/functions/key_import.importPKCS8.md index 8688044c2f..f9e27338ca 100644 --- a/docs/functions/key_import.importPKCS8.md +++ b/docs/functions/key_import.importPKCS8.md @@ -1,12 +1,15 @@ # Function: importPKCS8 -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **importPKCS8**<`T`\>(`pkcs8`, `alg`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> Imports a PEM-encoded PKCS#8 string as a runtime-specific private key representation (KeyObject -or CryptoKey). See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) to -learn about key to algorithm requirements and mapping. Encrypted keys are not supported. +or CryptoKey). **`example`** Usage @@ -31,7 +34,7 @@ const ecPrivateKey = await jose.importPKCS8(pkcs8, algorithm) | Name | Type | Description | | :------ | :------ | :------ | | `pkcs8` | `string` | - | -| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. | +| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`PEMImportOptions`](../interfaces/key_import.PEMImportOptions.md) | - | #### Returns diff --git a/docs/functions/key_import.importSPKI.md b/docs/functions/key_import.importSPKI.md index 0816221350..4d6a39c033 100644 --- a/docs/functions/key_import.importSPKI.md +++ b/docs/functions/key_import.importSPKI.md @@ -1,12 +1,15 @@ # Function: importSPKI -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **importSPKI**<`T`\>(`spki`, `alg`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> Imports a PEM-encoded SPKI string as a runtime-specific public key representation (KeyObject or -CryptoKey). See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) to learn -about key to algorithm requirements and mapping. +CryptoKey). **`example`** Usage @@ -30,7 +33,7 @@ const ecPublicKey = await jose.importSPKI(spki, algorithm) | Name | Type | Description | | :------ | :------ | :------ | | `spki` | `string` | - | -| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. | +| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`PEMImportOptions`](../interfaces/key_import.PEMImportOptions.md) | - | #### Returns diff --git a/docs/functions/key_import.importX509.md b/docs/functions/key_import.importX509.md index 1f766b1244..cb514b9ac4 100644 --- a/docs/functions/key_import.importX509.md +++ b/docs/functions/key_import.importX509.md @@ -1,13 +1,15 @@ # Function: importX509 -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **importX509**<`T`\>(`x509`, `alg`, `options?`): [`Promise`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise )<`T`\> Imports the SPKI from an X.509 string certificate as a runtime-specific public key representation -(KeyObject or CryptoKey). See [Algorithm Key -Requirements](https://github.com/panva/jose/issues/210) to learn about key to algorithm -requirements and mapping. +(KeyObject or CryptoKey). **`example`** Usage @@ -37,7 +39,7 @@ const ecPublicKey = await jose.importX509(x509, algorithm) | Name | Type | Description | | :------ | :------ | :------ | | `x509` | `string` | - | -| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. | +| `alg` | `string` | (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used with the imported key, its presence is only enforced in Web Crypto API runtimes. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`PEMImportOptions`](../interfaces/key_import.PEMImportOptions.md) | - | #### Returns diff --git a/docs/functions/util_base64url.decode.md b/docs/functions/util_base64url.decode.md index 0593e09adf..9227226714 100644 --- a/docs/functions/util_base64url.decode.md +++ b/docs/functions/util_base64url.decode.md @@ -1,6 +1,10 @@ # Function: decode -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **decode**(`input`): [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) diff --git a/docs/functions/util_base64url.encode.md b/docs/functions/util_base64url.encode.md index da37a053a4..153e66faef 100644 --- a/docs/functions/util_base64url.encode.md +++ b/docs/functions/util_base64url.encode.md @@ -1,6 +1,10 @@ # Function: encode -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **encode**(`input`): `string` diff --git a/docs/functions/util_decode_jwt.decodeJwt.md b/docs/functions/util_decode_jwt.decodeJwt.md index a22a50009b..3269b6170e 100644 --- a/docs/functions/util_decode_jwt.decodeJwt.md +++ b/docs/functions/util_decode_jwt.decodeJwt.md @@ -1,6 +1,10 @@ # Function: decodeJwt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **decodeJwt**(`jwt`): [`JWTPayload`](../interfaces/types.JWTPayload.md) diff --git a/docs/functions/util_decode_protected_header.decodeProtectedHeader.md b/docs/functions/util_decode_protected_header.decodeProtectedHeader.md index e4487f720b..208ebbec50 100644 --- a/docs/functions/util_decode_protected_header.decodeProtectedHeader.md +++ b/docs/functions/util_decode_protected_header.decodeProtectedHeader.md @@ -1,6 +1,10 @@ # Function: decodeProtectedHeader -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ▸ **decodeProtectedHeader**(`token`): [`ProtectedHeaderParameters`](../types/util_decode_protected_header.ProtectedHeaderParameters.md) diff --git a/docs/interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md b/docs/interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md index 4e3f87779f..af511ff149 100644 --- a/docs/interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md +++ b/docs/interfaces/jwe_compact_decrypt.CompactDecryptGetKey.md @@ -1,6 +1,10 @@ # Interface: CompactDecryptGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md b/docs/interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md index 9fefc0b63f..a36397b239 100644 --- a/docs/interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md +++ b/docs/interfaces/jwe_flattened_decrypt.FlattenedDecryptGetKey.md @@ -1,6 +1,10 @@ # Interface: FlattenedDecryptGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md b/docs/interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md index 5c1f6e8cb9..5002259d0c 100644 --- a/docs/interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md +++ b/docs/interfaces/jwe_general_decrypt.GeneralDecryptGetKey.md @@ -1,6 +1,10 @@ # Interface: GeneralDecryptGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/jwe_general_encrypt.Recipient.md b/docs/interfaces/jwe_general_encrypt.Recipient.md index 186250afcb..32d2a61163 100644 --- a/docs/interfaces/jwe_general_encrypt.Recipient.md +++ b/docs/interfaces/jwe_general_encrypt.Recipient.md @@ -1,6 +1,10 @@ # Interface: Recipient -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/jwks_remote.RemoteJWKSetOptions.md b/docs/interfaces/jwks_remote.RemoteJWKSetOptions.md index 0e810ad2b0..83b427c081 100644 --- a/docs/interfaces/jwks_remote.RemoteJWKSetOptions.md +++ b/docs/interfaces/jwks_remote.RemoteJWKSetOptions.md @@ -1,6 +1,10 @@ # Interface: RemoteJWKSetOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Options for the remote JSON Web Key Set. @@ -23,8 +27,8 @@ Options for the remote JSON Web Key Set. An instance of [http.Agent](https://nodejs.org/api/http.html#class-httpagent) or [https.Agent](https://nodejs.org/api/https.html#class-httpsagent) to pass to the [http.get](https://nodejs.org/api/http.html#httpgetoptions-callback) or -[https.get](https://nodejs.org/api/https.html#httpsgetoptions-callback) method's options. Use -when behind an http(s) proxy. This is a Node.js runtime specific option, it is ignored when +[https.get](https://nodejs.org/api/https.html#httpsgetoptions-callback) method's options. +Use when behind an http(s) proxy. This is a Node.js runtime specific option, it is ignored when used outside of Node.js runtime. ___ diff --git a/docs/interfaces/jws_compact_verify.CompactVerifyGetKey.md b/docs/interfaces/jws_compact_verify.CompactVerifyGetKey.md index a1a6ba9ced..14a5fb5842 100644 --- a/docs/interfaces/jws_compact_verify.CompactVerifyGetKey.md +++ b/docs/interfaces/jws_compact_verify.CompactVerifyGetKey.md @@ -1,6 +1,10 @@ # Interface: CompactVerifyGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable @@ -11,9 +15,7 @@ Interface for Compact JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. -See -[createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) -to verify using a remote JSON Web Key Set. +**`see`** [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. #### Parameters diff --git a/docs/interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md b/docs/interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md index 412b54e822..363ad88cb0 100644 --- a/docs/interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md +++ b/docs/interfaces/jws_flattened_verify.FlattenedVerifyGetKey.md @@ -1,6 +1,10 @@ # Interface: FlattenedVerifyGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable @@ -11,9 +15,7 @@ Interface for Flattened JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. -See -[createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) -to verify using a remote JSON Web Key Set. +**`see`** [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. #### Parameters diff --git a/docs/interfaces/jws_general_sign.Signature.md b/docs/interfaces/jws_general_sign.Signature.md index 3632a9b6aa..1a7cb415bc 100644 --- a/docs/interfaces/jws_general_sign.Signature.md +++ b/docs/interfaces/jws_general_sign.Signature.md @@ -1,6 +1,10 @@ # Interface: Signature -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/jws_general_verify.GeneralVerifyGetKey.md b/docs/interfaces/jws_general_verify.GeneralVerifyGetKey.md index 8c0e82e131..fa89192e5e 100644 --- a/docs/interfaces/jws_general_verify.GeneralVerifyGetKey.md +++ b/docs/interfaces/jws_general_verify.GeneralVerifyGetKey.md @@ -1,6 +1,10 @@ # Interface: GeneralVerifyGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable @@ -11,9 +15,7 @@ Interface for General JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. -See -[createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) -to verify using a remote JSON Web Key Set. +**`see`** [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. #### Parameters diff --git a/docs/interfaces/jwt_decrypt.JWTDecryptGetKey.md b/docs/interfaces/jwt_decrypt.JWTDecryptGetKey.md index d10bad6afa..3e16eb7f9e 100644 --- a/docs/interfaces/jwt_decrypt.JWTDecryptGetKey.md +++ b/docs/interfaces/jwt_decrypt.JWTDecryptGetKey.md @@ -1,6 +1,10 @@ # Interface: JWTDecryptGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/jwt_decrypt.JWTDecryptOptions.md b/docs/interfaces/jwt_decrypt.JWTDecryptOptions.md index 93f4837743..9751b627bd 100644 --- a/docs/interfaces/jwt_decrypt.JWTDecryptOptions.md +++ b/docs/interfaces/jwt_decrypt.JWTDecryptOptions.md @@ -1,6 +1,10 @@ # Interface: JWTDecryptOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Combination of JWE Decryption options and JWT Claims Set verification options. diff --git a/docs/interfaces/jwt_unsecured.UnsecuredResult.md b/docs/interfaces/jwt_unsecured.UnsecuredResult.md index 8c18b5fa44..8a309cb45b 100644 --- a/docs/interfaces/jwt_unsecured.UnsecuredResult.md +++ b/docs/interfaces/jwt_unsecured.UnsecuredResult.md @@ -1,6 +1,10 @@ # Interface: UnsecuredResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/jwt_verify.JWTVerifyGetKey.md b/docs/interfaces/jwt_verify.JWTVerifyGetKey.md index c3b6f229a0..b8d16ce49d 100644 --- a/docs/interfaces/jwt_verify.JWTVerifyGetKey.md +++ b/docs/interfaces/jwt_verify.JWTVerifyGetKey.md @@ -1,6 +1,10 @@ # Interface: JWTVerifyGetKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable @@ -11,9 +15,7 @@ Interface for JWT Verification dynamic key resolution. No token components have been verified at the time of this function call. -See -[createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) -to verify using a remote JSON Web Key Set. +**`see`** [createRemoteJWKSet](../functions/jwks_remote.createRemoteJWKSet.md#function-createremotejwkset) to verify using a remote JSON Web Key Set. #### Parameters diff --git a/docs/interfaces/jwt_verify.JWTVerifyOptions.md b/docs/interfaces/jwt_verify.JWTVerifyOptions.md index fa6743841c..54ac34f23f 100644 --- a/docs/interfaces/jwt_verify.JWTVerifyOptions.md +++ b/docs/interfaces/jwt_verify.JWTVerifyOptions.md @@ -1,6 +1,10 @@ # Interface: JWTVerifyOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Combination of JWS Verification options and JWT Claims Set verification options. diff --git a/docs/interfaces/key_generate_key_pair.GenerateKeyPairOptions.md b/docs/interfaces/key_generate_key_pair.GenerateKeyPairOptions.md index f0b2ade6f3..1fd14d5529 100644 --- a/docs/interfaces/key_generate_key_pair.GenerateKeyPairOptions.md +++ b/docs/interfaces/key_generate_key_pair.GenerateKeyPairOptions.md @@ -1,6 +1,10 @@ # Interface: GenerateKeyPairOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/key_generate_key_pair.GenerateKeyPairResult.md b/docs/interfaces/key_generate_key_pair.GenerateKeyPairResult.md index ce0e9e98e9..c695316b53 100644 --- a/docs/interfaces/key_generate_key_pair.GenerateKeyPairResult.md +++ b/docs/interfaces/key_generate_key_pair.GenerateKeyPairResult.md @@ -1,6 +1,10 @@ # Interface: GenerateKeyPairResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Type parameters diff --git a/docs/interfaces/key_generate_secret.GenerateSecretOptions.md b/docs/interfaces/key_generate_secret.GenerateSecretOptions.md index f46bdd2698..d9f23f58ae 100644 --- a/docs/interfaces/key_generate_secret.GenerateSecretOptions.md +++ b/docs/interfaces/key_generate_secret.GenerateSecretOptions.md @@ -1,6 +1,10 @@ # Interface: GenerateSecretOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/key_import.PEMImportOptions.md b/docs/interfaces/key_import.PEMImportOptions.md index 798a751804..de5faa32bb 100644 --- a/docs/interfaces/key_import.PEMImportOptions.md +++ b/docs/interfaces/key_import.PEMImportOptions.md @@ -1,6 +1,10 @@ # Interface: PEMImportOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.CompactDecryptResult.md b/docs/interfaces/types.CompactDecryptResult.md index 615a4a4e3c..1307372244 100644 --- a/docs/interfaces/types.CompactDecryptResult.md +++ b/docs/interfaces/types.CompactDecryptResult.md @@ -1,6 +1,10 @@ # Interface: CompactDecryptResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.CompactJWEHeaderParameters.md b/docs/interfaces/types.CompactJWEHeaderParameters.md index a40bf51b4b..a7d62ec1b8 100644 --- a/docs/interfaces/types.CompactJWEHeaderParameters.md +++ b/docs/interfaces/types.CompactJWEHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: CompactJWEHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized Compact JWE Header Parameters, any other Header Members may also be present. diff --git a/docs/interfaces/types.CompactJWSHeaderParameters.md b/docs/interfaces/types.CompactJWSHeaderParameters.md index 48bc749de7..a00457b9f4 100644 --- a/docs/interfaces/types.CompactJWSHeaderParameters.md +++ b/docs/interfaces/types.CompactJWSHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: CompactJWSHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized Compact JWS Header Parameters, any other Header Members may also be present. diff --git a/docs/interfaces/types.CompactVerifyResult.md b/docs/interfaces/types.CompactVerifyResult.md index e57b8aa19a..e70b4fdbac 100644 --- a/docs/interfaces/types.CompactVerifyResult.md +++ b/docs/interfaces/types.CompactVerifyResult.md @@ -1,6 +1,10 @@ # Interface: CompactVerifyResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.CritOption.md b/docs/interfaces/types.CritOption.md index 0f4fc97b0b..71d164d1d7 100644 --- a/docs/interfaces/types.CritOption.md +++ b/docs/interfaces/types.CritOption.md @@ -1,6 +1,10 @@ # Interface: CritOption -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. diff --git a/docs/interfaces/types.DecryptOptions.md b/docs/interfaces/types.DecryptOptions.md index 2abcc86ebf..e307c906ad 100644 --- a/docs/interfaces/types.DecryptOptions.md +++ b/docs/interfaces/types.DecryptOptions.md @@ -1,6 +1,10 @@ # Interface: DecryptOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWE Decryption options. diff --git a/docs/interfaces/types.DeflateFunction.md b/docs/interfaces/types.DeflateFunction.md index 08200f69dc..cb9680f464 100644 --- a/docs/interfaces/types.DeflateFunction.md +++ b/docs/interfaces/types.DeflateFunction.md @@ -1,6 +1,10 @@ # Interface: DeflateFunction -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/types.DeflateOption.md b/docs/interfaces/types.DeflateOption.md index ce5a69f7c4..106d49e4ae 100644 --- a/docs/interfaces/types.DeflateOption.md +++ b/docs/interfaces/types.DeflateOption.md @@ -1,6 +1,10 @@ # Interface: DeflateOption -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWE Deflate option. diff --git a/docs/interfaces/types.EncryptOptions.md b/docs/interfaces/types.EncryptOptions.md index eafb9be8f7..d7688bad09 100644 --- a/docs/interfaces/types.EncryptOptions.md +++ b/docs/interfaces/types.EncryptOptions.md @@ -1,6 +1,10 @@ # Interface: EncryptOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWE Encryption options. diff --git a/docs/interfaces/types.FlattenedDecryptResult.md b/docs/interfaces/types.FlattenedDecryptResult.md index 95da8007dd..6b7a098040 100644 --- a/docs/interfaces/types.FlattenedDecryptResult.md +++ b/docs/interfaces/types.FlattenedDecryptResult.md @@ -1,6 +1,10 @@ # Interface: FlattenedDecryptResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.FlattenedJWE.md b/docs/interfaces/types.FlattenedJWE.md index 870fb6e8a2..8d257c97bc 100644 --- a/docs/interfaces/types.FlattenedJWE.md +++ b/docs/interfaces/types.FlattenedJWE.md @@ -1,6 +1,10 @@ # Interface: FlattenedJWE -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Flattened JWE definition. diff --git a/docs/interfaces/types.FlattenedJWS.md b/docs/interfaces/types.FlattenedJWS.md index 618d5b696a..76cbe99a35 100644 --- a/docs/interfaces/types.FlattenedJWS.md +++ b/docs/interfaces/types.FlattenedJWS.md @@ -1,9 +1,13 @@ # Interface: FlattenedJWS -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Flattened JWS definition. Payload is returned as an empty string when JWS Unencoded Payload -Option [RFC7797](https://www.rfc-editor.org/rfc/rfc7797) is used. +([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) is used. ## Table of contents diff --git a/docs/interfaces/types.FlattenedJWSInput.md b/docs/interfaces/types.FlattenedJWSInput.md index 149d6d4cbc..fc288154f6 100644 --- a/docs/interfaces/types.FlattenedJWSInput.md +++ b/docs/interfaces/types.FlattenedJWSInput.md @@ -1,6 +1,10 @@ # Interface: FlattenedJWSInput -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Flattened JWS definition for verify function inputs, allows payload as Uint8Array for detached signature validation. diff --git a/docs/interfaces/types.FlattenedVerifyResult.md b/docs/interfaces/types.FlattenedVerifyResult.md index 57dbb4d8ce..e0ba37b7ac 100644 --- a/docs/interfaces/types.FlattenedVerifyResult.md +++ b/docs/interfaces/types.FlattenedVerifyResult.md @@ -1,6 +1,10 @@ # Interface: FlattenedVerifyResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.GeneralDecryptResult.md b/docs/interfaces/types.GeneralDecryptResult.md index 02cd07e7c7..2b9ff49211 100644 --- a/docs/interfaces/types.GeneralDecryptResult.md +++ b/docs/interfaces/types.GeneralDecryptResult.md @@ -1,6 +1,10 @@ # Interface: GeneralDecryptResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.GeneralJWE.md b/docs/interfaces/types.GeneralJWE.md index cb2a9c150d..58453a5794 100644 --- a/docs/interfaces/types.GeneralJWE.md +++ b/docs/interfaces/types.GeneralJWE.md @@ -1,6 +1,10 @@ # Interface: GeneralJWE -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.GeneralJWS.md b/docs/interfaces/types.GeneralJWS.md index 10cf551256..530f365ea5 100644 --- a/docs/interfaces/types.GeneralJWS.md +++ b/docs/interfaces/types.GeneralJWS.md @@ -1,9 +1,13 @@ # Interface: GeneralJWS -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -General JWS definition. Payload is returned as an empty string when JWS Unencoded Payload Option -[RFC7797](https://www.rfc-editor.org/rfc/rfc7797) is used. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +General JWS definition. Payload is returned as an empty string when JWS Unencoded Payload +([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) is used. ## Table of contents diff --git a/docs/interfaces/types.GeneralJWSInput.md b/docs/interfaces/types.GeneralJWSInput.md index ff1d8ba98d..21e8443102 100644 --- a/docs/interfaces/types.GeneralJWSInput.md +++ b/docs/interfaces/types.GeneralJWSInput.md @@ -1,6 +1,10 @@ # Interface: GeneralJWSInput -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- General JWS definition for verify function inputs, allows payload as Uint8Array for detached signature validation. @@ -18,8 +22,9 @@ signature validation. • **payload**: `string` \| [`Uint8Array`]( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array ) -The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 -"b64": false is used the value passed may also be a Uint8Array. +The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when +JWS Unencoded Payload ([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) "b64": false is +used the value passed may also be a Uint8Array. ___ diff --git a/docs/interfaces/types.GeneralVerifyResult.md b/docs/interfaces/types.GeneralVerifyResult.md index 74017c977a..18bf5bb359 100644 --- a/docs/interfaces/types.GeneralVerifyResult.md +++ b/docs/interfaces/types.GeneralVerifyResult.md @@ -1,6 +1,10 @@ # Interface: GeneralVerifyResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.GetKeyFunction.md b/docs/interfaces/types.GetKeyFunction.md index 251336b5b3..5b995cd2c4 100644 --- a/docs/interfaces/types.GetKeyFunction.md +++ b/docs/interfaces/types.GetKeyFunction.md @@ -1,6 +1,10 @@ # Interface: GetKeyFunction -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Type parameters diff --git a/docs/interfaces/types.InflateFunction.md b/docs/interfaces/types.InflateFunction.md index cae62c979d..6440030a1b 100644 --- a/docs/interfaces/types.InflateFunction.md +++ b/docs/interfaces/types.InflateFunction.md @@ -1,6 +1,10 @@ # Interface: InflateFunction -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Callable diff --git a/docs/interfaces/types.JSONWebKeySet.md b/docs/interfaces/types.JSONWebKeySet.md index aa57570a5a..73cbd1b319 100644 --- a/docs/interfaces/types.JSONWebKeySet.md +++ b/docs/interfaces/types.JSONWebKeySet.md @@ -1,6 +1,10 @@ # Interface: JSONWebKeySet -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JSON Web Key Set diff --git a/docs/interfaces/types.JWEHeaderParameters.md b/docs/interfaces/types.JWEHeaderParameters.md index 9a97fefe20..c9ce66297a 100644 --- a/docs/interfaces/types.JWEHeaderParameters.md +++ b/docs/interfaces/types.JWEHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: JWEHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized JWE Header Parameters, any other Header members may also be present. diff --git a/docs/interfaces/types.JWEKeyManagementHeaderParameters.md b/docs/interfaces/types.JWEKeyManagementHeaderParameters.md index 6f1dc13ec7..f65cc8931f 100644 --- a/docs/interfaces/types.JWEKeyManagementHeaderParameters.md +++ b/docs/interfaces/types.JWEKeyManagementHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: JWEKeyManagementHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized JWE Key Management-related Header Parameters. diff --git a/docs/interfaces/types.JWK.md b/docs/interfaces/types.JWK.md index 5ec35a03c9..d1ee9ab69c 100644 --- a/docs/interfaces/types.JWK.md +++ b/docs/interfaces/types.JWK.md @@ -1,9 +1,13 @@ # Interface: JWK -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) -JSON Web Key ([JWK](https://www.rfc-editor.org/rfc/rfc7517)). "RSA", "EC", "OKP", and "oct" key -types are supported. +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +JSON Web Key ([JWK](https://www.rfc-editor.org/rfc/rfc7517)). "RSA", "EC", "OKP", and "oct" +key types are supported. ## Table of contents diff --git a/docs/interfaces/types.JWSHeaderParameters.md b/docs/interfaces/types.JWSHeaderParameters.md index b50184aeb3..dea4f325b9 100644 --- a/docs/interfaces/types.JWSHeaderParameters.md +++ b/docs/interfaces/types.JWSHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: JWSHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized JWS Header Parameters, any other Header Members may also be present. diff --git a/docs/interfaces/types.JWTClaimVerificationOptions.md b/docs/interfaces/types.JWTClaimVerificationOptions.md index 7c1bce5391..b5b3f35ad9 100644 --- a/docs/interfaces/types.JWTClaimVerificationOptions.md +++ b/docs/interfaces/types.JWTClaimVerificationOptions.md @@ -1,6 +1,10 @@ # Interface: JWTClaimVerificationOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWT Claims Set verification options. diff --git a/docs/interfaces/types.JWTDecryptResult.md b/docs/interfaces/types.JWTDecryptResult.md index 44cc9901a6..7daf530623 100644 --- a/docs/interfaces/types.JWTDecryptResult.md +++ b/docs/interfaces/types.JWTDecryptResult.md @@ -1,6 +1,10 @@ # Interface: JWTDecryptResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.JWTHeaderParameters.md b/docs/interfaces/types.JWTHeaderParameters.md index b3dab6465f..50e3413c9d 100644 --- a/docs/interfaces/types.JWTHeaderParameters.md +++ b/docs/interfaces/types.JWTHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: JWTHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized Signed JWT Header Parameters, any other Header Members may also be present. diff --git a/docs/interfaces/types.JWTPayload.md b/docs/interfaces/types.JWTPayload.md index bd443454f3..61257f6ced 100644 --- a/docs/interfaces/types.JWTPayload.md +++ b/docs/interfaces/types.JWTPayload.md @@ -1,6 +1,10 @@ # Interface: JWTPayload -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Recognized JWT Claims Set members, any other members may also be present. @@ -22,7 +26,9 @@ Recognized JWT Claims Set members, any other members may also be present. • `Optional` **aud**: `string` \| `string`[] -JWT Audience [RFC7519#section-4.1.3](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3). +JWT Audience + +**`see`** [RFC7519#section-4.1.3](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) ___ diff --git a/docs/interfaces/types.JWTVerifyResult.md b/docs/interfaces/types.JWTVerifyResult.md index e003e4d3dd..9c02efab7a 100644 --- a/docs/interfaces/types.JWTVerifyResult.md +++ b/docs/interfaces/types.JWTVerifyResult.md @@ -1,6 +1,10 @@ # Interface: JWTVerifyResult -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.JoseHeaderParameters.md b/docs/interfaces/types.JoseHeaderParameters.md index d72fb5a422..d542260903 100644 --- a/docs/interfaces/types.JoseHeaderParameters.md +++ b/docs/interfaces/types.JoseHeaderParameters.md @@ -1,6 +1,10 @@ # Interface: JoseHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/interfaces/types.ResolvedKey.md b/docs/interfaces/types.ResolvedKey.md index 440f563190..d7f82c5e9e 100644 --- a/docs/interfaces/types.ResolvedKey.md +++ b/docs/interfaces/types.ResolvedKey.md @@ -1,6 +1,10 @@ # Interface: ResolvedKey -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Type parameters diff --git a/docs/interfaces/types.SignOptions.md b/docs/interfaces/types.SignOptions.md index 82c99b4742..b03944a057 100644 --- a/docs/interfaces/types.SignOptions.md +++ b/docs/interfaces/types.SignOptions.md @@ -1,6 +1,10 @@ # Interface: SignOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWS Signing options. diff --git a/docs/interfaces/types.VerifyOptions.md b/docs/interfaces/types.VerifyOptions.md index 1ca049577e..6cc6fadcaf 100644 --- a/docs/interfaces/types.VerifyOptions.md +++ b/docs/interfaces/types.VerifyOptions.md @@ -1,6 +1,10 @@ # Interface: VerifyOptions -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- JWS Verification options. diff --git a/docs/modules/jwe_compact_decrypt.md b/docs/modules/jwe_compact_decrypt.md index 8e2b0f2634..0023b9c428 100644 --- a/docs/modules/jwe_compact_decrypt.md +++ b/docs/modules/jwe_compact_decrypt.md @@ -1,6 +1,10 @@ # Module: jwe/compact/decrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwe_compact_encrypt.md b/docs/modules/jwe_compact_encrypt.md index 1104e51b33..97f2d7ebe4 100644 --- a/docs/modules/jwe_compact_encrypt.md +++ b/docs/modules/jwe_compact_encrypt.md @@ -1,6 +1,10 @@ # Module: jwe/compact/encrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwe_flattened_decrypt.md b/docs/modules/jwe_flattened_decrypt.md index 8276003f29..3e5c697551 100644 --- a/docs/modules/jwe_flattened_decrypt.md +++ b/docs/modules/jwe_flattened_decrypt.md @@ -1,6 +1,10 @@ # Module: jwe/flattened/decrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwe_flattened_encrypt.md b/docs/modules/jwe_flattened_encrypt.md index f2b9f10596..edd23a9022 100644 --- a/docs/modules/jwe_flattened_encrypt.md +++ b/docs/modules/jwe_flattened_encrypt.md @@ -1,6 +1,10 @@ # Module: jwe/flattened/encrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwe_general_decrypt.md b/docs/modules/jwe_general_decrypt.md index 3f34536065..8e0d0f490a 100644 --- a/docs/modules/jwe_general_decrypt.md +++ b/docs/modules/jwe_general_decrypt.md @@ -1,6 +1,10 @@ # Module: jwe/general/decrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwe_general_encrypt.md b/docs/modules/jwe_general_encrypt.md index 211560e012..9581c03def 100644 --- a/docs/modules/jwe_general_encrypt.md +++ b/docs/modules/jwe_general_encrypt.md @@ -1,6 +1,10 @@ # Module: jwe/general/encrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwk_embedded.md b/docs/modules/jwk_embedded.md index fce2def2c1..44bda97adf 100644 --- a/docs/modules/jwk_embedded.md +++ b/docs/modules/jwk_embedded.md @@ -1,6 +1,10 @@ # Module: jwk/embedded -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwk_thumbprint.md b/docs/modules/jwk_thumbprint.md index 5472add1ed..be604e7b1b 100644 --- a/docs/modules/jwk_thumbprint.md +++ b/docs/modules/jwk_thumbprint.md @@ -1,6 +1,10 @@ # Module: jwk/thumbprint -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwks_local.md b/docs/modules/jwks_local.md index f677c03391..04f37a53d5 100644 --- a/docs/modules/jwks_local.md +++ b/docs/modules/jwks_local.md @@ -1,6 +1,10 @@ # Module: jwks/local -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwks_remote.md b/docs/modules/jwks_remote.md index d17102771e..f2d8b52024 100644 --- a/docs/modules/jwks_remote.md +++ b/docs/modules/jwks_remote.md @@ -1,6 +1,10 @@ # Module: jwks/remote -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_compact_sign.md b/docs/modules/jws_compact_sign.md index 874d594e9a..1b8842b0e4 100644 --- a/docs/modules/jws_compact_sign.md +++ b/docs/modules/jws_compact_sign.md @@ -1,6 +1,10 @@ # Module: jws/compact/sign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_compact_verify.md b/docs/modules/jws_compact_verify.md index 1f2ab791b0..e60b9f83c7 100644 --- a/docs/modules/jws_compact_verify.md +++ b/docs/modules/jws_compact_verify.md @@ -1,6 +1,10 @@ # Module: jws/compact/verify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_flattened_sign.md b/docs/modules/jws_flattened_sign.md index 7b153cdf5d..69e6171e90 100644 --- a/docs/modules/jws_flattened_sign.md +++ b/docs/modules/jws_flattened_sign.md @@ -1,6 +1,10 @@ # Module: jws/flattened/sign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_flattened_verify.md b/docs/modules/jws_flattened_verify.md index 2880b5a0b9..3638c7d052 100644 --- a/docs/modules/jws_flattened_verify.md +++ b/docs/modules/jws_flattened_verify.md @@ -1,6 +1,10 @@ # Module: jws/flattened/verify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_general_sign.md b/docs/modules/jws_general_sign.md index 869f37f5c2..695fab661e 100644 --- a/docs/modules/jws_general_sign.md +++ b/docs/modules/jws_general_sign.md @@ -1,6 +1,10 @@ # Module: jws/general/sign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jws_general_verify.md b/docs/modules/jws_general_verify.md index 1767c4a98b..3779e95b48 100644 --- a/docs/modules/jws_general_verify.md +++ b/docs/modules/jws_general_verify.md @@ -1,6 +1,10 @@ # Module: jws/general/verify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_decrypt.md b/docs/modules/jwt_decrypt.md index e203dba5b9..782a59bcad 100644 --- a/docs/modules/jwt_decrypt.md +++ b/docs/modules/jwt_decrypt.md @@ -1,6 +1,10 @@ # Module: jwt/decrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_encrypt.md b/docs/modules/jwt_encrypt.md index a848439aac..5e91225873 100644 --- a/docs/modules/jwt_encrypt.md +++ b/docs/modules/jwt_encrypt.md @@ -1,6 +1,10 @@ # Module: jwt/encrypt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_produce.md b/docs/modules/jwt_produce.md index 607737fedd..2cf5be0907 100644 --- a/docs/modules/jwt_produce.md +++ b/docs/modules/jwt_produce.md @@ -1,6 +1,10 @@ # Module: jwt/produce -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_sign.md b/docs/modules/jwt_sign.md index 9362d801be..56e8257262 100644 --- a/docs/modules/jwt_sign.md +++ b/docs/modules/jwt_sign.md @@ -1,6 +1,10 @@ # Module: jwt/sign -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_unsecured.md b/docs/modules/jwt_unsecured.md index f0425e8d44..5c5428f955 100644 --- a/docs/modules/jwt_unsecured.md +++ b/docs/modules/jwt_unsecured.md @@ -1,6 +1,10 @@ # Module: jwt/unsecured -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/jwt_verify.md b/docs/modules/jwt_verify.md index 28abc1a317..f47d5ae8a6 100644 --- a/docs/modules/jwt_verify.md +++ b/docs/modules/jwt_verify.md @@ -1,6 +1,10 @@ # Module: jwt/verify -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/key_export.md b/docs/modules/key_export.md index 426df730b9..167f6338be 100644 --- a/docs/modules/key_export.md +++ b/docs/modules/key_export.md @@ -1,6 +1,10 @@ # Module: key/export -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/key_generate_key_pair.md b/docs/modules/key_generate_key_pair.md index a0e5bb427a..9e6bc4f127 100644 --- a/docs/modules/key_generate_key_pair.md +++ b/docs/modules/key_generate_key_pair.md @@ -1,6 +1,10 @@ # Module: key/generate\_key\_pair -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/key_generate_secret.md b/docs/modules/key_generate_secret.md index c4df711800..6bd2acfa66 100644 --- a/docs/modules/key_generate_secret.md +++ b/docs/modules/key_generate_secret.md @@ -1,6 +1,10 @@ # Module: key/generate\_secret -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/key_import.md b/docs/modules/key_import.md index 92dcf2fb0f..e5f83a0062 100644 --- a/docs/modules/key_import.md +++ b/docs/modules/key_import.md @@ -1,6 +1,10 @@ # Module: key/import -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/types.md b/docs/modules/types.md index ec3d099afa..63bc6b0ad4 100644 --- a/docs/modules/types.md +++ b/docs/modules/types.md @@ -1,6 +1,10 @@ # Module: types -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/util_base64url.md b/docs/modules/util_base64url.md index b127c305ea..686750c3e6 100644 --- a/docs/modules/util_base64url.md +++ b/docs/modules/util_base64url.md @@ -1,6 +1,10 @@ # Module: util/base64url -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/util_decode_jwt.md b/docs/modules/util_decode_jwt.md index e94eece09a..17e924906f 100644 --- a/docs/modules/util_decode_jwt.md +++ b/docs/modules/util_decode_jwt.md @@ -1,6 +1,10 @@ # Module: util/decode\_jwt -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/util_decode_protected_header.md b/docs/modules/util_decode_protected_header.md index cd7fb03fcd..19d6fc300f 100644 --- a/docs/modules/util_decode_protected_header.md +++ b/docs/modules/util_decode_protected_header.md @@ -1,6 +1,10 @@ # Module: util/decode\_protected\_header -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/modules/util_errors.md b/docs/modules/util_errors.md index b48eb1bf8d..07bba6fda3 100644 --- a/docs/modules/util_errors.md +++ b/docs/modules/util_errors.md @@ -1,6 +1,10 @@ # Module: util/errors -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- ## Table of contents diff --git a/docs/types/types.KeyLike.md b/docs/types/types.KeyLike.md index b36761eb57..befe793b0c 100644 --- a/docs/types/types.KeyLike.md +++ b/docs/types/types.KeyLike.md @@ -1,17 +1,22 @@ # Type alias: KeyLike -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Ƭ **KeyLike**: `Object` KeyLike are runtime-specific classes representing asymmetric keys or symmetric secrets. These are instances of [CryptoKey](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) and -additionally [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) in Node.js runtime. +additionally [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) in Node.js +runtime. [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) instances are also accepted as symmetric secret representation only. -[Key Import Functions](../modules/key_import.md#readme) can be used to import PEM, or JWK -formatted asymmetric keys and certificates to these runtime-specific representations. +[Key Import Functions](../modules/key_import.md) can be used to import PEM, or JWK formatted +asymmetric keys and certificates to these runtime-specific representations. In Node.js the [Buffer](https://nodejs.org/api/buffer.html#buffer) class is a subclass of Uint8Array and so Buffer can be provided for symmetric secrets as well. @@ -21,12 +26,12 @@ key/secret available in the Node.js runtime. In addition to the import functions you may use the runtime APIs [crypto.createPublicKey](https://nodejs.org/api/crypto.html#cryptocreatepublickeykey), [crypto.createPrivateKey](https://nodejs.org/api/crypto.html#cryptocreateprivatekeykey), and -[crypto.createSecretKey](https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding) to -obtain a KeyObject from your existing key material. +[crypto.createSecretKey](https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding) +to obtain a `KeyObject` from your existing key material. -[CryptoKey](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) is a representation of a -key/secret available in the Browser and Web-interoperable runtimes. In addition to the import -functions of this library you may use the +[CryptoKey](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) is a representation +of a key/secret available in the Browser and Web-interoperable runtimes. In addition to the +import functions of this library you may use the [SubtleCrypto.importKey](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey) API to obtain a CryptoKey from your existing key material. diff --git a/docs/types/util_decode_protected_header.ProtectedHeaderParameters.md b/docs/types/util_decode_protected_header.ProtectedHeaderParameters.md index b32422c9b4..6dbfc35150 100644 --- a/docs/types/util_decode_protected_header.ProtectedHeaderParameters.md +++ b/docs/types/util_decode_protected_header.ProtectedHeaderParameters.md @@ -1,5 +1,9 @@ # Type alias: ProtectedHeaderParameters -[💗 Help the project](https://github.com/sponsors/panva) +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- Ƭ **ProtectedHeaderParameters**: [`JWSHeaderParameters`](../interfaces/types.JWSHeaderParameters.md) & [`JWEHeaderParameters`](../interfaces/types.JWEHeaderParameters.md) diff --git a/package-lock.json b/package-lock.json index 02fbdad4dd..6221764ba6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jose", - "version": "4.13.1", + "version": "4.13.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jose", - "version": "4.13.1", + "version": "4.13.2", "license": "MIT", "devDependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index fdae954a67..d8deeb3ffb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jose", - "version": "4.13.1", + "version": "4.13.2", "description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes", "keywords": [ "browser",