1 | const isObject = require('../help/is_object')
|
2 | let validateCrit = require('../help/validate_crit')
|
3 | const { JWSInvalid } = require('../errors')
|
4 |
|
5 | validateCrit = validateCrit.bind(undefined, JWSInvalid)
|
6 |
|
7 | const compactSerializer = (payload, [recipient]) => {
|
8 | return `${recipient.protected}.${payload}.${recipient.signature}`
|
9 | }
|
10 | compactSerializer.validate = (jws, { 0: { unprotectedHeader, protectedHeader }, length }) => {
|
11 | if (length !== 1 || unprotectedHeader) {
|
12 | throw new JWSInvalid('JWS Compact Serialization doesn\'t support multiple recipients or JWS unprotected headers')
|
13 | }
|
14 | validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
15 | }
|
16 |
|
17 | const flattenedSerializer = (payload, [recipient]) => {
|
18 | const { header, signature, protected: prot } = recipient
|
19 |
|
20 | return {
|
21 | payload,
|
22 | ...prot ? { protected: prot } : undefined,
|
23 | ...header ? { header } : undefined,
|
24 | signature
|
25 | }
|
26 | }
|
27 | flattenedSerializer.validate = (jws, { 0: { unprotectedHeader, protectedHeader }, length }) => {
|
28 | if (length !== 1) {
|
29 | throw new JWSInvalid('Flattened JWS JSON Serialization doesn\'t support multiple recipients')
|
30 | }
|
31 | validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
32 | }
|
33 |
|
34 | const generalSerializer = (payload, recipients) => {
|
35 | return {
|
36 | payload,
|
37 | signatures: recipients.map(({ header, signature, protected: prot }) => {
|
38 | return {
|
39 | ...prot ? { protected: prot } : undefined,
|
40 | ...header ? { header } : undefined,
|
41 | signature
|
42 | }
|
43 | })
|
44 | }
|
45 | }
|
46 | generalSerializer.validate = (jws, recipients) => {
|
47 | let validateB64 = false
|
48 | recipients.forEach(({ protectedHeader, unprotectedHeader }) => {
|
49 | if (protectedHeader && !validateB64 && 'b64' in protectedHeader) {
|
50 | validateB64 = true
|
51 | }
|
52 | validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
53 | })
|
54 |
|
55 | if (validateB64) {
|
56 | const values = recipients.map(({ protectedHeader }) => protectedHeader && protectedHeader.b64)
|
57 | if (!values.every((actual, i, [expected]) => actual === expected)) {
|
58 | throw new JWSInvalid('the "b64" Header Parameter value MUST be the same for all recipients')
|
59 | }
|
60 | }
|
61 | }
|
62 |
|
63 | const isJSON = (input) => {
|
64 | return isObject(input) && (typeof input.payload === 'string' || Buffer.isBuffer(input.payload))
|
65 | }
|
66 |
|
67 | const isValidRecipient = (recipient) => {
|
68 | return isObject(recipient) && typeof recipient.signature === 'string' &&
|
69 | (recipient.header === undefined || isObject(recipient.header)) &&
|
70 | (recipient.protected === undefined || typeof recipient.protected === 'string')
|
71 | }
|
72 |
|
73 | const isMultiRecipient = (input) => {
|
74 | if (Array.isArray(input.signatures) && input.signatures.every(isValidRecipient)) {
|
75 | return true
|
76 | }
|
77 |
|
78 | return false
|
79 | }
|
80 |
|
81 | const detect = (input) => {
|
82 | if (typeof input === 'string' && input.split('.').length === 3) {
|
83 | return 'compact'
|
84 | }
|
85 |
|
86 | if (isJSON(input)) {
|
87 | if (isMultiRecipient(input)) {
|
88 | return 'general'
|
89 | }
|
90 |
|
91 | if (isValidRecipient(input)) {
|
92 | return 'flattened'
|
93 | }
|
94 | }
|
95 |
|
96 | throw new JWSInvalid('JWS malformed or invalid serialization')
|
97 | }
|
98 |
|
99 | module.exports = {
|
100 | compact: compactSerializer,
|
101 | flattened: flattenedSerializer,
|
102 | general: generalSerializer,
|
103 | detect
|
104 | }
|