2021-12-07 13:18:08 -05:00

122 lines
4.0 KiB
JavaScript

const isObject = require('../help/is_object')
let validateCrit = require('../help/validate_crit')
const { JWEInvalid } = require('../errors')
validateCrit = validateCrit.bind(undefined, JWEInvalid)
const compactSerializer = (final, [recipient]) => {
return `${final.protected}.${recipient.encrypted_key}.${final.iv}.${final.ciphertext}.${final.tag}`
}
compactSerializer.validate = (protectedHeader, unprotectedHeader, aad, { 0: { header }, length }) => {
if (length !== 1 || aad || unprotectedHeader || header) {
throw new JWEInvalid('JWE Compact Serialization doesn\'t support multiple recipients, JWE unprotected headers or AAD')
}
validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
}
const flattenedSerializer = (final, [recipient]) => {
const { header, encrypted_key: encryptedKey } = recipient
return {
...(final.protected ? { protected: final.protected } : undefined),
...(final.unprotected ? { unprotected: final.unprotected } : undefined),
...(header ? { header } : undefined),
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined),
...(final.aad ? { aad: final.aad } : undefined),
iv: final.iv,
ciphertext: final.ciphertext,
tag: final.tag
}
}
flattenedSerializer.validate = (protectedHeader, unprotectedHeader, aad, { 0: { header }, length }) => {
if (length !== 1) {
throw new JWEInvalid('Flattened JWE JSON Serialization doesn\'t support multiple recipients')
}
validateCrit(protectedHeader, { ...unprotectedHeader, ...header }, protectedHeader ? protectedHeader.crit : undefined)
}
const generalSerializer = (final, recipients) => {
const result = {
...(final.protected ? { protected: final.protected } : undefined),
...(final.unprotected ? { unprotected: final.unprotected } : undefined),
recipients: recipients.map(({ header, encrypted_key: encryptedKey, generatedHeader }) => {
if (!header && !encryptedKey && !generatedHeader) {
return false
}
return {
...(header || generatedHeader ? { header: { ...header, ...generatedHeader } } : undefined),
...(encryptedKey ? { encrypted_key: encryptedKey } : undefined)
}
}).filter(Boolean),
...(final.aad ? { aad: final.aad } : undefined),
iv: final.iv,
ciphertext: final.ciphertext,
tag: final.tag
}
if (!result.recipients.length) {
delete result.recipients
}
return result
}
generalSerializer.validate = (protectedHeader, unprotectedHeader, aad, recipients) => {
recipients.forEach(({ header }) => {
validateCrit(protectedHeader, { ...header, ...unprotectedHeader }, protectedHeader ? protectedHeader.crit : undefined)
})
}
const isJSON = (input) => {
return isObject(input) &&
typeof input.ciphertext === 'string' &&
typeof input.iv === 'string' &&
typeof input.tag === 'string' &&
(input.unprotected === undefined || isObject(input.unprotected)) &&
(input.protected === undefined || typeof input.protected === 'string') &&
(input.aad === undefined || typeof input.aad === 'string')
}
const isSingleRecipient = (input) => {
return (input.encrypted_key === undefined || typeof input.encrypted_key === 'string') &&
(input.header === undefined || isObject(input.header))
}
const isValidRecipient = (recipient) => {
return isObject(recipient) && typeof recipient.encrypted_key === 'string' && (recipient.header === undefined || isObject(recipient.header))
}
const isMultiRecipient = (input) => {
if (Array.isArray(input.recipients) && input.recipients.every(isValidRecipient)) {
return true
}
return false
}
const detect = (input) => {
if (typeof input === 'string' && input.split('.').length === 5) {
return 'compact'
}
if (isJSON(input)) {
if (isMultiRecipient(input)) {
return 'general'
}
if (isSingleRecipient(input)) {
return 'flattened'
}
}
throw new JWEInvalid('JWE malformed or invalid serialization')
}
module.exports = {
compact: compactSerializer,
flattened: flattenedSerializer,
general: generalSerializer,
detect
}