105 lines
3.2 KiB
JavaScript
105 lines
3.2 KiB
JavaScript
const isObject = require('../help/is_object')
|
|
let validateCrit = require('../help/validate_crit')
|
|
const { JWSInvalid } = require('../errors')
|
|
|
|
validateCrit = validateCrit.bind(undefined, JWSInvalid)
|
|
|
|
const compactSerializer = (payload, [recipient]) => {
|
|
return `${recipient.protected}.${payload}.${recipient.signature}`
|
|
}
|
|
compactSerializer.validate = (jws, { 0: { unprotectedHeader, protectedHeader }, length }) => {
|
|
if (length !== 1 || unprotectedHeader) {
|
|
throw new JWSInvalid('JWS Compact Serialization doesn\'t support multiple recipients or JWS unprotected headers')
|
|
}
|
|
validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
|
}
|
|
|
|
const flattenedSerializer = (payload, [recipient]) => {
|
|
const { header, signature, protected: prot } = recipient
|
|
|
|
return {
|
|
payload,
|
|
...prot ? { protected: prot } : undefined,
|
|
...header ? { header } : undefined,
|
|
signature
|
|
}
|
|
}
|
|
flattenedSerializer.validate = (jws, { 0: { unprotectedHeader, protectedHeader }, length }) => {
|
|
if (length !== 1) {
|
|
throw new JWSInvalid('Flattened JWS JSON Serialization doesn\'t support multiple recipients')
|
|
}
|
|
validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
|
}
|
|
|
|
const generalSerializer = (payload, recipients) => {
|
|
return {
|
|
payload,
|
|
signatures: recipients.map(({ header, signature, protected: prot }) => {
|
|
return {
|
|
...prot ? { protected: prot } : undefined,
|
|
...header ? { header } : undefined,
|
|
signature
|
|
}
|
|
})
|
|
}
|
|
}
|
|
generalSerializer.validate = (jws, recipients) => {
|
|
let validateB64 = false
|
|
recipients.forEach(({ protectedHeader, unprotectedHeader }) => {
|
|
if (protectedHeader && !validateB64 && 'b64' in protectedHeader) {
|
|
validateB64 = true
|
|
}
|
|
validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
|
})
|
|
|
|
if (validateB64) {
|
|
const values = recipients.map(({ protectedHeader }) => protectedHeader && protectedHeader.b64)
|
|
if (!values.every((actual, i, [expected]) => actual === expected)) {
|
|
throw new JWSInvalid('the "b64" Header Parameter value MUST be the same for all recipients')
|
|
}
|
|
}
|
|
}
|
|
|
|
const isJSON = (input) => {
|
|
return isObject(input) && (typeof input.payload === 'string' || Buffer.isBuffer(input.payload))
|
|
}
|
|
|
|
const isValidRecipient = (recipient) => {
|
|
return isObject(recipient) && typeof recipient.signature === 'string' &&
|
|
(recipient.header === undefined || isObject(recipient.header)) &&
|
|
(recipient.protected === undefined || typeof recipient.protected === 'string')
|
|
}
|
|
|
|
const isMultiRecipient = (input) => {
|
|
if (Array.isArray(input.signatures) && input.signatures.every(isValidRecipient)) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
const detect = (input) => {
|
|
if (typeof input === 'string' && input.split('.').length === 3) {
|
|
return 'compact'
|
|
}
|
|
|
|
if (isJSON(input)) {
|
|
if (isMultiRecipient(input)) {
|
|
return 'general'
|
|
}
|
|
|
|
if (isValidRecipient(input)) {
|
|
return 'flattened'
|
|
}
|
|
}
|
|
|
|
throw new JWSInvalid('JWS malformed or invalid serialization')
|
|
}
|
|
|
|
module.exports = {
|
|
compact: compactSerializer,
|
|
flattened: flattenedSerializer,
|
|
general: generalSerializer,
|
|
detect
|
|
}
|