UNPKG

did-jwt

Version:

Library for Signing and Verifying JWTs that use DIDs as issuers and JWEs that use DIDs as recipients

90 lines • 3.61 kB
import { base64ToBytes, bytesToBase64url, decodeBase64url, stringToBytes, toSealed } from '../util.js'; function validateJWE(jwe) { if (!(jwe.protected && jwe.iv && jwe.ciphertext && jwe.tag)) { throw new Error('bad_jwe: missing properties'); } if (jwe.recipients) { jwe.recipients.map((rec) => { if (!(rec.header && rec.encrypted_key)) { throw new Error('bad_jwe: malformed recipients'); } }); } } function encodeJWE({ ciphertext, tag, iv, protectedHeader, recipient }, aad) { const jwe = { protected: protectedHeader, iv: bytesToBase64url(iv ?? new Uint8Array(0)), ciphertext: bytesToBase64url(ciphertext), tag: bytesToBase64url(tag ?? new Uint8Array(0)), }; if (aad) jwe.aad = bytesToBase64url(aad); if (recipient) jwe.recipients = [recipient]; return jwe; } export async function createJWE(cleartext, encrypters, protectedHeader = {}, aad, useSingleEphemeralKey = false) { if (encrypters[0].alg === 'dir') { if (encrypters.length > 1) throw new Error('not_supported: Can only do "dir" encryption to one key.'); const encryptionResult = await encrypters[0].encrypt(cleartext, protectedHeader, aad); return encodeJWE(encryptionResult, aad); } else { const tmpEnc = encrypters[0].enc; if (!encrypters.reduce((acc, encrypter) => acc && encrypter.enc === tmpEnc, true)) { throw new Error('invalid_argument: Incompatible encrypters passed'); } let cek; let jwe; let epk; if (useSingleEphemeralKey) { epk = encrypters[0].genEpk?.(); const alg = encrypters[0].alg; protectedHeader = { ...protectedHeader, alg, epk: epk?.publicKeyJWK }; } for (const encrypter of encrypters) { if (!cek) { const encryptionResult = await encrypter.encrypt(cleartext, protectedHeader, aad, epk); cek = encryptionResult.cek; jwe = encodeJWE(encryptionResult, aad); } else { const recipient = await encrypter.encryptCek?.(cek, epk); if (recipient) { jwe?.recipients?.push(recipient); } } } return jwe; } } export async function decryptJWE(jwe, decrypter) { validateJWE(jwe); const protHeader = JSON.parse(decodeBase64url(jwe.protected)); if (protHeader.enc !== decrypter.enc) throw new Error(`not_supported: Decrypter does not supported: '${protHeader.enc}'`); const sealed = toSealed(jwe.ciphertext, jwe.tag); const aad = stringToBytes(jwe.aad ? `${jwe.protected}.${jwe.aad}` : jwe.protected); let cleartext = null; if (protHeader.alg === 'dir' && decrypter.alg === 'dir') { cleartext = await decrypter.decrypt(sealed, base64ToBytes(jwe.iv), aad); } else if (!jwe.recipients || jwe.recipients.length === 0) { throw new Error('bad_jwe: missing recipients'); } else { for (let i = 0; !cleartext && i < jwe.recipients.length; i++) { const recipient = jwe.recipients[i]; Object.assign(recipient.header, protHeader); if (recipient.header.alg === decrypter.alg) { cleartext = await decrypter.decrypt(sealed, base64ToBytes(jwe.iv), aad, recipient); } } } if (cleartext === null) throw new Error('failure: Failed to decrypt'); return cleartext; } //# sourceMappingURL=JWE.js.map