Add node modules and compiled JavaScript from main (#54)
Co-authored-by: Oliver King <oking3@uncc.edu>
This commit is contained in:
committed by
GitHub
parent
4a983766a0
commit
52d71d28bd
229
node_modules/jose/lib/jwe/decrypt.js
generated
vendored
Normal file
229
node_modules/jose/lib/jwe/decrypt.js
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
const { inflateRawSync } = require('zlib')
|
||||
|
||||
const base64url = require('../help/base64url')
|
||||
const getKey = require('../help/get_key')
|
||||
const { KeyStore } = require('../jwks')
|
||||
const errors = require('../errors')
|
||||
const { check, decrypt, keyManagementDecrypt } = require('../jwa')
|
||||
const JWK = require('../jwk')
|
||||
|
||||
const { createSecretKey } = require('../help/key_object')
|
||||
const generateCEK = require('./generate_cek')
|
||||
const validateHeaders = require('./validate_headers')
|
||||
const { detect: resolveSerialization } = require('./serializers')
|
||||
|
||||
const SINGLE_RECIPIENT = new Set(['compact', 'flattened'])
|
||||
|
||||
const combineHeader = (prot = {}, unprotected = {}, header = {}) => {
|
||||
if (typeof prot === 'string') {
|
||||
prot = base64url.JSON.decode(prot)
|
||||
}
|
||||
|
||||
const p2s = prot.p2s || unprotected.p2s || header.p2s
|
||||
const apu = prot.apu || unprotected.apu || header.apu
|
||||
const apv = prot.apv || unprotected.apv || header.apv
|
||||
const iv = prot.iv || unprotected.iv || header.iv
|
||||
const tag = prot.tag || unprotected.tag || header.tag
|
||||
|
||||
return {
|
||||
...prot,
|
||||
...unprotected,
|
||||
...header,
|
||||
...(typeof p2s === 'string' ? { p2s: base64url.decodeToBuffer(p2s) } : undefined),
|
||||
...(typeof apu === 'string' ? { apu: base64url.decodeToBuffer(apu) } : undefined),
|
||||
...(typeof apv === 'string' ? { apv: base64url.decodeToBuffer(apv) } : undefined),
|
||||
...(typeof iv === 'string' ? { iv: base64url.decodeToBuffer(iv) } : undefined),
|
||||
...(typeof tag === 'string' ? { tag: base64url.decodeToBuffer(tag) } : undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const validateAlgorithms = (algorithms, option) => {
|
||||
if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some(s => typeof s !== 'string' || !s))) {
|
||||
throw new TypeError(`"${option}" option must be an array of non-empty strings`)
|
||||
}
|
||||
|
||||
if (!algorithms) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return new Set(algorithms)
|
||||
}
|
||||
|
||||
/*
|
||||
* @public
|
||||
*/
|
||||
const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms } = {}) => {
|
||||
key = getKey(key, true)
|
||||
|
||||
keyManagementAlgorithms = validateAlgorithms(keyManagementAlgorithms, 'keyManagementAlgorithms')
|
||||
contentEncryptionAlgorithms = validateAlgorithms(contentEncryptionAlgorithms, 'contentEncryptionAlgorithms')
|
||||
|
||||
if (!Array.isArray(crit) || crit.some(s => typeof s !== 'string' || !s)) {
|
||||
throw new TypeError('"crit" option must be an array of non-empty strings')
|
||||
}
|
||||
|
||||
if (!serialization) {
|
||||
serialization = resolveSerialization(jwe)
|
||||
}
|
||||
|
||||
let alg, ciphertext, enc, encryptedKey, iv, opts, prot, tag, unprotected, cek, aad, header
|
||||
|
||||
// treat general format with one recipient as flattened
|
||||
// skips iteration and avoids multi errors in this case
|
||||
if (serialization === 'general' && jwe.recipients.length === 1) {
|
||||
serialization = 'flattened'
|
||||
const { recipients, ...root } = jwe
|
||||
jwe = { ...root, ...recipients[0] }
|
||||
}
|
||||
|
||||
if (SINGLE_RECIPIENT.has(serialization)) {
|
||||
if (serialization === 'compact') { // compact serialization format
|
||||
([prot, encryptedKey, iv, ciphertext, tag] = jwe.split('.'))
|
||||
} else { // flattened serialization format
|
||||
({ protected: prot, encrypted_key: encryptedKey, iv, ciphertext, tag, unprotected, aad, header } = jwe)
|
||||
}
|
||||
|
||||
if (!skipValidateHeaders) {
|
||||
validateHeaders(prot, unprotected, [{ header }], true, crit)
|
||||
}
|
||||
|
||||
opts = combineHeader(prot, unprotected, header)
|
||||
|
||||
;({ alg, enc } = opts)
|
||||
|
||||
if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) {
|
||||
throw new errors.JOSEAlgNotWhitelisted('key management algorithm not whitelisted')
|
||||
}
|
||||
|
||||
if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) {
|
||||
throw new errors.JOSEAlgNotWhitelisted('content encryption algorithm not whitelisted')
|
||||
}
|
||||
|
||||
if (key instanceof KeyStore) {
|
||||
const keystore = key
|
||||
let keys
|
||||
if (opts.alg === 'dir') {
|
||||
keys = keystore.all({ kid: opts.kid, alg: opts.enc, key_ops: ['decrypt'] })
|
||||
} else {
|
||||
keys = keystore.all({ kid: opts.kid, alg: opts.alg, key_ops: ['unwrapKey'] })
|
||||
}
|
||||
switch (keys.length) {
|
||||
case 0:
|
||||
throw new errors.JWKSNoMatchingKey()
|
||||
case 1:
|
||||
// treat the call as if a Key instance was passed in
|
||||
// skips iteration and avoids multi errors in this case
|
||||
key = keys[0]
|
||||
break
|
||||
default: {
|
||||
const errs = []
|
||||
for (const key of keys) {
|
||||
try {
|
||||
return jweDecrypt(true, serialization, jwe, key, {
|
||||
crit,
|
||||
complete,
|
||||
contentEncryptionAlgorithms: contentEncryptionAlgorithms ? [...contentEncryptionAlgorithms] : undefined,
|
||||
keyManagementAlgorithms: keyManagementAlgorithms ? [...keyManagementAlgorithms] : undefined
|
||||
})
|
||||
} catch (err) {
|
||||
errs.push(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
const multi = new errors.JOSEMultiError(errs)
|
||||
if ([...multi].some(e => e instanceof errors.JWEDecryptionFailed)) {
|
||||
throw new errors.JWEDecryptionFailed()
|
||||
}
|
||||
throw multi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check(key, ...(alg === 'dir' ? ['decrypt', enc] : ['keyManagementDecrypt', alg]))
|
||||
|
||||
try {
|
||||
if (alg === 'dir') {
|
||||
cek = JWK.asKey(key, { alg: enc, use: 'enc' })
|
||||
} else if (alg === 'ECDH-ES') {
|
||||
const unwrapped = keyManagementDecrypt(alg, key, undefined, opts)
|
||||
cek = JWK.asKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' })
|
||||
} else {
|
||||
const unwrapped = keyManagementDecrypt(alg, key, base64url.decodeToBuffer(encryptedKey), opts)
|
||||
cek = JWK.asKey(createSecretKey(unwrapped), { alg: enc, use: 'enc' })
|
||||
}
|
||||
} catch (err) {
|
||||
// To mitigate the attacks described in RFC 3218, the
|
||||
// recipient MUST NOT distinguish between format, padding, and length
|
||||
// errors of encrypted keys. It is strongly recommended, in the event
|
||||
// of receiving an improperly formatted key, that the recipient
|
||||
// substitute a randomly generated CEK and proceed to the next step, to
|
||||
// mitigate timing attacks.
|
||||
cek = generateCEK(enc)
|
||||
}
|
||||
|
||||
let adata
|
||||
if (aad) {
|
||||
adata = Buffer.concat([
|
||||
Buffer.from(prot || ''),
|
||||
Buffer.from('.'),
|
||||
Buffer.from(aad)
|
||||
])
|
||||
} else {
|
||||
adata = Buffer.from(prot || '')
|
||||
}
|
||||
|
||||
try {
|
||||
iv = base64url.decodeToBuffer(iv)
|
||||
} catch (err) {}
|
||||
try {
|
||||
tag = base64url.decodeToBuffer(tag)
|
||||
} catch (err) {}
|
||||
|
||||
let cleartext = decrypt(enc, cek, base64url.decodeToBuffer(ciphertext), { iv, tag, aad: adata })
|
||||
|
||||
if (opts.zip) {
|
||||
cleartext = inflateRawSync(cleartext)
|
||||
}
|
||||
|
||||
if (complete) {
|
||||
const result = { cleartext, key, cek }
|
||||
if (aad) result.aad = aad
|
||||
if (header) result.header = header
|
||||
if (unprotected) result.unprotected = unprotected
|
||||
if (prot) result.protected = base64url.JSON.decode(prot)
|
||||
return result
|
||||
}
|
||||
|
||||
return cleartext
|
||||
}
|
||||
|
||||
validateHeaders(jwe.protected, jwe.unprotected, jwe.recipients.map(({ header }) => ({ header })), true, crit)
|
||||
|
||||
// general serialization format
|
||||
const { recipients, ...root } = jwe
|
||||
const errs = []
|
||||
for (const recipient of recipients) {
|
||||
try {
|
||||
return jweDecrypt(true, 'flattened', { ...root, ...recipient }, key, {
|
||||
crit,
|
||||
complete,
|
||||
contentEncryptionAlgorithms: contentEncryptionAlgorithms ? [...contentEncryptionAlgorithms] : undefined,
|
||||
keyManagementAlgorithms: keyManagementAlgorithms ? [...keyManagementAlgorithms] : undefined
|
||||
})
|
||||
} catch (err) {
|
||||
errs.push(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
const multi = new errors.JOSEMultiError(errs)
|
||||
if ([...multi].some(e => e instanceof errors.JWEDecryptionFailed)) {
|
||||
throw new errors.JWEDecryptionFailed()
|
||||
} else if ([...multi].every(e => e instanceof errors.JWKSNoMatchingKey)) {
|
||||
throw new errors.JWKSNoMatchingKey()
|
||||
}
|
||||
throw multi
|
||||
}
|
||||
|
||||
module.exports = jweDecrypt.bind(undefined, false, undefined)
|
Reference in New Issue
Block a user