UNPKG

did-jwt

Version:

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

233 lines • 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAuthEncrypter = createAuthEncrypter; exports.createAnonEncrypter = createAnonEncrypter; exports.createAuthDecrypter = createAuthDecrypter; exports.createAnonDecrypter = createAnonDecrypter; exports.x25519Encrypter = x25519Encrypter; exports.xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2 = xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2; exports.xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2 = xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2; exports.resolveX25519Encrypters = resolveX25519Encrypters; exports.x25519Decrypter = x25519Decrypter; exports.xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2 = xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2; exports.xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2 = xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2; const util_js_1 = require("../util.js"); const xc20pDir_js_1 = require("./xc20pDir.js"); const X25519_ECDH_1PU_js_1 = require("./X25519-ECDH-1PU.js"); const X25519_ECDH_ES_js_1 = require("./X25519-ECDH-ES.js"); const createEncrypter_js_1 = require("./createEncrypter.js"); /** * @deprecated Use * {@link xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2 | xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2() } instead */ function createAuthEncrypter(recipientPublicKey, senderSecret, options = {}) { return xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2(recipientPublicKey, senderSecret, options); } /** * @deprecated Use {@link xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2 | xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2() } * instead */ function createAnonEncrypter(publicKey, options = {}) { return xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2(publicKey, options); } /** * @deprecated Use * {@link xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2 | xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2() } instead */ function createAuthDecrypter(recipientSecret, senderPublicKey) { return xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2(recipientSecret, senderPublicKey); } /** * @deprecated Use {@link xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2 | xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2() } * instead */ function createAnonDecrypter(recipientSecret) { return xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2(recipientSecret); } function validateHeader(header) { if (!(header && header.epk && header.iv && header.tag)) { throw new Error('bad_jwe: malformed header'); } return header; } const xc20pKeyWrapper = { from: (wrappingKey) => { const wrap = async (cek) => { return (0, xc20pDir_js_1.xc20pEncrypter)(wrappingKey)(cek); }; return { wrap }; }, alg: 'XC20PKW', }; /** * @deprecated Use {@link xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2 | xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2() } * instead */ function x25519Encrypter(publicKey, kid, apv) { return xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2(publicKey, { kid, apv }); } /** * Recommended encrypter for anonymous encryption (i.e. no sender authentication). * Uses {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | ECDH-ES+XC20PKW v2}. * * @param recipientPublicKey - the byte array representing the recipient public key * @param options - {@link AnonEncryptParams} used to specify the recipient key ID (`kid`) * * @returns an {@link Encrypter} instance usable with {@link createJWE} * * NOTE: ECDH-ES+XC20PKW is a proposed draft in IETF and not a standard yet and * is subject to change as new revisions or until the official CFRG specification is released. */ function xc20pAnonEncrypterEcdhESx25519WithXc20PkwV2(recipientPublicKey, options = {}) { return (0, createEncrypter_js_1.createFullEncrypter)(recipientPublicKey, undefined, options, { createKek: X25519_ECDH_ES_js_1.createX25519EcdhEsKek, alg: 'ECDH-ES' }, xc20pKeyWrapper, { from: (cek) => (0, xc20pDir_js_1.xc20pDirEncrypter)(cek), enc: 'XC20P' }); } /** * Recommended encrypter for authenticated encryption (i.e. sender authentication and requires * sender private key to encrypt the data). * Uses {@link https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03 | ECDH-1PU v3 } and * {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | XC20PKW v2 }. * * @param recipientPublicKey - the byte array representing the recipient public key * @param senderSecret - either a Uint8Array representing the sender secret key or * an ECDH function that wraps the key and can promise a shared secret given a public key * @param options - {@link AuthEncryptParams} used to specify extra header parameters * * @returns an {@link Encrypter} instance usable with {@link createJWE} * * NOTE: ECDH-1PU and XC20PKW are proposed drafts in IETF and not a standard yet and * are subject to change as new revisions or until the official CFRG specification are released. * * Implements ECDH-1PU+XC20PKW with XChaCha20Poly1305 based on the following specs: * - {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | XC20PKW} * - {@link https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03 | ECDH-1PU} */ function xc20pAuthEncrypterEcdh1PuV3x25519WithXc20PkwV2(recipientPublicKey, senderSecret, options = {}) { return (0, createEncrypter_js_1.createFullEncrypter)(recipientPublicKey, senderSecret, options, { createKek: X25519_ECDH_1PU_js_1.createX25519Ecdh1PUv3Kek, alg: 'ECDH-1PU' }, xc20pKeyWrapper, { from: (cek) => (0, xc20pDir_js_1.xc20pDirEncrypter)(cek), enc: 'XC20P' }); } async function resolveX25519Encrypters(dids, resolver) { const encryptersForDID = async (did, resolved = []) => { const { didResolutionMetadata, didDocument } = await resolver.resolve(did); resolved.push(did); if (didResolutionMetadata?.error || didDocument == null) { throw new Error(`resolver_error: Could not resolve ${did}: ${didResolutionMetadata.error}, ${didResolutionMetadata.message}`); } let controllerEncrypters = []; if (!didDocument.controller && !didDocument.keyAgreement) { throw new Error(`no_suitable_keys: Could not find x25519 key for ${did}`); } if (didDocument.controller) { let controllers = Array.isArray(didDocument.controller) ? didDocument.controller : [didDocument.controller]; controllers = controllers.filter((c) => !resolved.includes(c)); const encrypterPromises = controllers.map((did) => encryptersForDID(did, resolved).catch(() => { return []; })); const encrypterArrays = await Promise.all(encrypterPromises); controllerEncrypters = [].concat(...encrypterArrays); } const agreementKeys = didDocument.keyAgreement ?.map((key) => { if (typeof key === 'string') { return [...(didDocument.publicKey || []), ...(didDocument.verificationMethod || [])].find((pk) => pk.id === key); } return key; }) ?.filter((key) => typeof key !== 'undefined'); const pks = agreementKeys?.filter((key) => ['X25519KeyAgreementKey2019', 'X25519KeyAgreementKey2020', 'JsonWebKey2020', 'Multikey'].includes(key.type)) ?? []; if (!pks.length && !controllerEncrypters.length) throw new Error(`no_suitable_keys: Could not find X25519 key for ${did}`); return pks .map((pk) => { const { keyBytes, keyType } = (0, util_js_1.extractPublicKeyBytes)(pk); if (keyType === 'X25519') { return x25519Encrypter(keyBytes, pk.id); } else { return null; } }) .filter(util_js_1.isDefined) .concat(...controllerEncrypters); }; const encrypterPromises = dids.map((did) => encryptersForDID(did)); const encrypterArrays = await Promise.all(encrypterPromises); return [].concat(...encrypterArrays); } /** * @deprecated Use {@link xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2 | xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2() } * instead */ function x25519Decrypter(receiverSecret) { return xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2(receiverSecret); } /** * Recommended decrypter for anonymous encryption (i.e. no sender authentication). * Uses {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | ECDH-ES+XC20PKW v2 }. * * @param recipientSecret - either a Uint8Array representing the recipient secret key or * an ECDH function that wraps the key and can promise a shared secret given a public key * * @returns a {@link Decrypter} instance usable with {@link decryptJWE} * * NOTE: ECDH-ES+XC20PKW is a proposed draft in IETF and not a standard yet and * is subject to change as new revisions or until the official CFRG specification is released. * * @beta */ function xc20pAnonDecrypterEcdhESx25519WithXc20PkwV2(recipientSecret) { const alg = 'ECDH-ES+XC20PKW'; const enc = 'XC20P'; async function decrypt(sealed, iv, aad, recipient) { recipient = recipient; const header = validateHeader(recipient.header); const kek = await (0, X25519_ECDH_ES_js_1.computeX25519EcdhEsKek)(recipient, recipientSecret, alg); if (!kek) return null; // Content Encryption Key const sealedCek = (0, util_js_1.toSealed)(recipient.encrypted_key, header.tag); const cek = await (0, xc20pDir_js_1.xc20pDirDecrypter)(kek).decrypt(sealedCek, (0, util_js_1.base64ToBytes)(header.iv)); if (cek === null) return null; return (0, xc20pDir_js_1.xc20pDirDecrypter)(cek).decrypt(sealed, iv, aad); } return { alg, enc, decrypt }; } /** * Recommended decrypter for authenticated encryption (i.e. sender authentication and requires * sender public key to decrypt the data). * Uses {@link https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03 | ECDH-1PU v3 } and * {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | XC20PKW v2 }. * * @param recipientSecret - either a Uint8Array representing the recipient secret key or * an ECDH function that wraps the key and can promise a shared secret given a public key * @param senderPublicKey - the byte array representing the sender public key * * @returns a {@link Decrypter} instance usable with {@link decryptJWE} * * NOTE: ECDH-1PU and XC20PKW are proposed drafts in IETF and not a standard yet and * are subject to change as new revisions or until the official CFRG specification are released. * * @beta * * Implements ECDH-1PU+XC20PKW with XChaCha20Poly1305 based on the following specs: * - {@link https://tools.ietf.org/html/draft-amringer-jose-chacha-02 | XC20PKW} * - {@link https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03 | ECDH-1PU} */ function xc20pAuthDecrypterEcdh1PuV3x25519WithXc20PkwV2(recipientSecret, senderPublicKey) { const alg = 'ECDH-1PU+XC20PKW'; const enc = 'XC20P'; async function decrypt(sealed, iv, aad, recipient) { recipient = recipient; const header = validateHeader(recipient.header); const kek = await (0, X25519_ECDH_1PU_js_1.computeX25519Ecdh1PUv3Kek)(recipient, recipientSecret, senderPublicKey, alg); if (!kek) return null; // Content Encryption Key const sealedCek = (0, util_js_1.toSealed)(recipient.encrypted_key, header.tag); const cek = await (0, xc20pDir_js_1.xc20pDirDecrypter)(kek).decrypt(sealedCek, (0, util_js_1.base64ToBytes)(header.iv)); if (cek === null) return null; return (0, xc20pDir_js_1.xc20pDirDecrypter)(cek).decrypt(sealed, iv, aad); } return { alg, enc, decrypt }; } //# sourceMappingURL=xc20pEncryption.js.map