Comparing version
@@ -5,2 +5,11 @@ # Changelog | ||
## [3.1.0](https://github.com/panva/jose/compare/v3.0.2...v3.1.0) (2020-11-22) | ||
### Features | ||
* added "KeyLike to JWK" module ([7a8418e](https://github.com/panva/jose/commit/7a8418eadd68b645fb7edf78873a35980ea8e41d)), closes [#109](https://github.com/panva/jose/issues/109) | ||
* allow compact verify/decrypt tokens to be uint8array encoded ([e39c3db](https://github.com/panva/jose/commit/e39c3dba75e5ae70697e6a4f93096c492a265c07)) | ||
* allow http.Agent and https.Agent passed in remote JWK Set ([38494a8](https://github.com/panva/jose/commit/38494a88828a8df2015efa78ca29c1a6317a3a50)) | ||
## [3.0.2](https://github.com/panva/jose/compare/v3.0.1...v3.0.2) (2020-11-15) | ||
@@ -7,0 +16,0 @@ |
import decrypt from '../flattened/decrypt.js'; | ||
import { JWEInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWEInvalid('Compact JWE must be a string or Uint8Array'); | ||
} | ||
@@ -12,9 +16,9 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split('.'); | ||
const decrypted = await decrypt({ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined), | ||
iv: (iv || undefined), | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined), | ||
encrypted_key: encryptedKey || undefined, | ||
}, key, options); | ||
return { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; | ||
} |
@@ -21,27 +21,27 @@ import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new JWEInvalid('JOSE Header missing'); | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new JWEInvalid('JWE Initialization Vector missing or incorrect type'); | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new JWEInvalid('JWE Ciphertext missing or incorrect type'); | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new JWEInvalid('JWE Authentication Tag missing or incorrect type'); | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new JWEInvalid('JWE Protected Header incorrect type'); | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new JWEInvalid('JWE Encrypted Key incorrect type'); | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new JWEInvalid('JWE AAD incorrect type'); | ||
} | ||
if ('header' in jwe && !isObject(jwe.header)) { | ||
if (jwe.header !== undefined && !isObject(jwe.header)) { | ||
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type'); | ||
} | ||
if ('unprotected' in jwe && !isObject(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { | ||
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); | ||
@@ -63,3 +63,3 @@ } | ||
checkCrit(parsedProt, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -73,6 +73,6 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); | ||
@@ -89,3 +89,3 @@ } | ||
let encryptedKey; | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url(jwe.encrypted_key); | ||
@@ -110,3 +110,3 @@ } | ||
let additionalData; | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)); | ||
@@ -122,12 +122,12 @@ } | ||
const result = { plaintext }; | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url(jwe.aad); | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected; | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header; | ||
@@ -134,0 +134,0 @@ } |
@@ -76,3 +76,3 @@ import ivFactory from '../../lib/iv.js'; | ||
checkCrit(this._protectedHeader, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -86,7 +86,7 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
if (!enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing'); | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -93,0 +93,0 @@ let encryptedKey; |
@@ -24,3 +24,3 @@ import { decode as base64url } from '../runtime/base64url.js'; | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is unsupported'); | ||
@@ -27,0 +27,0 @@ } |
@@ -24,2 +24,3 @@ import parseJWK from '../jwk/parse.js'; | ||
this._url = new URL(url.href); | ||
this._options = { agent: options === null || options === void 0 ? void 0 : options.agent }; | ||
this._timeoutDuration = | ||
@@ -91,3 +92,3 @@ typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; | ||
const cached = this._cached.get(jwk); | ||
if (!(protectedHeader.alg in cached)) { | ||
if (cached[protectedHeader.alg] === undefined) { | ||
const keyObject = (await parseJWK({ ...jwk, alg: protectedHeader.alg })); | ||
@@ -103,7 +104,6 @@ if (keyObject.type !== 'public') { | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration, this._options) | ||
.then((json) => { | ||
if (typeof json !== 'object' || | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -110,0 +110,0 @@ json.keys.some((key) => typeof key !== 'object' || !key)) { |
import verify from '../flattened/verify.js'; | ||
import { JWSInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWSInvalid('Compact JWS must be a string or Uint8Array'); | ||
} | ||
@@ -11,4 +15,8 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); | ||
} | ||
const verified = await verify({ payload, protected: protectedHeader, signature }, key, options); | ||
const verified = await verify({ | ||
payload: (payload || undefined), | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined), | ||
}, key, options); | ||
return { payload: verified.payload, protectedHeader: verified.protectedHeader }; | ||
} |
@@ -47,4 +47,4 @@ import isDisjoint from '../../lib/is_disjoint.js'; | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -51,0 +51,0 @@ checkKeyType(alg, key); |
@@ -16,15 +16,15 @@ import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
if (jws.protected === undefined && jws.header === undefined) { | ||
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); | ||
} | ||
if ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new JWSInvalid('JWS Protected Header incorrect type'); | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new JWSInvalid('JWS Payload missing'); | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new JWSInvalid('JWS Signature missing or incorrect type'); | ||
} | ||
if ('header' in jws && !isObject(jws.header)) { | ||
if (jws.header !== undefined && !isObject(jws.header)) { | ||
throw new JWSInvalid('JWS Unprotected Header incorrect type'); | ||
@@ -53,4 +53,4 @@ } | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -90,6 +90,6 @@ const algorithms = options && checkAlgOption(options.algorithms); | ||
const result = { payload }; | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header; | ||
@@ -96,0 +96,0 @@ } |
@@ -8,9 +8,9 @@ import decrypt from '../jwe/compact/decrypt.js'; | ||
const { protectedHeader } = decrypted; | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); | ||
} | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); | ||
} | ||
if ('aud' in protectedHeader && | ||
if (protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { | ||
@@ -17,0 +17,0 @@ throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); |
@@ -15,3 +15,3 @@ import { JOSENotSupported, JWEInvalid } from '../util/errors.js'; | ||
function assertHeaderParameter(joseHeader, parameter, name) { | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new JWEInvalid(`JOSE Header ${name} (${parameter}) missing`); | ||
@@ -37,3 +37,3 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -43,5 +43,5 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk); | ||
let partyVInfo; | ||
if (joseHeader.apu) | ||
if (joseHeader.apu !== undefined) | ||
partyUInfo = base64url(joseHeader.apu); | ||
if (joseHeader.apv) | ||
if (joseHeader.apv !== undefined) | ||
partyVInfo = base64url(joseHeader.apv); | ||
@@ -91,3 +91,3 @@ const sharedSecret = await ECDH.deriveKey(ephemeralKey, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, parseInt(alg.substr(-5, 3), 10) || cekLengths.get(joseHeader.enc), partyUInfo, partyVInfo); | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -94,0 +94,0 @@ } |
@@ -25,3 +25,3 @@ import cekFactory, { bitLengths as cekLengths } from '../lib/cek.js'; | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -80,3 +80,3 @@ const { apu, apv } = providedParameters; | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -83,0 +83,0 @@ } |
@@ -57,11 +57,11 @@ import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'; | ||
const now = epoch(currentDate || new Date()); | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); | ||
} | ||
} | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -74,3 +74,3 @@ throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); | ||
} | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -77,0 +77,0 @@ throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); |
@@ -10,3 +10,3 @@ const minute = 60; | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`); | ||
throw new TypeError('invalid time period format'); | ||
} | ||
@@ -13,0 +13,0 @@ const value = parseFloat(matched[1]); |
import { JOSENotSupported } from '../util/errors.js'; | ||
const isString = (input) => typeof input !== 'string' || input.length === 0; | ||
function validateCrit(Err, supported, protectedHeader, joseHeader) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]); | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set(); | ||
} | ||
if (!Array.isArray(protectedHeader.crit) || | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString)) { | ||
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'); | ||
@@ -19,6 +18,6 @@ } | ||
} | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`); | ||
} | ||
else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); | ||
@@ -25,0 +24,0 @@ } |
@@ -27,4 +27,5 @@ import { JWEInvalid, JOSENotSupported } from '../util/errors.js'; | ||
} | ||
if ('algorithm' in cek) { | ||
if (cek.algorithm.length !== expected) { | ||
if (cek.algorithm !== undefined) { | ||
const { length } = cek.algorithm; | ||
if (length !== expected) { | ||
throw new JWEInvalid('Invalid Content Encryption Key length'); | ||
@@ -31,0 +32,0 @@ } |
export default (alg, key) => { | ||
if (alg.startsWith('HS')) { | ||
const bitlen = parseInt(alg.substr(-3), 10); | ||
if (!('length' in key.algorithm) || key.algorithm.length < bitlen) { | ||
const { length } = key.algorithm; | ||
if (typeof length !== 'number' || length < bitlen) { | ||
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`); | ||
@@ -9,4 +10,4 @@ } | ||
if (alg.startsWith('RS') || alg.startsWith('PS')) { | ||
if (!('modulusLength' in key.algorithm) || | ||
key.algorithm.modulusLength < 2048) { | ||
const { modulusLength } = key.algorithm; | ||
if (typeof modulusLength !== 'number' || modulusLength < 2048) { | ||
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); | ||
@@ -13,0 +14,0 @@ } |
@@ -9,6 +9,4 @@ import { concat, uint64be } from '../lib/buffer_utils.js'; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await crypto.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'decrypt', | ||
]); | ||
const macKey = await crypto.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
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}`, | ||
@@ -15,0 +13,0 @@ name: 'HMAC', |
@@ -7,6 +7,4 @@ import { concat, uint64be } from '../lib/buffer_utils.js'; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await crypto.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'encrypt', | ||
]); | ||
const macKey = await crypto.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
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}`, | ||
@@ -13,0 +11,0 @@ name: 'HMAC', |
@@ -5,5 +5,9 @@ "use strict"; | ||
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'); | ||
throw new errors_js_1.JWEInvalid('Compact JWE must be a string or Uint8Array'); | ||
} | ||
@@ -15,7 +19,7 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split('.'); | ||
const decrypted = await decrypt_js_1.default({ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined), | ||
iv: (iv || undefined), | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined), | ||
encrypted_key: encryptedKey || undefined, | ||
}, key, options); | ||
@@ -22,0 +26,0 @@ return { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; |
@@ -23,27 +23,27 @@ "use strict"; | ||
} | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new errors_js_1.JWEInvalid('JOSE Header missing'); | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Initialization Vector missing or incorrect type'); | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Ciphertext missing or incorrect type'); | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Authentication Tag missing or incorrect type'); | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Protected Header incorrect type'); | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Encrypted Key incorrect type'); | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE AAD incorrect type'); | ||
} | ||
if ('header' in jwe && !is_object_js_1.default(jwe.header)) { | ||
if (jwe.header !== undefined && !is_object_js_1.default(jwe.header)) { | ||
throw new errors_js_1.JWEInvalid('JWE Shared Unprotected Header incorrect type'); | ||
} | ||
if ('unprotected' in jwe && !is_object_js_1.default(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !is_object_js_1.default(jwe.unprotected)) { | ||
throw new errors_js_1.JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); | ||
@@ -65,3 +65,3 @@ } | ||
checkCrit(parsedProt, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -75,6 +75,6 @@ throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new errors_js_1.JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); | ||
@@ -91,3 +91,3 @@ } | ||
let encryptedKey; | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url_js_1.decode(jwe.encrypted_key); | ||
@@ -112,3 +112,3 @@ } | ||
let additionalData; | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = buffer_utils_js_1.concat(protectedHeader, buffer_utils_js_1.encoder.encode('.'), buffer_utils_js_1.encoder.encode(jwe.aad)); | ||
@@ -124,12 +124,12 @@ } | ||
const result = { plaintext }; | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url_js_1.decode(jwe.aad); | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected; | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header; | ||
@@ -136,0 +136,0 @@ } |
@@ -78,3 +78,3 @@ "use strict"; | ||
checkCrit(this._protectedHeader, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -88,7 +88,7 @@ throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
if (!enc) { | ||
throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing'); | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -95,0 +95,0 @@ let encryptedKey; |
@@ -26,3 +26,3 @@ "use strict"; | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new errors_js_1.JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is unsupported'); | ||
@@ -29,0 +29,0 @@ } |
@@ -26,2 +26,3 @@ "use strict"; | ||
this._url = new URL(url.href); | ||
this._options = { agent: options === null || options === void 0 ? void 0 : options.agent }; | ||
this._timeoutDuration = | ||
@@ -93,3 +94,3 @@ typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; | ||
const cached = this._cached.get(jwk); | ||
if (!(protectedHeader.alg in cached)) { | ||
if (cached[protectedHeader.alg] === undefined) { | ||
const keyObject = (await parse_js_1.default({ ...jwk, alg: protectedHeader.alg })); | ||
@@ -105,7 +106,6 @@ if (keyObject.type !== 'public') { | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetch_js_1.default(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetch_js_1.default(this._url, this._timeoutDuration, this._options) | ||
.then((json) => { | ||
if (typeof json !== 'object' || | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -112,0 +112,0 @@ json.keys.some((key) => typeof key !== 'object' || !key)) { |
@@ -5,5 +5,9 @@ "use strict"; | ||
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'); | ||
throw new errors_js_1.JWSInvalid('Compact JWS must be a string or Uint8Array'); | ||
} | ||
@@ -14,5 +18,9 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); | ||
} | ||
const verified = await verify_js_1.default({ payload, protected: protectedHeader, signature }, key, options); | ||
const verified = await verify_js_1.default({ | ||
payload: (payload || undefined), | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined), | ||
}, key, options); | ||
return { payload: verified.payload, protectedHeader: verified.protectedHeader }; | ||
} | ||
exports.default = compactVerify; |
@@ -49,4 +49,4 @@ "use strict"; | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -53,0 +53,0 @@ check_key_type_js_1.default(alg, key); |
@@ -18,15 +18,15 @@ "use strict"; | ||
} | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
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 ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new errors_js_1.JWSInvalid('JWS Protected Header incorrect type'); | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new errors_js_1.JWSInvalid('JWS Payload missing'); | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new errors_js_1.JWSInvalid('JWS Signature missing or incorrect type'); | ||
} | ||
if ('header' in jws && !is_object_js_1.default(jws.header)) { | ||
if (jws.header !== undefined && !is_object_js_1.default(jws.header)) { | ||
throw new errors_js_1.JWSInvalid('JWS Unprotected Header incorrect type'); | ||
@@ -55,4 +55,4 @@ } | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -92,6 +92,6 @@ const algorithms = options && checkAlgOption(options.algorithms); | ||
const result = { payload }; | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header; | ||
@@ -98,0 +98,0 @@ } |
@@ -10,9 +10,9 @@ "use strict"; | ||
const { protectedHeader } = decrypted; | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new errors_js_1.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); | ||
} | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new errors_js_1.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); | ||
} | ||
if ('aud' in protectedHeader && | ||
if (protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { | ||
@@ -19,0 +19,0 @@ throw new errors_js_1.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); |
@@ -17,3 +17,3 @@ "use strict"; | ||
function assertHeaderParameter(joseHeader, parameter, name) { | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new errors_js_1.JWEInvalid(`JOSE Header ${name} (${parameter}) missing`); | ||
@@ -39,3 +39,3 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new errors_js_1.JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new errors_js_1.JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -45,5 +45,5 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk); | ||
let partyVInfo; | ||
if (joseHeader.apu) | ||
if (joseHeader.apu !== undefined) | ||
partyUInfo = base64url_js_1.decode(joseHeader.apu); | ||
if (joseHeader.apv) | ||
if (joseHeader.apv !== undefined) | ||
partyVInfo = base64url_js_1.decode(joseHeader.apv); | ||
@@ -93,3 +93,3 @@ const sharedSecret = await ECDH.deriveKey(ephemeralKey, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, parseInt(alg.substr(-5, 3), 10) || cek_js_1.bitLengths.get(joseHeader.enc), partyUInfo, partyVInfo); | ||
default: { | ||
throw new errors_js_1.JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new errors_js_1.JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -96,0 +96,0 @@ } |
@@ -27,3 +27,3 @@ "use strict"; | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new errors_js_1.JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new errors_js_1.JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -82,3 +82,3 @@ const { apu, apv } = providedParameters; | ||
default: { | ||
throw new errors_js_1.JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new errors_js_1.JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -85,0 +85,0 @@ } |
@@ -59,11 +59,11 @@ "use strict"; | ||
const now = epoch_js_1.default(currentDate || new Date()); | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); | ||
} | ||
} | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -76,3 +76,3 @@ throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); | ||
} | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -79,0 +79,0 @@ throw new errors_js_1.JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); |
@@ -12,3 +12,3 @@ "use strict"; | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`); | ||
throw new TypeError('invalid time period format'); | ||
} | ||
@@ -15,0 +15,0 @@ const value = parseFloat(matched[1]); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const errors_js_1 = require("../util/errors.js"); | ||
const isString = (input) => typeof input !== 'string' || input.length === 0; | ||
function validateCrit(Err, supported, protectedHeader, joseHeader) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]); | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set(); | ||
} | ||
if (!Array.isArray(protectedHeader.crit) || | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString)) { | ||
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'); | ||
@@ -21,6 +20,6 @@ } | ||
} | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`); | ||
} | ||
else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); | ||
@@ -27,0 +26,0 @@ } |
@@ -10,3 +10,3 @@ "use strict"; | ||
const generateIv = iv_js_1.default(random_js_1.default); | ||
exports.wrap = async (alg, key, cek, iv) => { | ||
const wrap = async (alg, key, cek, iv) => { | ||
const jweAlgorithm = alg.substr(0, 7); | ||
@@ -17,5 +17,7 @@ iv || (iv = await generateIv(jweAlgorithm)); | ||
}; | ||
exports.unwrap = async (alg, key, encryptedKey, iv, tag) => { | ||
exports.wrap = wrap; | ||
const unwrap = async (alg, key, encryptedKey, iv, tag) => { | ||
const jweAlgorithm = alg.substr(0, 7); | ||
return decrypt_js_1.default(jweAlgorithm, key instanceof Uint8Array ? key : key.export(), encryptedKey, iv, tag, new Uint8Array()); | ||
}; | ||
exports.unwrap = unwrap; |
@@ -13,3 +13,3 @@ "use strict"; | ||
} | ||
exports.wrap = async (alg, key, cek) => { | ||
const wrap = async (alg, key, cek) => { | ||
const size = parseInt(alg.substr(1, 3), 10); | ||
@@ -25,3 +25,4 @@ const algorithm = `aes${size}-wrap`; | ||
}; | ||
exports.unwrap = async (alg, key, encryptedKey) => { | ||
exports.wrap = wrap; | ||
const unwrap = async (alg, key, encryptedKey) => { | ||
const size = parseInt(alg.substr(1, 3), 10); | ||
@@ -37,1 +38,2 @@ const algorithm = `aes${size}-wrap`; | ||
}; | ||
exports.unwrap = unwrap; |
@@ -5,4 +5,5 @@ "use strict"; | ||
const buffer_utils_js_1 = require("../lib/buffer_utils.js"); | ||
exports.encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); | ||
exports.decode = (input) => { | ||
const encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); | ||
exports.encode = encode; | ||
const decode = (input) => { | ||
let encoded = input; | ||
@@ -14,1 +15,2 @@ if (encoded instanceof Uint8Array) { | ||
}; | ||
exports.decode = decode; |
@@ -43,5 +43,6 @@ "use strict"; | ||
}; | ||
exports.setModulusLength = (keyObject, modulusLength) => { | ||
const setModulusLength = (keyObject, modulusLength) => { | ||
weakMap.set(keyObject, modulusLength); | ||
}; | ||
exports.setModulusLength = setModulusLength; | ||
exports.default = (key, alg) => { | ||
@@ -48,0 +49,0 @@ if (getModulusLength(key) < 2048) { |
@@ -15,4 +15,4 @@ "use strict"; | ||
} | ||
const encKey = cek.slice(keySize >> 3); | ||
const macKey = cek.slice(0, keySize >> 3); | ||
const encKey = cek.subarray(keySize >> 3); | ||
const macKey = cek.subarray(0, keySize >> 3); | ||
const macSize = parseInt(enc.substr(-3), 10); | ||
@@ -19,0 +19,0 @@ const algorithm = `aes-${keySize}-cbc`; |
@@ -13,3 +13,3 @@ "use strict"; | ||
const concatKdf = buffer_utils_js_1.concatKdf.bind(undefined, digest_js_1.default.bind(undefined, 'sha256')); | ||
exports.deriveKey = async (publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) => { | ||
const deriveKey = async (publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) => { | ||
const value = buffer_utils_js_1.concat(buffer_utils_js_1.lengthAndInput(buffer_utils_js_1.encoder.encode(algorithm)), buffer_utils_js_1.lengthAndInput(apu), buffer_utils_js_1.lengthAndInput(apv), buffer_utils_js_1.uint32be(keyLength)); | ||
@@ -19,3 +19,4 @@ const sharedSecret = crypto_1.diffieHellman({ privateKey, publicKey }); | ||
}; | ||
exports.ephemeralKeyToPublicJWK = function ephemeralKeyToPublicJWK(key) { | ||
exports.deriveKey = deriveKey; | ||
const ephemeralKeyToPublicJWK = function ephemeralKeyToPublicJWK(key) { | ||
switch (key.asymmetricKeyType) { | ||
@@ -43,3 +44,4 @@ case 'x25519': | ||
}; | ||
exports.generateEpk = async (key) => { | ||
exports.ephemeralKeyToPublicJWK = ephemeralKeyToPublicJWK; | ||
const generateEpk = async (key) => { | ||
switch (key.asymmetricKeyType) { | ||
@@ -59,3 +61,4 @@ case 'x25519': | ||
}; | ||
exports.publicJwkToEphemeralKey = async (jwk) => { | ||
exports.generateEpk = generateEpk; | ||
const publicJwkToEphemeralKey = async (jwk) => { | ||
let pem; | ||
@@ -101,3 +104,5 @@ switch (jwk.crv) { | ||
}; | ||
exports.publicJwkToEphemeralKey = publicJwkToEphemeralKey; | ||
const curves = ['P-256', 'P-384', 'P-521', 'X25519', 'X448']; | ||
exports.ecdhAllowed = (key) => curves.includes(get_named_curve_js_1.default(key)); | ||
const ecdhAllowed = (key) => curves.includes(get_named_curve_js_1.default(key)); | ||
exports.ecdhAllowed = ecdhAllowed; |
@@ -13,4 +13,4 @@ "use strict"; | ||
} | ||
const encKey = cek.slice(keySize >> 3); | ||
const macKey = cek.slice(0, keySize >> 3); | ||
const encKey = cek.subarray(keySize >> 3); | ||
const macKey = cek.subarray(0, keySize >> 3); | ||
const algorithm = `aes-${keySize}-cbc`; | ||
@@ -17,0 +17,0 @@ const cipher = crypto_1.createCipheriv(algorithm, encKey, iv); |
@@ -12,10 +12,8 @@ "use strict"; | ||
const fetch = async (url, timeout, options) => { | ||
if (!(url.protocol in protocols)) { | ||
if (protocols[url.protocol] === undefined) { | ||
throw new TypeError('Unsupported URL protocol.'); | ||
} | ||
return new Promise((resolve, reject) => { | ||
protocols[url.protocol](url, { | ||
...options, | ||
timeout, | ||
}, async (response) => { | ||
const { agent } = options; | ||
protocols[url.protocol](url, { agent, timeout }, async (response) => { | ||
if (response.statusCode !== 200) { | ||
@@ -22,0 +20,0 @@ reject(new errors_js_1.JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response')); |
@@ -16,5 +16,8 @@ "use strict"; | ||
switch (key.asymmetricKeyType) { | ||
case 'ed25519': | ||
case 'ed448': | ||
return `Ed${key.asymmetricKeyType.substr(2)}`; | ||
case 'x25519': | ||
case 'x448': | ||
return key.asymmetricKeyType.toUpperCase(); | ||
return `X${key.asymmetricKeyType.substr(1)}`; | ||
case 'ec': { | ||
@@ -25,3 +28,5 @@ if (weakMap.has(key)) { | ||
if (key.type === 'private') { | ||
return getNamedCurve(crypto_1.createPublicKey(key)); | ||
const curve = getNamedCurve(crypto_1.createPublicKey(key)); | ||
weakMap.set(key, curve); | ||
return curve; | ||
} | ||
@@ -28,0 +33,0 @@ const buf = key.export({ format: 'der', type: 'spki' }); |
@@ -47,2 +47,7 @@ "use strict"; | ||
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) { | ||
@@ -57,6 +62,11 @@ enc.zero(); | ||
enc$2.octStr(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.from([0xa1])); | ||
enc$2.add(f2); | ||
const f = enc$2.end(); | ||
enc.add(Buffer.from([0x04])); | ||
enc.add(Buffer.from([f.length])); | ||
enc.add(f); | ||
const enc$4 = new asn1_sequence_encoder_js_1.default(); | ||
enc$4.add(f); | ||
const f3 = enc$4.end(Buffer.from([0x04])); | ||
enc.add(f3); | ||
const der = enc.end(); | ||
@@ -71,7 +81,2 @@ const keyObject = crypto_1.createPrivateKey({ key: der, format: 'der', type: 'pkcs8' }); | ||
enc.add(enc$1.end()); | ||
const pub = Buffer.concat([ | ||
Buffer.alloc(1, 4), | ||
Buffer.from(jwk.x, 'base64'), | ||
Buffer.from(jwk.y, 'base64'), | ||
]); | ||
enc.bitStr(pub); | ||
@@ -78,0 +83,0 @@ const der = enc.end(); |
@@ -12,3 +12,3 @@ "use strict"; | ||
const pbkdf2 = util_1.promisify(crypto_1.pbkdf2); | ||
exports.encrypt = async (alg, key, cek, p2c = Math.floor(Math.random() * 2049) + 2048, p2s = random_js_1.default(new Uint8Array(16))) => { | ||
const encrypt = async (alg, key, cek, p2c = Math.floor(Math.random() * 2049) + 2048, p2s = random_js_1.default(new Uint8Array(16))) => { | ||
check_p2s_js_1.default(p2s); | ||
@@ -22,3 +22,4 @@ const salt = buffer_utils_js_1.p2s(alg, p2s); | ||
}; | ||
exports.decrypt = async (alg, key, encryptedKey, p2c, p2s) => { | ||
exports.encrypt = encrypt; | ||
const decrypt = async (alg, key, encryptedKey, p2c, p2s) => { | ||
check_p2s_js_1.default(p2s); | ||
@@ -31,1 +32,2 @@ const salt = buffer_utils_js_1.p2s(alg, p2s); | ||
}; | ||
exports.decrypt = decrypt; |
@@ -39,3 +39,3 @@ "use strict"; | ||
}; | ||
exports.encrypt = async (alg, key, cek) => { | ||
const encrypt = async (alg, key, cek) => { | ||
const padding = resolvePadding(alg); | ||
@@ -46,3 +46,4 @@ const oaepHash = resolveOaepHash(alg); | ||
}; | ||
exports.decrypt = async (alg, key, encryptedKey) => { | ||
exports.encrypt = encrypt; | ||
const decrypt = async (alg, key, encryptedKey) => { | ||
const padding = resolvePadding(alg); | ||
@@ -53,1 +54,2 @@ const oaepHash = resolveOaepHash(alg); | ||
}; | ||
exports.decrypt = decrypt; |
@@ -8,7 +8,9 @@ "use strict"; | ||
const deflateRaw = util_1.promisify(zlib_1.deflateRaw); | ||
exports.inflate = async (input) => { | ||
const inflate = async (input) => { | ||
return inflateRaw(input); | ||
}; | ||
exports.deflate = async (input) => { | ||
exports.inflate = inflate; | ||
const deflate = async (input) => { | ||
return deflateRaw(input); | ||
}; | ||
exports.deflate = deflate; |
import decrypt from '../flattened/decrypt.js'; | ||
import { JWEInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWEInvalid('Compact JWE must be a string or Uint8Array'); | ||
} | ||
@@ -12,9 +16,9 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split('.'); | ||
const decrypted = await decrypt({ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined), | ||
iv: (iv || undefined), | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined), | ||
encrypted_key: encryptedKey || undefined, | ||
}, key, options); | ||
return { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; | ||
} |
@@ -21,27 +21,27 @@ import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new JWEInvalid('JOSE Header missing'); | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new JWEInvalid('JWE Initialization Vector missing or incorrect type'); | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new JWEInvalid('JWE Ciphertext missing or incorrect type'); | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new JWEInvalid('JWE Authentication Tag missing or incorrect type'); | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new JWEInvalid('JWE Protected Header incorrect type'); | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new JWEInvalid('JWE Encrypted Key incorrect type'); | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new JWEInvalid('JWE AAD incorrect type'); | ||
} | ||
if ('header' in jwe && !isObject(jwe.header)) { | ||
if (jwe.header !== undefined && !isObject(jwe.header)) { | ||
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type'); | ||
} | ||
if ('unprotected' in jwe && !isObject(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { | ||
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); | ||
@@ -63,3 +63,3 @@ } | ||
checkCrit(parsedProt, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -73,6 +73,6 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); | ||
@@ -89,3 +89,3 @@ } | ||
let encryptedKey; | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url(jwe.encrypted_key); | ||
@@ -110,3 +110,3 @@ } | ||
let additionalData; | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)); | ||
@@ -122,12 +122,12 @@ } | ||
const result = { plaintext }; | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url(jwe.aad); | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected; | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header; | ||
@@ -134,0 +134,0 @@ } |
@@ -76,3 +76,3 @@ import ivFactory from '../../lib/iv.js'; | ||
checkCrit(this._protectedHeader, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -86,7 +86,7 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
if (!enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing'); | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -93,0 +93,0 @@ let encryptedKey; |
@@ -24,3 +24,3 @@ import { decode as base64url } from '../runtime/base64url.js'; | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is unsupported'); | ||
@@ -27,0 +27,0 @@ } |
@@ -24,2 +24,3 @@ import parseJWK from '../jwk/parse.js'; | ||
this._url = new URL(url.href); | ||
this._options = { agent: options === null || options === void 0 ? void 0 : options.agent }; | ||
this._timeoutDuration = | ||
@@ -91,3 +92,3 @@ typeof (options === null || options === void 0 ? void 0 : options.timeoutDuration) === 'number' ? options === null || options === void 0 ? void 0 : options.timeoutDuration : 5000; | ||
const cached = this._cached.get(jwk); | ||
if (!(protectedHeader.alg in cached)) { | ||
if (cached[protectedHeader.alg] === undefined) { | ||
const keyObject = (await parseJWK({ ...jwk, alg: protectedHeader.alg })); | ||
@@ -103,7 +104,6 @@ if (keyObject.type !== 'public') { | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration, this._options) | ||
.then((json) => { | ||
if (typeof json !== 'object' || | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -110,0 +110,0 @@ json.keys.some((key) => typeof key !== 'object' || !key)) { |
import verify from '../flattened/verify.js'; | ||
import { JWSInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWSInvalid('Compact JWS must be a string or Uint8Array'); | ||
} | ||
@@ -11,4 +15,8 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); | ||
} | ||
const verified = await verify({ payload, protected: protectedHeader, signature }, key, options); | ||
const verified = await verify({ | ||
payload: (payload || undefined), | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined), | ||
}, key, options); | ||
return { payload: verified.payload, protectedHeader: verified.protectedHeader }; | ||
} |
@@ -47,4 +47,4 @@ import isDisjoint from '../../lib/is_disjoint.js'; | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -51,0 +51,0 @@ checkKeyType(alg, key); |
@@ -16,15 +16,15 @@ import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
if (jws.protected === undefined && jws.header === undefined) { | ||
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); | ||
} | ||
if ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new JWSInvalid('JWS Protected Header incorrect type'); | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new JWSInvalid('JWS Payload missing'); | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new JWSInvalid('JWS Signature missing or incorrect type'); | ||
} | ||
if ('header' in jws && !isObject(jws.header)) { | ||
if (jws.header !== undefined && !isObject(jws.header)) { | ||
throw new JWSInvalid('JWS Unprotected Header incorrect type'); | ||
@@ -53,4 +53,4 @@ } | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -90,6 +90,6 @@ const algorithms = options && checkAlgOption(options.algorithms); | ||
const result = { payload }; | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header; | ||
@@ -96,0 +96,0 @@ } |
@@ -8,9 +8,9 @@ import decrypt from '../jwe/compact/decrypt.js'; | ||
const { protectedHeader } = decrypted; | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); | ||
} | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); | ||
} | ||
if ('aud' in protectedHeader && | ||
if (protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { | ||
@@ -17,0 +17,0 @@ throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); |
@@ -15,3 +15,3 @@ import { JOSENotSupported, JWEInvalid } from '../util/errors.js'; | ||
function assertHeaderParameter(joseHeader, parameter, name) { | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new JWEInvalid(`JOSE Header ${name} (${parameter}) missing`); | ||
@@ -37,3 +37,3 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -43,5 +43,5 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk); | ||
let partyVInfo; | ||
if (joseHeader.apu) | ||
if (joseHeader.apu !== undefined) | ||
partyUInfo = base64url(joseHeader.apu); | ||
if (joseHeader.apv) | ||
if (joseHeader.apv !== undefined) | ||
partyVInfo = base64url(joseHeader.apv); | ||
@@ -91,3 +91,3 @@ const sharedSecret = await ECDH.deriveKey(ephemeralKey, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, parseInt(alg.substr(-5, 3), 10) || cekLengths.get(joseHeader.enc), partyUInfo, partyVInfo); | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -94,0 +94,0 @@ } |
@@ -25,3 +25,3 @@ import cekFactory, { bitLengths as cekLengths } from '../lib/cek.js'; | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -80,3 +80,3 @@ const { apu, apv } = providedParameters; | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -83,0 +83,0 @@ } |
@@ -57,11 +57,11 @@ import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'; | ||
const now = epoch(currentDate || new Date()); | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); | ||
} | ||
} | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -74,3 +74,3 @@ throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); | ||
} | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -77,0 +77,0 @@ throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); |
@@ -10,3 +10,3 @@ const minute = 60; | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`); | ||
throw new TypeError('invalid time period format'); | ||
} | ||
@@ -13,0 +13,0 @@ const value = parseFloat(matched[1]); |
import { JOSENotSupported } from '../util/errors.js'; | ||
const isString = (input) => typeof input !== 'string' || input.length === 0; | ||
function validateCrit(Err, supported, protectedHeader, joseHeader) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]); | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set(); | ||
} | ||
if (!Array.isArray(protectedHeader.crit) || | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString)) { | ||
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'); | ||
@@ -19,6 +18,6 @@ } | ||
} | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`); | ||
} | ||
else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); | ||
@@ -25,0 +24,0 @@ } |
@@ -13,4 +13,4 @@ import { getCiphers, KeyObject, createDecipheriv } from 'crypto'; | ||
} | ||
const encKey = cek.slice(keySize >> 3); | ||
const macKey = cek.slice(0, keySize >> 3); | ||
const encKey = cek.subarray(keySize >> 3); | ||
const macKey = cek.subarray(0, keySize >> 3); | ||
const macSize = parseInt(enc.substr(-3), 10); | ||
@@ -17,0 +17,0 @@ const algorithm = `aes-${keySize}-cbc`; |
@@ -11,4 +11,4 @@ import { KeyObject, createCipheriv } from 'crypto'; | ||
} | ||
const encKey = cek.slice(keySize >> 3); | ||
const macKey = cek.slice(0, keySize >> 3); | ||
const encKey = cek.subarray(keySize >> 3); | ||
const macKey = cek.subarray(0, keySize >> 3); | ||
const algorithm = `aes-${keySize}-cbc`; | ||
@@ -15,0 +15,0 @@ const cipher = createCipheriv(algorithm, encKey, iv); |
@@ -10,10 +10,8 @@ import { get as http } from 'http'; | ||
const fetch = async (url, timeout, options) => { | ||
if (!(url.protocol in protocols)) { | ||
if (protocols[url.protocol] === undefined) { | ||
throw new TypeError('Unsupported URL protocol.'); | ||
} | ||
return new Promise((resolve, reject) => { | ||
protocols[url.protocol](url, { | ||
...options, | ||
timeout, | ||
}, async (response) => { | ||
const { agent } = options; | ||
protocols[url.protocol](url, { agent, timeout }, async (response) => { | ||
if (response.statusCode !== 200) { | ||
@@ -20,0 +18,0 @@ reject(new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response')); |
@@ -13,5 +13,8 @@ import { createPublicKey } from 'crypto'; | ||
switch (key.asymmetricKeyType) { | ||
case 'ed25519': | ||
case 'ed448': | ||
return `Ed${key.asymmetricKeyType.substr(2)}`; | ||
case 'x25519': | ||
case 'x448': | ||
return key.asymmetricKeyType.toUpperCase(); | ||
return `X${key.asymmetricKeyType.substr(1)}`; | ||
case 'ec': { | ||
@@ -22,3 +25,5 @@ if (weakMap.has(key)) { | ||
if (key.type === 'private') { | ||
return getNamedCurve(createPublicKey(key)); | ||
const curve = getNamedCurve(createPublicKey(key)); | ||
weakMap.set(key, curve); | ||
return curve; | ||
} | ||
@@ -25,0 +30,0 @@ const buf = key.export({ format: 'der', type: 'spki' }); |
@@ -45,2 +45,7 @@ import { createPrivateKey, createPublicKey, createSecretKey } from 'crypto'; | ||
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) { | ||
@@ -55,6 +60,11 @@ enc.zero(); | ||
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(); | ||
enc.add(Buffer.from([0x04])); | ||
enc.add(Buffer.from([f.length])); | ||
enc.add(f); | ||
const enc$4 = new Asn1SequenceEncoder(); | ||
enc$4.add(f); | ||
const f3 = enc$4.end(Buffer.from([0x04])); | ||
enc.add(f3); | ||
const der = enc.end(); | ||
@@ -69,7 +79,2 @@ const keyObject = createPrivateKey({ key: der, format: 'der', type: 'pkcs8' }); | ||
enc.add(enc$1.end()); | ||
const pub = Buffer.concat([ | ||
Buffer.alloc(1, 4), | ||
Buffer.from(jwk.x, 'base64'), | ||
Buffer.from(jwk.y, 'base64'), | ||
]); | ||
enc.bitStr(pub); | ||
@@ -76,0 +81,0 @@ const der = enc.end(); |
@@ -5,5 +5,9 @@ "use strict"; | ||
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'); | ||
throw new errors_js_1.JWEInvalid('Compact JWE must be a string or Uint8Array'); | ||
} | ||
@@ -15,7 +19,7 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split('.'); | ||
const decrypted = await decrypt_js_1.default({ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined), | ||
iv: (iv || undefined), | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined), | ||
encrypted_key: encryptedKey || undefined, | ||
}, key, options); | ||
@@ -22,0 +26,0 @@ return { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; |
@@ -23,27 +23,27 @@ "use strict"; | ||
} | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new errors_js_1.JWEInvalid('JOSE Header missing'); | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Initialization Vector missing or incorrect type'); | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Ciphertext missing or incorrect type'); | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Authentication Tag missing or incorrect type'); | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Protected Header incorrect type'); | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE Encrypted Key incorrect type'); | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new errors_js_1.JWEInvalid('JWE AAD incorrect type'); | ||
} | ||
if ('header' in jwe && !is_object_js_1.default(jwe.header)) { | ||
if (jwe.header !== undefined && !is_object_js_1.default(jwe.header)) { | ||
throw new errors_js_1.JWEInvalid('JWE Shared Unprotected Header incorrect type'); | ||
} | ||
if ('unprotected' in jwe && !is_object_js_1.default(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !is_object_js_1.default(jwe.unprotected)) { | ||
throw new errors_js_1.JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); | ||
@@ -65,3 +65,3 @@ } | ||
checkCrit(parsedProt, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -75,6 +75,6 @@ throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new errors_js_1.JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); | ||
@@ -91,3 +91,3 @@ } | ||
let encryptedKey; | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url_js_1.decode(jwe.encrypted_key); | ||
@@ -112,3 +112,3 @@ } | ||
let additionalData; | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = buffer_utils_js_1.concat(protectedHeader, buffer_utils_js_1.encoder.encode('.'), buffer_utils_js_1.encoder.encode(jwe.aad)); | ||
@@ -124,12 +124,12 @@ } | ||
const result = { plaintext }; | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url_js_1.decode(jwe.aad); | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected; | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header; | ||
@@ -136,0 +136,0 @@ } |
@@ -78,3 +78,3 @@ "use strict"; | ||
checkCrit(this._protectedHeader, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -88,7 +88,7 @@ throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
if (!enc) { | ||
throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing'); | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -95,0 +95,0 @@ let encryptedKey; |
@@ -26,3 +26,3 @@ "use strict"; | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new errors_js_1.JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is unsupported'); | ||
@@ -29,0 +29,0 @@ } |
@@ -26,2 +26,3 @@ "use strict"; | ||
this._url = new URL(url.href); | ||
this._options = { agent: options?.agent }; | ||
this._timeoutDuration = | ||
@@ -93,3 +94,3 @@ typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000; | ||
const cached = this._cached.get(jwk); | ||
if (!(protectedHeader.alg in cached)) { | ||
if (cached[protectedHeader.alg] === undefined) { | ||
const keyObject = (await parse_js_1.default({ ...jwk, alg: protectedHeader.alg })); | ||
@@ -105,7 +106,6 @@ if (keyObject.type !== 'public') { | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetch_js_1.default(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetch_js_1.default(this._url, this._timeoutDuration, this._options) | ||
.then((json) => { | ||
if (typeof json !== 'object' || | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -112,0 +112,0 @@ json.keys.some((key) => typeof key !== 'object' || !key)) { |
@@ -5,5 +5,9 @@ "use strict"; | ||
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'); | ||
throw new errors_js_1.JWSInvalid('Compact JWS must be a string or Uint8Array'); | ||
} | ||
@@ -14,5 +18,9 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); | ||
} | ||
const verified = await verify_js_1.default({ payload, protected: protectedHeader, signature }, key, options); | ||
const verified = await verify_js_1.default({ | ||
payload: (payload || undefined), | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined), | ||
}, key, options); | ||
return { payload: verified.payload, protectedHeader: verified.protectedHeader }; | ||
} | ||
exports.default = compactVerify; |
@@ -49,4 +49,4 @@ "use strict"; | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -53,0 +53,0 @@ check_key_type_js_1.default(alg, key); |
@@ -18,15 +18,15 @@ "use strict"; | ||
} | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
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 ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new errors_js_1.JWSInvalid('JWS Protected Header incorrect type'); | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new errors_js_1.JWSInvalid('JWS Payload missing'); | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new errors_js_1.JWSInvalid('JWS Signature missing or incorrect type'); | ||
} | ||
if ('header' in jws && !is_object_js_1.default(jws.header)) { | ||
if (jws.header !== undefined && !is_object_js_1.default(jws.header)) { | ||
throw new errors_js_1.JWSInvalid('JWS Unprotected Header incorrect type'); | ||
@@ -55,4 +55,4 @@ } | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new errors_js_1.JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -92,6 +92,6 @@ const algorithms = options && checkAlgOption(options.algorithms); | ||
const result = { payload }; | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header; | ||
@@ -98,0 +98,0 @@ } |
@@ -10,9 +10,9 @@ "use strict"; | ||
const { protectedHeader } = decrypted; | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new errors_js_1.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); | ||
} | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new errors_js_1.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); | ||
} | ||
if ('aud' in protectedHeader && | ||
if (protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { | ||
@@ -19,0 +19,0 @@ throw new errors_js_1.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); |
@@ -17,3 +17,3 @@ "use strict"; | ||
function assertHeaderParameter(joseHeader, parameter, name) { | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new errors_js_1.JWEInvalid(`JOSE Header ${name} (${parameter}) missing`); | ||
@@ -39,3 +39,3 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new errors_js_1.JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new errors_js_1.JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -45,5 +45,5 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk); | ||
let partyVInfo; | ||
if (joseHeader.apu) | ||
if (joseHeader.apu !== undefined) | ||
partyUInfo = base64url_js_1.decode(joseHeader.apu); | ||
if (joseHeader.apv) | ||
if (joseHeader.apv !== undefined) | ||
partyVInfo = base64url_js_1.decode(joseHeader.apv); | ||
@@ -93,3 +93,3 @@ const sharedSecret = await ECDH.deriveKey(ephemeralKey, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, parseInt(alg.substr(-5, 3), 10) || cek_js_1.bitLengths.get(joseHeader.enc), partyUInfo, partyVInfo); | ||
default: { | ||
throw new errors_js_1.JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new errors_js_1.JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -96,0 +96,0 @@ } |
@@ -27,3 +27,3 @@ "use strict"; | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new errors_js_1.JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new errors_js_1.JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -82,3 +82,3 @@ const { apu, apv } = providedParameters; | ||
default: { | ||
throw new errors_js_1.JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new errors_js_1.JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -85,0 +85,0 @@ } |
@@ -59,11 +59,11 @@ "use strict"; | ||
const now = epoch_js_1.default(currentDate || new Date()); | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new errors_js_1.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); | ||
} | ||
} | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -76,3 +76,3 @@ throw new errors_js_1.JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); | ||
} | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -79,0 +79,0 @@ throw new errors_js_1.JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); |
@@ -12,3 +12,3 @@ "use strict"; | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`); | ||
throw new TypeError('invalid time period format'); | ||
} | ||
@@ -15,0 +15,0 @@ const value = parseFloat(matched[1]); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const errors_js_1 = require("../util/errors.js"); | ||
const isString = (input) => typeof input !== 'string' || input.length === 0; | ||
function validateCrit(Err, supported, protectedHeader, joseHeader) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]); | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set(); | ||
} | ||
if (!Array.isArray(protectedHeader.crit) || | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString)) { | ||
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'); | ||
@@ -21,6 +20,6 @@ } | ||
} | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`); | ||
} | ||
else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); | ||
@@ -27,0 +26,0 @@ } |
@@ -10,3 +10,3 @@ "use strict"; | ||
const generateIv = iv_js_1.default(random_js_1.default); | ||
exports.wrap = async (alg, key, cek, iv) => { | ||
const wrap = async (alg, key, cek, iv) => { | ||
const jweAlgorithm = alg.substr(0, 7); | ||
@@ -17,5 +17,7 @@ iv ||= await generateIv(jweAlgorithm); | ||
}; | ||
exports.unwrap = async (alg, key, encryptedKey, iv, tag) => { | ||
exports.wrap = wrap; | ||
const unwrap = async (alg, key, encryptedKey, iv, tag) => { | ||
const jweAlgorithm = alg.substr(0, 7); | ||
return decrypt_js_1.default(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array()); | ||
}; | ||
exports.unwrap = unwrap; |
@@ -11,3 +11,3 @@ "use strict"; | ||
} | ||
exports.wrap = async (alg, key, cek) => { | ||
const wrap = async (alg, key, cek) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -25,3 +25,4 @@ let cryptoKey; | ||
}; | ||
exports.unwrap = async (alg, key, encryptedKey) => { | ||
exports.wrap = wrap; | ||
const unwrap = async (alg, key, encryptedKey) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -39,1 +40,2 @@ let cryptoKey; | ||
}; | ||
exports.unwrap = unwrap; |
@@ -5,4 +5,5 @@ "use strict"; | ||
const buffer_utils_js_1 = require("../lib/buffer_utils.js"); | ||
exports.encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); | ||
exports.decode = (input) => { | ||
const encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); | ||
exports.encode = encode; | ||
const decode = (input) => { | ||
let encoded = input; | ||
@@ -14,1 +15,2 @@ if (encoded instanceof Uint8Array) { | ||
}; | ||
exports.decode = decode; |
@@ -29,4 +29,5 @@ "use strict"; | ||
} | ||
if ('algorithm' in cek) { | ||
if (cek.algorithm.length !== expected) { | ||
if (cek.algorithm !== undefined) { | ||
const { length } = cek.algorithm; | ||
if (length !== expected) { | ||
throw new errors_js_1.JWEInvalid('Invalid Content Encryption Key length'); | ||
@@ -33,0 +34,0 @@ } |
@@ -6,3 +6,4 @@ "use strict"; | ||
const bitlen = parseInt(alg.substr(-3), 10); | ||
if (!('length' in key.algorithm) || key.algorithm.length < bitlen) { | ||
const { length } = key.algorithm; | ||
if (typeof length !== 'number' || length < bitlen) { | ||
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`); | ||
@@ -12,4 +13,4 @@ } | ||
if (alg.startsWith('RS') || alg.startsWith('PS')) { | ||
if (!('modulusLength' in key.algorithm) || | ||
key.algorithm.modulusLength < 2048) { | ||
const { modulusLength } = key.algorithm; | ||
if (typeof modulusLength !== 'number' || modulusLength < 2048) { | ||
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); | ||
@@ -16,0 +17,0 @@ } |
@@ -11,6 +11,4 @@ "use strict"; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'decrypt', | ||
]); | ||
const macKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
const encKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.subarray(keySize >> 3), 'AES-CBC', false, ['decrypt']); | ||
const macKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.subarray(0, keySize >> 3), { | ||
hash: `SHA-${keySize << 1}`, | ||
@@ -17,0 +15,0 @@ name: 'HMAC', |
@@ -8,3 +8,3 @@ "use strict"; | ||
const concatKdf = buffer_utils_js_1.concatKdf.bind(undefined, digest_js_1.default.bind(undefined, 'sha256')); | ||
exports.deriveKey = async (publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) => { | ||
const deriveKey = async (publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -22,3 +22,4 @@ const value = buffer_utils_js_1.concat(buffer_utils_js_1.lengthAndInput(buffer_utils_js_1.encoder.encode(algorithm)), buffer_utils_js_1.lengthAndInput(apu), buffer_utils_js_1.lengthAndInput(apv), buffer_utils_js_1.uint32be(keyLength)); | ||
}; | ||
exports.ephemeralKeyToPublicJWK = async function ephemeralKeyToPublicJWK(key) { | ||
exports.deriveKey = deriveKey; | ||
const ephemeralKeyToPublicJWK = async function ephemeralKeyToPublicJWK(key) { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -28,11 +29,15 @@ const { crv, kty, x, y } = (await webcrypto_js_1.default.subtle.exportKey('jwk', key)); | ||
}; | ||
exports.generateEpk = async (key) => { | ||
exports.ephemeralKeyToPublicJWK = ephemeralKeyToPublicJWK; | ||
const generateEpk = async (key) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
return (await webcrypto_js_1.default.subtle.generateKey({ name: 'ECDH', namedCurve: key.algorithm.namedCurve }, true, ['deriveBits'])).privateKey; | ||
}; | ||
exports.publicJwkToEphemeralKey = async (jwk) => { | ||
exports.generateEpk = generateEpk; | ||
const publicJwkToEphemeralKey = async (jwk) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
return webcrypto_js_1.default.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: jwk.crv }, true, []); | ||
}; | ||
exports.publicJwkToEphemeralKey = publicJwkToEphemeralKey; | ||
const curves = ['P-256', 'P-384', 'P-521']; | ||
exports.ecdhAllowed = (key) => curves.includes(key.algorithm.namedCurve); | ||
const ecdhAllowed = (key) => curves.includes(key.algorithm.namedCurve); | ||
exports.ecdhAllowed = ecdhAllowed; |
@@ -9,6 +9,4 @@ "use strict"; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'encrypt', | ||
]); | ||
const macKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
const encKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.subarray(keySize >> 3), 'AES-CBC', false, ['encrypt']); | ||
const macKey = await webcrypto_js_1.default.subtle.importKey('raw', cek.subarray(0, keySize >> 3), { | ||
hash: `SHA-${keySize << 1}`, | ||
@@ -15,0 +13,0 @@ name: 'HMAC', |
@@ -12,10 +12,8 @@ "use strict"; | ||
const fetch = async (url, timeout, options) => { | ||
if (!(url.protocol in protocols)) { | ||
if (protocols[url.protocol] === undefined) { | ||
throw new TypeError('Unsupported URL protocol.'); | ||
} | ||
return new Promise((resolve, reject) => { | ||
protocols[url.protocol](url, { | ||
...options, | ||
timeout, | ||
}, async (response) => { | ||
const { agent } = options; | ||
protocols[url.protocol](url, { agent, timeout }, async (response) => { | ||
if (response.statusCode !== 200) { | ||
@@ -22,0 +20,0 @@ reject(new errors_js_1.JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response')); |
@@ -10,3 +10,3 @@ "use strict"; | ||
const webcrypto_js_1 = require("./webcrypto.js"); | ||
exports.encrypt = async (alg, key, cek, p2c = Math.floor(Math.random() * 2049) + 2048, p2s = random_js_1.default(new Uint8Array(16))) => { | ||
const encrypt = async (alg, key, cek, p2c = Math.floor(Math.random() * 2049) + 2048, p2s = random_js_1.default(new Uint8Array(16))) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -46,3 +46,4 @@ check_p2s_js_1.default(p2s); | ||
}; | ||
exports.decrypt = async (alg, key, encryptedKey, p2c, p2s) => { | ||
exports.encrypt = encrypt; | ||
const decrypt = async (alg, key, encryptedKey, p2c, p2s) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -81,1 +82,2 @@ check_p2s_js_1.default(p2s); | ||
}; | ||
exports.decrypt = decrypt; |
@@ -8,3 +8,3 @@ "use strict"; | ||
const check_key_length_js_1 = require("./check_key_length.js"); | ||
exports.encrypt = async (alg, key, cek) => { | ||
const encrypt = async (alg, key, cek) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -21,3 +21,4 @@ check_key_length_js_1.default(alg, key); | ||
}; | ||
exports.decrypt = async (alg, key, encryptedKey) => { | ||
exports.encrypt = encrypt; | ||
const decrypt = async (alg, key, encryptedKey) => { | ||
webcrypto_js_1.ensureSecureContext(); | ||
@@ -34,1 +35,2 @@ check_key_length_js_1.default(alg, key); | ||
}; | ||
exports.decrypt = decrypt; |
@@ -5,3 +5,3 @@ "use strict"; | ||
const crypto = require("crypto"); | ||
if (!('webcrypto' in crypto)) { | ||
if (crypto.webcrypto === undefined) { | ||
throw new Error('Node.js crypto.webcrypto is not available in your runtime'); | ||
@@ -8,0 +8,0 @@ } |
@@ -8,7 +8,9 @@ "use strict"; | ||
const deflateRaw = util_1.promisify(zlib_1.deflateRaw); | ||
exports.inflate = async (input) => { | ||
const inflate = async (input) => { | ||
return inflateRaw(input); | ||
}; | ||
exports.deflate = async (input) => { | ||
exports.inflate = inflate; | ||
const deflate = async (input) => { | ||
return deflateRaw(input); | ||
}; | ||
exports.deflate = deflate; |
import decrypt from '../flattened/decrypt.js'; | ||
import { JWEInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWEInvalid('Compact JWE must be a string or Uint8Array'); | ||
} | ||
@@ -12,9 +16,9 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split('.'); | ||
const decrypted = await decrypt({ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined), | ||
iv: (iv || undefined), | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined), | ||
encrypted_key: encryptedKey || undefined, | ||
}, key, options); | ||
return { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader }; | ||
} |
@@ -21,27 +21,27 @@ import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new JWEInvalid('JOSE Header missing'); | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new JWEInvalid('JWE Initialization Vector missing or incorrect type'); | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new JWEInvalid('JWE Ciphertext missing or incorrect type'); | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new JWEInvalid('JWE Authentication Tag missing or incorrect type'); | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new JWEInvalid('JWE Protected Header incorrect type'); | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new JWEInvalid('JWE Encrypted Key incorrect type'); | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new JWEInvalid('JWE AAD incorrect type'); | ||
} | ||
if ('header' in jwe && !isObject(jwe.header)) { | ||
if (jwe.header !== undefined && !isObject(jwe.header)) { | ||
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type'); | ||
} | ||
if ('unprotected' in jwe && !isObject(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { | ||
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type'); | ||
@@ -63,3 +63,3 @@ } | ||
checkCrit(parsedProt, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -73,6 +73,6 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header'); | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header'); | ||
@@ -89,3 +89,3 @@ } | ||
let encryptedKey; | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url(jwe.encrypted_key); | ||
@@ -110,3 +110,3 @@ } | ||
let additionalData; | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)); | ||
@@ -122,12 +122,12 @@ } | ||
const result = { plaintext }; | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url(jwe.aad); | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected; | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header; | ||
@@ -134,0 +134,0 @@ } |
@@ -76,3 +76,3 @@ import ivFactory from '../../lib/iv.js'; | ||
checkCrit(this._protectedHeader, joseHeader); | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -86,7 +86,7 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected'); | ||
const { alg, enc } = joseHeader; | ||
if (!alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
if (!enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing'); | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -93,0 +93,0 @@ let encryptedKey; |
@@ -24,3 +24,3 @@ import { decode as base64url } from '../runtime/base64url.js'; | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is unsupported'); | ||
@@ -27,0 +27,0 @@ } |
@@ -24,2 +24,3 @@ import parseJWK from '../jwk/parse.js'; | ||
this._url = new URL(url.href); | ||
this._options = { agent: options?.agent }; | ||
this._timeoutDuration = | ||
@@ -91,3 +92,3 @@ typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000; | ||
const cached = this._cached.get(jwk); | ||
if (!(protectedHeader.alg in cached)) { | ||
if (cached[protectedHeader.alg] === undefined) { | ||
const keyObject = (await parseJWK({ ...jwk, alg: protectedHeader.alg })); | ||
@@ -103,7 +104,6 @@ if (keyObject.type !== 'public') { | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration, this._options) | ||
.then((json) => { | ||
if (typeof json !== 'object' || | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -110,0 +110,0 @@ json.keys.some((key) => typeof key !== 'object' || !key)) { |
import verify from '../flattened/verify.js'; | ||
import { JWSInvalid } from '../../util/errors.js'; | ||
import { decoder } from '../../lib/buffer_utils.js'; | ||
export default 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'); | ||
throw new JWSInvalid('Compact JWS must be a string or Uint8Array'); | ||
} | ||
@@ -11,4 +15,8 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.'); | ||
} | ||
const verified = await verify({ payload, protected: protectedHeader, signature }, key, options); | ||
const verified = await verify({ | ||
payload: (payload || undefined), | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined), | ||
}, key, options); | ||
return { payload: verified.payload, protectedHeader: verified.protectedHeader }; | ||
} |
@@ -47,4 +47,4 @@ import isDisjoint from '../../lib/is_disjoint.js'; | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -51,0 +51,0 @@ checkKeyType(alg, key); |
@@ -16,15 +16,15 @@ import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js'; | ||
} | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
if (jws.protected === undefined && jws.header === undefined) { | ||
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members'); | ||
} | ||
if ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new JWSInvalid('JWS Protected Header incorrect type'); | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new JWSInvalid('JWS Payload missing'); | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new JWSInvalid('JWS Signature missing or incorrect type'); | ||
} | ||
if ('header' in jws && !isObject(jws.header)) { | ||
if (jws.header !== undefined && !isObject(jws.header)) { | ||
throw new JWSInvalid('JWS Unprotected Header incorrect type'); | ||
@@ -53,4 +53,4 @@ } | ||
const { alg } = joseHeader; | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header'); | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); | ||
} | ||
@@ -90,6 +90,6 @@ const algorithms = options && checkAlgOption(options.algorithms); | ||
const result = { payload }; | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt; | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header; | ||
@@ -96,0 +96,0 @@ } |
@@ -8,9 +8,9 @@ import decrypt from '../jwe/compact/decrypt.js'; | ||
const { protectedHeader } = decrypted; | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch', 'iss', 'mismatch'); | ||
} | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch', 'sub', 'mismatch'); | ||
} | ||
if ('aud' in protectedHeader && | ||
if (protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud)) { | ||
@@ -17,0 +17,0 @@ throw new JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch', 'aud', 'mismatch'); |
@@ -15,3 +15,3 @@ import { JOSENotSupported, JWEInvalid } from '../util/errors.js'; | ||
function assertHeaderParameter(joseHeader, parameter, name) { | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new JWEInvalid(`JOSE Header ${name} (${parameter}) missing`); | ||
@@ -37,3 +37,3 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -43,5 +43,5 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk); | ||
let partyVInfo; | ||
if (joseHeader.apu) | ||
if (joseHeader.apu !== undefined) | ||
partyUInfo = base64url(joseHeader.apu); | ||
if (joseHeader.apv) | ||
if (joseHeader.apv !== undefined) | ||
partyVInfo = base64url(joseHeader.apv); | ||
@@ -91,3 +91,3 @@ const sharedSecret = await ECDH.deriveKey(ephemeralKey, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, parseInt(alg.substr(-5, 3), 10) || cekLengths.get(joseHeader.enc), partyUInfo, partyVInfo); | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -94,0 +94,0 @@ } |
@@ -25,3 +25,3 @@ import cekFactory, { bitLengths as cekLengths } from '../lib/cek.js'; | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime'); | ||
throw new JOSENotSupported('ECDH-ES with the provided key is not allowed or not supported by your javascript runtime'); | ||
} | ||
@@ -80,3 +80,3 @@ const { apu, apv } = providedParameters; | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`); | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value'); | ||
} | ||
@@ -83,0 +83,0 @@ } |
@@ -57,11 +57,11 @@ import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js'; | ||
const now = epoch(currentDate || new Date()); | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid'); | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed'); | ||
} | ||
} | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -74,3 +74,3 @@ throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid'); | ||
} | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -77,0 +77,0 @@ throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid'); |
@@ -10,3 +10,3 @@ const minute = 60; | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`); | ||
throw new TypeError('invalid time period format'); | ||
} | ||
@@ -13,0 +13,0 @@ const value = parseFloat(matched[1]); |
import { JOSENotSupported } from '../util/errors.js'; | ||
const isString = (input) => typeof input !== 'string' || input.length === 0; | ||
function validateCrit(Err, supported, protectedHeader, joseHeader) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]); | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set(); | ||
} | ||
if (!Array.isArray(protectedHeader.crit) || | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString)) { | ||
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'); | ||
@@ -19,6 +18,6 @@ } | ||
} | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`); | ||
} | ||
else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); | ||
@@ -25,0 +24,0 @@ } |
@@ -27,4 +27,5 @@ import { JWEInvalid, JOSENotSupported } from '../util/errors.js'; | ||
} | ||
if ('algorithm' in cek) { | ||
if (cek.algorithm.length !== expected) { | ||
if (cek.algorithm !== undefined) { | ||
const { length } = cek.algorithm; | ||
if (length !== expected) { | ||
throw new JWEInvalid('Invalid Content Encryption Key length'); | ||
@@ -31,0 +32,0 @@ } |
export default (alg, key) => { | ||
if (alg.startsWith('HS')) { | ||
const bitlen = parseInt(alg.substr(-3), 10); | ||
if (!('length' in key.algorithm) || key.algorithm.length < bitlen) { | ||
const { length } = key.algorithm; | ||
if (typeof length !== 'number' || length < bitlen) { | ||
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`); | ||
@@ -9,4 +10,4 @@ } | ||
if (alg.startsWith('RS') || alg.startsWith('PS')) { | ||
if (!('modulusLength' in key.algorithm) || | ||
key.algorithm.modulusLength < 2048) { | ||
const { modulusLength } = key.algorithm; | ||
if (typeof modulusLength !== 'number' || modulusLength < 2048) { | ||
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); | ||
@@ -13,0 +14,0 @@ } |
@@ -9,6 +9,4 @@ import { concat, uint64be } from '../lib/buffer_utils.js'; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await crypto.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'decrypt', | ||
]); | ||
const macKey = await crypto.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
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}`, | ||
@@ -15,0 +13,0 @@ name: 'HMAC', |
@@ -7,6 +7,4 @@ import { concat, uint64be } from '../lib/buffer_utils.js'; | ||
const keySize = parseInt(enc.substr(1, 3), 10); | ||
const encKey = await crypto.subtle.importKey('raw', cek.slice(keySize >> 3), 'AES-CBC', false, [ | ||
'encrypt', | ||
]); | ||
const macKey = await crypto.subtle.importKey('raw', cek.slice(0, keySize >> 3), { | ||
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}`, | ||
@@ -13,0 +11,0 @@ name: 'HMAC', |
@@ -10,10 +10,8 @@ import { get as http } from 'http'; | ||
const fetch = async (url, timeout, options) => { | ||
if (!(url.protocol in protocols)) { | ||
if (protocols[url.protocol] === undefined) { | ||
throw new TypeError('Unsupported URL protocol.'); | ||
} | ||
return new Promise((resolve, reject) => { | ||
protocols[url.protocol](url, { | ||
...options, | ||
timeout, | ||
}, async (response) => { | ||
const { agent } = options; | ||
protocols[url.protocol](url, { agent, timeout }, async (response) => { | ||
if (response.statusCode !== 200) { | ||
@@ -20,0 +18,0 @@ reject(new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response')); |
import * as crypto from 'crypto'; | ||
if (!('webcrypto' in crypto)) { | ||
if (crypto.webcrypto === undefined) { | ||
throw new Error('Node.js crypto.webcrypto is not available in your runtime'); | ||
@@ -4,0 +4,0 @@ } |
235
package.json
{ | ||
"name": "jose", | ||
"version": "3.0.2", | ||
"version": "3.1.0", | ||
"description": "JSON Web Almost Everything - JWA, JWS, JWE, JWK, JWT, JWKS with no dependencies", | ||
@@ -47,177 +47,9 @@ "keywords": [ | ||
"imports": { | ||
"#dist/jwe/compact/decrypt": { | ||
"import": "./dist/node/esm/jwe/compact/decrypt.js", | ||
"require": "./dist/node/cjs/jwe/compact/decrypt.js" | ||
"#dist/*": { | ||
"import": "./dist/node/esm/*.js", | ||
"require": "./dist/node/cjs/*.js" | ||
}, | ||
"#dist/webcrypto/jwe/compact/decrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwe/compact/decrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwe/compact/decrypt.js" | ||
}, | ||
"#dist/jwe/compact/encrypt": { | ||
"import": "./dist/node/esm/jwe/compact/encrypt.js", | ||
"require": "./dist/node/cjs/jwe/compact/encrypt.js" | ||
}, | ||
"#dist/webcrypto/jwe/compact/encrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwe/compact/encrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwe/compact/encrypt.js" | ||
}, | ||
"#dist/jwe/flattened/decrypt": { | ||
"import": "./dist/node/esm/jwe/flattened/decrypt.js", | ||
"require": "./dist/node/cjs/jwe/flattened/decrypt.js" | ||
}, | ||
"#dist/webcrypto/jwe/flattened/decrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwe/flattened/decrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwe/flattened/decrypt.js" | ||
}, | ||
"#dist/jwe/flattened/encrypt": { | ||
"import": "./dist/node/esm/jwe/flattened/encrypt.js", | ||
"require": "./dist/node/cjs/jwe/flattened/encrypt.js" | ||
}, | ||
"#dist/webcrypto/jwe/flattened/encrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwe/flattened/encrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwe/flattened/encrypt.js" | ||
}, | ||
"#dist/jwk/embedded": { | ||
"import": "./dist/node/esm/jwk/embedded.js", | ||
"require": "./dist/node/cjs/jwk/embedded.js" | ||
}, | ||
"#dist/webcrypto/jwk/embedded": { | ||
"import": "./dist/node/webcrypto/esm/jwk/embedded.js", | ||
"require": "./dist/node/webcrypto/cjs/jwk/embedded.js" | ||
}, | ||
"#dist/jwk/parse": { | ||
"import": "./dist/node/esm/jwk/parse.js", | ||
"require": "./dist/node/cjs/jwk/parse.js" | ||
}, | ||
"#dist/webcrypto/jwk/parse": { | ||
"import": "./dist/node/webcrypto/esm/jwk/parse.js", | ||
"require": "./dist/node/webcrypto/cjs/jwk/parse.js" | ||
}, | ||
"#dist/jwk/thumbprint": { | ||
"import": "./dist/node/esm/jwk/thumbprint.js", | ||
"require": "./dist/node/cjs/jwk/thumbprint.js" | ||
}, | ||
"#dist/webcrypto/jwk/thumbprint": { | ||
"import": "./dist/node/webcrypto/esm/jwk/thumbprint.js", | ||
"require": "./dist/node/webcrypto/cjs/jwk/thumbprint.js" | ||
}, | ||
"#dist/jwks/remote": { | ||
"import": "./dist/node/esm/jwks/remote.js", | ||
"require": "./dist/node/cjs/jwks/remote.js" | ||
}, | ||
"#dist/webcrypto/jwks/remote": { | ||
"import": "./dist/node/webcrypto/esm/jwks/remote.js", | ||
"require": "./dist/node/webcrypto/cjs/jwks/remote.js" | ||
}, | ||
"#dist/jws/compact/sign": { | ||
"import": "./dist/node/esm/jws/compact/sign.js", | ||
"require": "./dist/node/cjs/jws/compact/sign.js" | ||
}, | ||
"#dist/webcrypto/jws/compact/sign": { | ||
"import": "./dist/node/webcrypto/esm/jws/compact/sign.js", | ||
"require": "./dist/node/webcrypto/cjs/jws/compact/sign.js" | ||
}, | ||
"#dist/jws/compact/verify": { | ||
"import": "./dist/node/esm/jws/compact/verify.js", | ||
"require": "./dist/node/cjs/jws/compact/verify.js" | ||
}, | ||
"#dist/webcrypto/jws/compact/verify": { | ||
"import": "./dist/node/webcrypto/esm/jws/compact/verify.js", | ||
"require": "./dist/node/webcrypto/cjs/jws/compact/verify.js" | ||
}, | ||
"#dist/jws/flattened/sign": { | ||
"import": "./dist/node/esm/jws/flattened/sign.js", | ||
"require": "./dist/node/cjs/jws/flattened/sign.js" | ||
}, | ||
"#dist/webcrypto/jws/flattened/sign": { | ||
"import": "./dist/node/webcrypto/esm/jws/flattened/sign.js", | ||
"require": "./dist/node/webcrypto/cjs/jws/flattened/sign.js" | ||
}, | ||
"#dist/jws/flattened/verify": { | ||
"import": "./dist/node/esm/jws/flattened/verify.js", | ||
"require": "./dist/node/cjs/jws/flattened/verify.js" | ||
}, | ||
"#dist/webcrypto/jws/flattened/verify": { | ||
"import": "./dist/node/webcrypto/esm/jws/flattened/verify.js", | ||
"require": "./dist/node/webcrypto/cjs/jws/flattened/verify.js" | ||
}, | ||
"#dist/jwt/decrypt": { | ||
"import": "./dist/node/esm/jwt/decrypt.js", | ||
"require": "./dist/node/cjs/jwt/decrypt.js" | ||
}, | ||
"#dist/webcrypto/jwt/decrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwt/decrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwt/decrypt.js" | ||
}, | ||
"#dist/jwt/encrypt": { | ||
"import": "./dist/node/esm/jwt/encrypt.js", | ||
"require": "./dist/node/cjs/jwt/encrypt.js" | ||
}, | ||
"#dist/webcrypto/jwt/encrypt": { | ||
"import": "./dist/node/webcrypto/esm/jwt/encrypt.js", | ||
"require": "./dist/node/webcrypto/cjs/jwt/encrypt.js" | ||
}, | ||
"#dist/jwt/sign": { | ||
"import": "./dist/node/esm/jwt/sign.js", | ||
"require": "./dist/node/cjs/jwt/sign.js" | ||
}, | ||
"#dist/webcrypto/jwt/sign": { | ||
"import": "./dist/node/webcrypto/esm/jwt/sign.js", | ||
"require": "./dist/node/webcrypto/cjs/jwt/sign.js" | ||
}, | ||
"#dist/jwt/unsecured": { | ||
"import": "./dist/node/esm/jwt/unsecured.js", | ||
"require": "./dist/node/cjs/jwt/unsecured.js" | ||
}, | ||
"#dist/webcrypto/jwt/unsecured": { | ||
"import": "./dist/node/webcrypto/esm/jwt/unsecured.js", | ||
"require": "./dist/node/webcrypto/cjs/jwt/unsecured.js" | ||
}, | ||
"#dist/jwt/verify": { | ||
"import": "./dist/node/esm/jwt/verify.js", | ||
"require": "./dist/node/cjs/jwt/verify.js" | ||
}, | ||
"#dist/webcrypto/jwt/verify": { | ||
"import": "./dist/node/webcrypto/esm/jwt/verify.js", | ||
"require": "./dist/node/webcrypto/cjs/jwt/verify.js" | ||
}, | ||
"#dist/util/base64url": { | ||
"import": "./dist/node/esm/util/base64url.js", | ||
"require": "./dist/node/cjs/util/base64url.js" | ||
}, | ||
"#dist/webcrypto/util/base64url": { | ||
"import": "./dist/node/webcrypto/esm/util/base64url.js", | ||
"require": "./dist/node/webcrypto/cjs/util/base64url.js" | ||
}, | ||
"#dist/util/errors": { | ||
"import": "./dist/node/esm/util/errors.js", | ||
"require": "./dist/node/cjs/util/errors.js" | ||
}, | ||
"#dist/webcrypto/util/errors": { | ||
"import": "./dist/node/webcrypto/esm/util/errors.js", | ||
"require": "./dist/node/webcrypto/cjs/util/errors.js" | ||
}, | ||
"#dist/util/generate_key_pair": { | ||
"import": "./dist/node/esm/util/generate_key_pair.js", | ||
"require": "./dist/node/cjs/util/generate_key_pair.js" | ||
}, | ||
"#dist/webcrypto/util/generate_key_pair": { | ||
"import": "./dist/node/webcrypto/esm/util/generate_key_pair.js", | ||
"require": "./dist/node/webcrypto/cjs/util/generate_key_pair.js" | ||
}, | ||
"#dist/util/generate_secret": { | ||
"import": "./dist/node/esm/util/generate_secret.js", | ||
"require": "./dist/node/cjs/util/generate_secret.js" | ||
}, | ||
"#dist/webcrypto/util/generate_secret": { | ||
"import": "./dist/node/webcrypto/esm/util/generate_secret.js", | ||
"require": "./dist/node/webcrypto/cjs/util/generate_secret.js" | ||
}, | ||
"#dist/util/random": { | ||
"import": "./dist/node/esm/util/random.js", | ||
"require": "./dist/node/cjs/util/random.js" | ||
}, | ||
"#dist/webcrypto/util/random": { | ||
"import": "./dist/node/webcrypto/esm/util/random.js", | ||
"require": "./dist/node/webcrypto/cjs/util/random.js" | ||
"#dist/webcrypto/*": { | ||
"import": "./dist/node/webcrypto/esm/*.js", | ||
"require": "./dist/node/webcrypto/cjs/*.js" | ||
} | ||
@@ -271,2 +103,11 @@ }, | ||
}, | ||
"./jwk/from_key_like": { | ||
"browser": "./dist/browser/jwk/from_key_like.js", | ||
"import": "./dist/node/esm/jwk/from_key_like.js", | ||
"require": "./dist/node/cjs/jwk/from_key_like.js" | ||
}, | ||
"./webcrypto/jwk/from_key_like": { | ||
"import": "./dist/node/webcrypto/esm/jwk/from_key_like.js", | ||
"require": "./dist/node/webcrypto/cjs/jwk/from_key_like.js" | ||
}, | ||
"./jwk/parse": { | ||
@@ -438,7 +279,13 @@ "browser": "./dist/browser/jwk/parse.js", | ||
"build-all": "run-s clear build:*", | ||
"build-fast-all": "run-s clear build-fast:*", | ||
"build-fast:browser": "npm run-script runtime-browser && npm run-script -s esbuild-find | xargs -0 esbuild --target=es2018 --outdir=dist/browser --format=esm && echo '{\"type\": \"module\"}'> dist/browser/package.json", | ||
"build-fast:node-cjs": "npm run-script runtime-node && npm run-script -s esbuild-find | xargs -0 esbuild --platform=node --target=node12 --outdir=dist/node/cjs --format=cjs", | ||
"build-fast:node-esm": "npm run-script runtime-node && npm run-script -s esbuild-find | xargs -0 esbuild --platform=node --target=node12 --outdir=dist/node/esm --format=esm && echo '{\"type\": \"module\"}'> dist/node/esm/package.json", | ||
"build-fast:node-webcrypto-cjs": "npm run-script runtime-node-webcrypto && npm run-script -s esbuild-find | xargs -0 esbuild --platform=node --target=esnext --outdir=dist/node/webcrypto/cjs --format=cjs", | ||
"build-fast:node-webcrypto-esm": "npm run-script runtime-node-webcrypto && npm run-script -s esbuild-find | xargs -0 esbuild --platform=node --target=esnext --outdir=dist/node/webcrypto/esm --format=esm && echo '{\"type\": \"module\"}'> dist/node/webcrypto/esm/package.json", | ||
"build:browser": "run-s runtime-browser lint 'build -- -p ./tsconfig/browser.json' && echo '{\"type\": \"module\"}'> dist/browser/package.json", | ||
"build:node-cjs": "run-s runtime-node lint 'build -- -p ./tsconfig/node-cjs.json'", | ||
"build:node-esm": "run-s runtime-node lint 'build -- -p ./tsconfig/node-esm.json' && echo '{\"type\": \"module\"}'> dist/node/esm/package.json", | ||
"build:node-webcrypto-cjs": "run-s runtime-node-webcrypto && run-s 'build -- -p ./tsconfig/node-webcrypto-cjs.json'", | ||
"build:node-webcrypto-esm": "run-s runtime-node-webcrypto && run-s 'build -- -p ./tsconfig/node-webcrypto-esm.json' && echo '{\"type\": \"module\"}'> dist/node/webcrypto/esm/package.json", | ||
"build:node-webcrypto-cjs": "run-s runtime-node-webcrypto lint 'build -- -p ./tsconfig/node-webcrypto-cjs.json'", | ||
"build:node-webcrypto-esm": "run-s runtime-node-webcrypto lint 'build -- -p ./tsconfig/node-webcrypto-esm.json' && echo '{\"type\": \"module\"}'> dist/node/webcrypto/esm/package.json", | ||
"clear": "rm -rf dist", | ||
@@ -448,3 +295,4 @@ "coverage": "npm run-script runtime-node && c8 npm run-script test", | ||
"docs": "run-s docs:*", | ||
"docs:generate": "npx typedoc --disableOutputCheck --excludeNotExported --excludePrivate --excludeProtected --gitRevision main --readme none --listInvalidSymbolLinks --plugin typedoc-plugin-markdown --out docs --includeDeclarations --excludeExternals --tsconfig ./tsconfig/browser.json --mode modules src/types.d.ts src/jwt/*.ts src/jwe/**/*.ts src/jws/**/*.ts src/jwk/*.ts src/jwks/*.ts src/util/*.ts --hideProjectName --hideGenerator --allReflectionsHaveOwnDocument --hideBreadcrumbs", | ||
"docs:generate": "typedoc --disableOutputCheck --excludeNotExported --excludePrivate --excludeProtected --gitRevision main --readme none --listInvalidSymbolLinks --plugin typedoc-plugin-markdown --out docs --includeDeclarations --excludeExternals --tsconfig ./tsconfig/browser.json --mode modules src/types.d.ts src/jwt/*.ts src/jwe/**/*.ts src/jws/**/*.ts src/jwk/*.ts src/jwks/*.ts src/util/*.ts --hideProjectName --hideGenerator --allReflectionsHaveOwnDocument --hideBreadcrumbs", | ||
"esbuild-find": "find src -type f -name '*.ts' -not -path '*/runtime/*/*' -not -name '*.d.ts' -print0", | ||
"lint": "eslint --config ./src/.eslintrc.json ./src", | ||
@@ -459,5 +307,5 @@ "runtime-browser": "run-s runtime:clear runtime:browser:* runtime:refs", | ||
"runtime:refs": "run-s -s runtime:find | xargs -0 sed -i '' -e \"s/'\\.\\.\\//'\\.\\//g\" -e \"s/'\\.\\/\\.\\./'../g\"", | ||
"test": "npm run-script test-rollup && ava", | ||
"test-browser": "webpack && karma start", | ||
"test-rollup": "rm -rf test/cjs && find test -type f -name '*.mjs' -print0 | xargs -0 rollup --silent --no-interop --preserveModules --format cjs --dir test/cjs", | ||
"test": "npm run-script test-cjs && ava", | ||
"test-browser": "find test-browser -type f -name '*.js' -print0 | xargs -0 npx esbuild --outdir=dist-browser-tests --bundle && karma start", | ||
"test-cjs": "rm -rf test/cjs && find test -type f -name '*.mjs' -print0 | xargs -0 npx esbuild --target=esnext --outdir=test/cjs --format=cjs", | ||
"test-webcrypto": "WEBCRYPTO=true npm test" | ||
@@ -476,28 +324,27 @@ }, | ||
"devDependencies": { | ||
"@types/node": "^14.14.2", | ||
"@typescript-eslint/eslint-plugin": "^4.5.0", | ||
"@types/node": "^14.14.9", | ||
"@typescript-eslint/eslint-plugin": "^4.8.1", | ||
"ava": "^3.13.0", | ||
"bowser": "^2.11.0", | ||
"c8": "^7.3.5", | ||
"eslint": "^7.12.0", | ||
"eslint-config-airbnb-base": "^14.2.0", | ||
"esbuild": "0.8.12", | ||
"eslint": "^7.14.0", | ||
"eslint-config-airbnb-base": "^14.2.1", | ||
"eslint-config-airbnb-typescript": "^12.0.0", | ||
"eslint-config-prettier": "^6.14.0", | ||
"eslint-plugin-import": "^2.22.1", | ||
"eslint-plugin-jsdoc": "^30.7.3", | ||
"eslint-plugin-jsdoc": "^30.7.8", | ||
"glob": "^7.1.6", | ||
"karma": "^5.2.3", | ||
"karma-browserstack-launcher": "^1.6.0", | ||
"karma-qunit": "^4.1.1", | ||
"nock": "^13.0.4", | ||
"karma-brief-reporter": "0.2.1", | ||
"karma-browserstack-launcher": "1.6.0", | ||
"karma-qunit": "4.1.1", | ||
"nock": "^13.0.5", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^2.1.2", | ||
"prettier": "^2.2.0", | ||
"qunit": "^2.12.0", | ||
"rollup": "^2.33.1", | ||
"timekeeper": "^2.2.0", | ||
"typedoc": "0.19.2", | ||
"typedoc-plugin-markdown": "3.0.11", | ||
"typescript": "~4.0.3", | ||
"webpack": "^5.4.0", | ||
"webpack-cli": "^4.2.0" | ||
"typescript": "~4.0.5" | ||
}, | ||
@@ -504,0 +351,0 @@ "c8": { |
@@ -47,3 +47,4 @@ # jose | ||
- JSON Web Key (JWK) | ||
- [Parsing & Conversion](docs/functions/_jwk_parse_.parsejwk.md#readme) | ||
- [Parsing (JWK to KeyLike)](docs/functions/_jwk_parse_.parsejwk.md#readme) | ||
- [Conversion (KeyLike to JWK)](docs/functions/_jwk_from_key_like_.fromkeylike.md#readme) | ||
- [Thumbprints](docs/functions/_jwk_thumbprint_.calculatethumbprint.md#readme) | ||
@@ -59,2 +60,6 @@ - [EmbeddedJWK](docs/functions/_jwk_embedded_.embeddedjwk.md#readme) | ||
## Examples | ||
A continuously growing list of examples is available in the [tracker](https://github.com/panva/jose/issues?q=label%3Aexample+label%3Av3.x). | ||
## JOSE Support Matrix | ||
@@ -215,2 +220,11 @@ | ||
#### "Module '"crypto"' has no exported member '...'" | ||
Update @types/node as your project's development dependency | ||
``` | ||
npm uninstall @types/node | ||
npm install --save-dev @types/node | ||
``` | ||
#### Why? Just. Why? | ||
@@ -217,0 +231,0 @@ |
import decrypt from '../flattened/decrypt.js' | ||
import { JWEInvalid } from '../../util/errors.js' | ||
import { decoder } from '../../lib/buffer_utils.js' | ||
import type { | ||
@@ -63,8 +64,13 @@ KeyLike, | ||
export default async function compactDecrypt( | ||
jwe: string, | ||
jwe: string | Uint8Array, | ||
key: KeyLike | CompactDecryptGetKey, | ||
options?: DecryptOptions, | ||
): Promise<CompactDecryptResult> { | ||
if (jwe instanceof Uint8Array) { | ||
// eslint-disable-next-line no-param-reassign | ||
jwe = decoder.decode(jwe) | ||
} | ||
if (typeof jwe !== 'string') { | ||
throw new JWEInvalid('Compact JWE must be a string') | ||
throw new JWEInvalid('Compact JWE must be a string or Uint8Array') | ||
} | ||
@@ -81,7 +87,7 @@ const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length } = jwe.split( | ||
{ | ||
ciphertext, | ||
iv, | ||
protected: protectedHeader, | ||
tag, | ||
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined), | ||
ciphertext: (ciphertext || undefined) as string, | ||
iv: (iv || undefined) as string, | ||
protected: protectedHeader || undefined, | ||
tag: (tag || undefined) as string, | ||
encrypted_key: encryptedKey || undefined, | ||
}, | ||
@@ -88,0 +94,0 @@ key as Parameters<typeof decrypt>[1], |
@@ -101,35 +101,35 @@ import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js' | ||
if (!('protected' in jwe) && !('header' in jwe) && !('unprotected' in jwe)) { | ||
if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { | ||
throw new JWEInvalid('JOSE Header missing') | ||
} | ||
if (!('iv' in jwe) || typeof jwe.iv !== 'string') { | ||
if (typeof jwe.iv !== 'string') { | ||
throw new JWEInvalid('JWE Initialization Vector missing or incorrect type') | ||
} | ||
if (!('ciphertext' in jwe) || typeof jwe.ciphertext !== 'string') { | ||
if (typeof jwe.ciphertext !== 'string') { | ||
throw new JWEInvalid('JWE Ciphertext missing or incorrect type') | ||
} | ||
if (!('tag' in jwe) || typeof jwe.tag !== 'string') { | ||
if (typeof jwe.tag !== 'string') { | ||
throw new JWEInvalid('JWE Authentication Tag missing or incorrect type') | ||
} | ||
if ('protected' in jwe && typeof jwe.protected !== 'string') { | ||
if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { | ||
throw new JWEInvalid('JWE Protected Header incorrect type') | ||
} | ||
if ('encrypted_key' in jwe && typeof jwe.encrypted_key !== 'string') { | ||
if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { | ||
throw new JWEInvalid('JWE Encrypted Key incorrect type') | ||
} | ||
if ('aad' in jwe && typeof jwe.aad !== 'string') { | ||
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { | ||
throw new JWEInvalid('JWE AAD incorrect type') | ||
} | ||
if ('header' in jwe && !isObject(jwe.header)) { | ||
if (jwe.header !== undefined && !isObject(jwe.header)) { | ||
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type') | ||
} | ||
if ('unprotected' in jwe && !isObject(jwe.unprotected)) { | ||
if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { | ||
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type') | ||
@@ -157,3 +157,3 @@ } | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!parsedProt || !parsedProt.zip) { | ||
@@ -172,7 +172,7 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected') | ||
if (!alg) { | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header') | ||
} | ||
if (!enc) { | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header') | ||
@@ -193,3 +193,3 @@ } | ||
let encryptedKey!: Uint8Array | ||
if ('encrypted_key' in jwe) { | ||
if (jwe.encrypted_key !== undefined) { | ||
encryptedKey = base64url(jwe.encrypted_key!) | ||
@@ -226,3 +226,3 @@ } | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
additionalData = concat(protectedHeader, encoder.encode('.'), encoder.encode(jwe.aad)) | ||
@@ -241,15 +241,15 @@ } else { | ||
if ('protected' in jwe) { | ||
if (jwe.protected !== undefined) { | ||
result.protectedHeader = parsedProt | ||
} | ||
if ('aad' in jwe) { | ||
if (jwe.aad !== undefined) { | ||
result.additionalAuthenticatedData = base64url(jwe.aad!) | ||
} | ||
if ('unprotected' in jwe) { | ||
if (jwe.unprotected !== undefined) { | ||
result.sharedUnprotectedHeader = jwe.unprotected | ||
} | ||
if ('header' in jwe) { | ||
if (jwe.header !== undefined) { | ||
result.unprotectedHeader = jwe.header | ||
@@ -256,0 +256,0 @@ } |
@@ -210,3 +210,3 @@ import type { | ||
if ('zip' in joseHeader) { | ||
if (joseHeader.zip !== undefined) { | ||
if (!this._protectedHeader || !this._protectedHeader.zip) { | ||
@@ -225,8 +225,8 @@ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected') | ||
if (!alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing') | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid') | ||
} | ||
if (!enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing') | ||
if (typeof enc !== 'string' || !enc) { | ||
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid') | ||
} | ||
@@ -233,0 +233,0 @@ |
@@ -8,6 +8,6 @@ import { decode as base64url } from '../runtime/base64url.js' | ||
/** | ||
* Converts a JWK to a runtime-specific key representation. Either JWK "alg" (Algorithm) | ||
* Parameter must be present or the optional "alg" argument. When running on a platform | ||
* 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`. | ||
* Converts 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 platform 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`. | ||
* | ||
@@ -82,3 +82,3 @@ * @param jwk JSON Web Key. | ||
case 'RSA': | ||
if ('oth' in jwk) { | ||
if (jwk.oth !== undefined) { | ||
throw new JOSENotSupported( | ||
@@ -85,0 +85,0 @@ 'RSA JWK "oth" (Other Primes Info) Parameter value is unsupported', |
import type { KeyObject } from 'crypto' | ||
import type { Agent as HttpAgent } from 'http' | ||
import type { Agent as HttpsAgent } from 'https' | ||
@@ -46,2 +48,10 @@ import type { JWSHeaderParameters, JWK, FlattenedJWSInput, GetKeyFunction } from '../types.d' | ||
cooldownDuration?: number | ||
/** | ||
* An instance of http.Agent or https.Agent to pass to the http.get or | ||
* https.get method 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?: HttpAgent | HttpsAgent | ||
} | ||
@@ -64,2 +74,4 @@ | ||
private _options: Pick<RemoteJWKSetOptions, 'agent'> | ||
constructor(url: URL, options?: RemoteJWKSetOptions) { | ||
@@ -70,2 +82,3 @@ if (!(url instanceof URL)) { | ||
this._url = new URL(url.href) | ||
this._options = { agent: options?.agent } | ||
this._timeoutDuration = | ||
@@ -158,3 +171,3 @@ typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000 | ||
const cached = this._cached.get(jwk)! | ||
if (!(protectedHeader.alg! in cached)) { | ||
if (cached[protectedHeader.alg!] === undefined) { | ||
const keyObject = (await parseJWK({ ...jwk, alg: protectedHeader.alg! })) as | ||
@@ -176,3 +189,3 @@ | KeyObject | ||
if (!this._pendingFetch) { | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration) | ||
this._pendingFetch = fetchJson(this._url, this._timeoutDuration, this._options) | ||
.then((json: { keys: object[] }) => { | ||
@@ -182,3 +195,2 @@ if ( | ||
!json || | ||
!('keys' in json) || | ||
!Array.isArray(json.keys) || | ||
@@ -185,0 +197,0 @@ json.keys.some((key: object) => typeof key !== 'object' || !key) |
import verify from '../flattened/verify.js' | ||
import { JWSInvalid } from '../../util/errors.js' | ||
import { decoder } from '../../lib/buffer_utils.js' | ||
import type { | ||
@@ -60,8 +61,13 @@ CompactVerifyResult, | ||
export default async function compactVerify( | ||
jws: string, | ||
jws: string | Uint8Array, | ||
key: KeyLike | CompactVerifyGetKey, | ||
options?: VerifyOptions, | ||
): Promise<CompactVerifyResult> { | ||
if (jws instanceof Uint8Array) { | ||
// eslint-disable-next-line no-param-reassign | ||
jws = decoder.decode(jws) | ||
} | ||
if (typeof jws !== 'string') { | ||
throw new JWSInvalid('Compact JWS must be a string') | ||
throw new JWSInvalid('Compact JWS must be a string or Uint8Array') | ||
} | ||
@@ -75,3 +81,7 @@ const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.') | ||
const verified = await verify( | ||
{ payload, protected: protectedHeader, signature }, | ||
{ | ||
payload: (payload || undefined) as string, | ||
protected: protectedHeader || undefined, | ||
signature: (signature || undefined) as string, | ||
}, | ||
key as Parameters<typeof verify>[1], | ||
@@ -78,0 +88,0 @@ options, |
@@ -128,4 +128,4 @@ /* eslint-disable no-underscore-dangle */ | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header') | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') | ||
} | ||
@@ -132,0 +132,0 @@ |
@@ -84,19 +84,19 @@ import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js' | ||
if (!('protected' in jws) && !('header' in jws)) { | ||
if (jws.protected === undefined && jws.header === undefined) { | ||
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members') | ||
} | ||
if ('protected' in jws && typeof jws.protected !== 'string') { | ||
if (jws.protected !== undefined && typeof jws.protected !== 'string') { | ||
throw new JWSInvalid('JWS Protected Header incorrect type') | ||
} | ||
if (!('payload' in jws)) { | ||
if (jws.payload === undefined) { | ||
throw new JWSInvalid('JWS Payload missing') | ||
} | ||
if (!('signature' in jws) || typeof jws.signature !== 'string') { | ||
if (typeof jws.signature !== 'string') { | ||
throw new JWSInvalid('JWS Signature missing or incorrect type') | ||
} | ||
if ('header' in jws && !isObject(jws.header)) { | ||
if (jws.header !== undefined && !isObject(jws.header)) { | ||
throw new JWSInvalid('JWS Unprotected Header incorrect type') | ||
@@ -135,4 +135,4 @@ } | ||
if (!alg) { | ||
throw new JWSInvalid('missing JWS signature algorithm in JWS Header') | ||
if (typeof alg !== 'string' || !alg) { | ||
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') | ||
} | ||
@@ -184,7 +184,7 @@ | ||
if ('protected' in jws) { | ||
if (jws.protected !== undefined) { | ||
result.protectedHeader = parsedProt | ||
} | ||
if ('header' in jws) { | ||
if (jws.header !== undefined) { | ||
result.unprotectedHeader = jws.header | ||
@@ -191,0 +191,0 @@ } |
@@ -66,3 +66,3 @@ import decrypt from '../jwe/compact/decrypt.js' | ||
export default async function jwtDecrypt( | ||
jwt: string, | ||
jwt: string | Uint8Array, | ||
key: KeyLike | JWTDecryptGetKey, | ||
@@ -76,3 +76,3 @@ options?: JWTDecryptOptions, | ||
if ('iss' in protectedHeader && protectedHeader.iss !== payload.iss) { | ||
if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { | ||
throw new JWTClaimValidationFailed( | ||
@@ -85,3 +85,3 @@ 'replicated "iss" claim header parameter mismatch', | ||
if ('sub' in protectedHeader && protectedHeader.sub !== payload.sub) { | ||
if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { | ||
throw new JWTClaimValidationFailed( | ||
@@ -95,3 +95,3 @@ 'replicated "sub" claim header parameter mismatch', | ||
if ( | ||
'aud' in protectedHeader && | ||
protectedHeader.aud !== undefined && | ||
JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud) | ||
@@ -98,0 +98,0 @@ ) { |
@@ -67,3 +67,3 @@ import verify from '../jws/compact/verify.js' | ||
export default async function jwtVerify( | ||
jwt: string, | ||
jwt: string | Uint8Array, | ||
key: KeyLike | JWTVerifyGetKey, | ||
@@ -70,0 +70,0 @@ options?: JWTVerifyOptions, |
@@ -18,4 +18,8 @@ import type { JWEHeaderParameters, KeyLike } from '../types.d' | ||
function assertHeaderParameter(joseHeader: object, parameter: string, name: string) { | ||
if (!(parameter in joseHeader)) { | ||
function assertHeaderParameter( | ||
joseHeader: { [propName: string]: any }, | ||
parameter: string, | ||
name: string, | ||
) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new JWEInvalid(`JOSE Header ${name} (${parameter}) missing`) | ||
@@ -51,3 +55,5 @@ } | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime') | ||
throw new JOSENotSupported( | ||
'ECDH-ES with the provided key is not allowed or not supported by your javascript runtime', | ||
) | ||
} | ||
@@ -57,4 +63,4 @@ const ephemeralKey = await ECDH.publicJwkToEphemeralKey(joseHeader.epk!) | ||
let partyVInfo!: Uint8Array | ||
if (joseHeader.apu) partyUInfo = base64url(joseHeader.apu) | ||
if (joseHeader.apv) partyVInfo = base64url(joseHeader.apv) | ||
if (joseHeader.apu !== undefined) partyUInfo = base64url(joseHeader.apu) | ||
if (joseHeader.apv !== undefined) partyVInfo = base64url(joseHeader.apv) | ||
const sharedSecret = await ECDH.deriveKey( | ||
@@ -117,3 +123,3 @@ ephemeralKey, | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`) | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value') | ||
} | ||
@@ -120,0 +126,0 @@ } |
@@ -42,3 +42,5 @@ import type { KeyLike, JWEKeyManagementHeaderParameters } from '../types.d' | ||
if (!ECDH.ecdhAllowed(key)) { | ||
throw new JOSENotSupported('ECDH not allowed or unsupported by your javascript runtime') | ||
throw new JOSENotSupported( | ||
'ECDH-ES with the provided key is not allowed or not supported by your javascript runtime', | ||
) | ||
} | ||
@@ -109,3 +111,3 @@ const { apu, apv } = providedParameters | ||
default: { | ||
throw new JOSENotSupported(`alg ${alg} is unsupported`) | ||
throw new JOSENotSupported('unsupported or invalid "alg" (JWE Algorithm) header value') | ||
} | ||
@@ -112,0 +114,0 @@ } |
@@ -85,7 +85,7 @@ import type { | ||
if ('iat' in payload || options.maxTokenAge) { | ||
if (payload.iat !== undefined || options.maxTokenAge) { | ||
if (typeof payload.iat !== 'number') { | ||
throw new JWTClaimValidationFailed('"iat" claim must be a number', 'iat', 'invalid') | ||
} | ||
if (!('exp' in payload) && payload.iat > now + tolerance) { | ||
if (payload.exp === undefined && payload.iat > now + tolerance) { | ||
throw new JWTClaimValidationFailed( | ||
@@ -99,3 +99,3 @@ '"iat" claim timestamp check failed (it should be in the past)', | ||
if ('nbf' in payload) { | ||
if (payload.nbf !== undefined) { | ||
if (typeof payload.nbf !== 'number') { | ||
@@ -113,3 +113,3 @@ throw new JWTClaimValidationFailed('"nbf" claim must be a number', 'nbf', 'invalid') | ||
if ('exp' in payload) { | ||
if (payload.exp !== undefined) { | ||
if (typeof payload.exp !== 'number') { | ||
@@ -116,0 +116,0 @@ throw new JWTClaimValidationFailed('"exp" claim must be a number', 'exp', 'invalid') |
@@ -14,3 +14,3 @@ const minute = 60 | ||
if (!matched) { | ||
throw new TypeError(`invalid time period format ("${str}")`) | ||
throw new TypeError('invalid time period format') | ||
} | ||
@@ -17,0 +17,0 @@ |
import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js' | ||
type CritCheckHeader = object & { | ||
interface CritCheckHeader { | ||
b64?: boolean | ||
crit?: string[] | ||
[propName: string]: any | ||
} | ||
const isString = (input: string) => typeof input !== 'string' || input.length === 0 | ||
function validateCrit( | ||
@@ -16,8 +15,8 @@ Err: typeof JWEInvalid | typeof JWSInvalid, | ||
) { | ||
if ('crit' in joseHeader && !('crit' in protectedHeader)) { | ||
if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) { | ||
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected') | ||
} | ||
if (!protectedHeader || !('crit' in protectedHeader)) { | ||
return new Set([]) | ||
if (!protectedHeader || protectedHeader.crit === undefined) { | ||
return new Set() | ||
} | ||
@@ -28,3 +27,3 @@ | ||
protectedHeader.crit.length === 0 || | ||
protectedHeader.crit.some(isString) | ||
protectedHeader.crit.some((input: string) => typeof input !== 'string' || input.length === 0) | ||
) { | ||
@@ -44,5 +43,5 @@ throw new Err( | ||
if (!(parameter in joseHeader)) { | ||
if (joseHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" is missing`) | ||
} else if (supported.get(parameter) && !(parameter in protectedHeader)) { | ||
} else if (supported.get(parameter) && protectedHeader[parameter] === undefined) { | ||
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`) | ||
@@ -49,0 +48,0 @@ } |
@@ -55,5 +55,9 @@ import type { JWK, KeyLike } from '../types.d' | ||
export interface AesGcmKwUnwrapFunction { | ||
(alg: string, key: any, encryptedKey: Uint8Array, iv: Uint8Array, tag: Uint8Array): Promise< | ||
Uint8Array | ||
> | ||
( | ||
alg: string, | ||
key: any, | ||
encryptedKey: Uint8Array, | ||
iv: Uint8Array, | ||
tag: Uint8Array, | ||
): Promise<Uint8Array> | ||
} | ||
@@ -68,5 +72,9 @@ export interface Pbes2KWEncryptFunction { | ||
export interface Pbes2KWDecryptFunction { | ||
(alg: string, key: any, encryptedKey: Uint8Array, p2c: number, p2s: Uint8Array): Promise< | ||
Uint8Array | ||
> | ||
( | ||
alg: string, | ||
key: any, | ||
encryptedKey: Uint8Array, | ||
p2c: number, | ||
p2s: Uint8Array, | ||
): Promise<Uint8Array> | ||
} | ||
@@ -115,3 +123,3 @@ export interface EcdhESDeriveKeyFunction { | ||
export interface FetchFunction { | ||
(url: URL, timeout: number): Promise<any> | ||
(url: URL, timeout: number, options: any): Promise<any> | ||
} | ||
@@ -124,1 +132,4 @@ export interface DigestFunction { | ||
} | ||
export interface JWKConvertFunction { | ||
(key: any): AsyncOrSync<JWK> | ||
} |
686726
3.5%25
-3.85%389
3.73%16656
3.92%253
5.86%