UNPKG

jose

Version:

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes

86 lines (85 loc) 3.77 kB
import { encode as b64u } from '../util/base64url.js'; import { sign } from './signing.js'; import { jwsAlgorithm } from './jws_algorithms.js'; import { isDisjoint } from './type_checks.js'; import { JWSInvalid } from '../util/errors.js'; import { concat, encode } from './buffer_utils.js'; import { serializeJoseHeader, validateB64, validateCrit, validateCritDuplicates, JWS_RECOGNIZED, } from './options.js'; import { prepareKey } from './key.js'; function serializeProtectedHeader(protectedHeader) { if (protectedHeader === undefined) return [undefined, '']; const normalized = serializeJoseHeader(JWSInvalid, protectedHeader); return [normalized[0], b64u(normalized[1])]; } function validateSignatureHeader(protectedHeader, joseHeader, crit) { validateCritDuplicates(JWSInvalid, protectedHeader); return validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader)); } function signatureAlgorithm(joseHeader) { const alg = joseHeader.alg; if (typeof alg !== 'string' || !alg) { throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); } return jwsAlgorithm(alg); } async function signSignature(protectedHeader, payload, entry, key) { const data = concat(encode(protectedHeader), encode('.'), payload); const k = await prepareKey(entry, key, 'sign'); return b64u(await sign(entry, k, data)); } export async function createSignature(input, key, assertB64) { let { protectedHeader, unprotectedHeader } = input; let protectedHeaderString; [protectedHeader, protectedHeaderString] = serializeProtectedHeader(protectedHeader); if (unprotectedHeader !== undefined) { unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]; } if (!protectedHeader && !unprotectedHeader) { throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); } if (!isDisjoint(protectedHeader, unprotectedHeader)) { throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); } const joseHeader = { ...protectedHeader, ...unprotectedHeader }; const b64 = validateSignatureHeader(protectedHeader, joseHeader, input.crit); assertB64?.(b64); const entry = signatureAlgorithm(joseHeader); let payloadS; let payloadB; if (b64) { const encoded = (input.encoded ??= []); encoded[0] ??= b64u(input.payload); encoded[1] ??= encode(encoded[0]); payloadS = encoded[0]; payloadB = encoded[1]; } else { payloadB = input.payload; payloadS = ''; } const jws = { signature: await signSignature(protectedHeaderString, payloadB, entry, key), payload: payloadS, }; if (protectedHeader) { jws.protected = protectedHeaderString; } if (unprotectedHeader) { jws.header = unprotectedHeader; } return [jws, b64]; } export async function createCompactSignature(payload, inputProtectedHeader, inputCrit, key, rejectUnencoded) { const [protectedHeader, protectedHeaderString] = serializeProtectedHeader(inputProtectedHeader); if (!protectedHeader) { throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); } const b64 = validateSignatureHeader(protectedHeader, protectedHeader, inputCrit); if (!b64) rejectUnencoded(); const entry = signatureAlgorithm(protectedHeader); const encodedPayload = b64u(payload); const signature = await signSignature(protectedHeaderString, encode(encodedPayload), entry, key); return `${protectedHeaderString}.${encodedPayload}.${signature}`; }