import { encode as base64url } from '../../runtime/base64url.js'; import sign from '../../runtime/sign.js'; import isDisjoint from '../../lib/is_disjoint.js'; import { JWSInvalid } from '../../util/errors.js'; import { encoder, decoder, concat } from '../../lib/buffer_utils.js'; import checkKeyType from '../../lib/check_key_type.js'; import validateCrit from '../../lib/validate_crit.js'; export class FlattenedSign { constructor(payload) { if (!(payload instanceof Uint8Array)) { throw new TypeError('payload must be an instance of Uint8Array'); } this._payload = payload; } setProtectedHeader(protectedHeader) { if (this._protectedHeader) { throw new TypeError('setProtectedHeader can only be called once'); } this._protectedHeader = protectedHeader; return this; } setUnprotectedHeader(unprotectedHeader) { if (this._unprotectedHeader) { throw new TypeError('setUnprotectedHeader can only be called once'); } this._unprotectedHeader = unprotectedHeader; return this; } async sign(key, options) { if (!this._protectedHeader && !this._unprotectedHeader) { throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()'); } if (!isDisjoint(this._protectedHeader, this._unprotectedHeader)) { throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint'); } const joseHeader = { ...this._protectedHeader, ...this._unprotectedHeader, }; const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, this._protectedHeader, joseHeader); let b64 = true; if (extensions.has('b64')) { b64 = this._protectedHeader.b64; if (typeof b64 !== 'boolean') { throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); } } const { alg } = joseHeader; if (typeof alg !== 'string' || !alg) { throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); } checkKeyType(alg, key, 'sign'); let payload = this._payload; if (b64) { payload = encoder.encode(base64url(payload)); } let protectedHeader; if (this._protectedHeader) { protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader))); } else { protectedHeader = encoder.encode(''); } const data = concat(protectedHeader, encoder.encode('.'), payload); const signature = await sign(alg, key, data); const jws = { signature: base64url(signature), payload: '', }; if (b64) { jws.payload = decoder.decode(payload); } if (this._unprotectedHeader) { jws.header = this._unprotectedHeader; } if (this._protectedHeader) { jws.protected = decoder.decode(protectedHeader); } return jws; } }