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
JavaScript
;
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