UNPKG

@hpke/common

Version:

A Hybrid Public Key Encryption (HPKE) internal-use common module for @hpke family modules.

197 lines (196 loc) 7.26 kB
import { EMPTY, INPUT_LENGTH_LIMIT } from "../consts.js"; import { DecapError, EncapError, InvalidParamError } from "../errors.js"; import { SUITE_ID_HEADER_KEM } from "../interfaces/kemInterface.js"; import { toArrayBuffer } from "../kdfs/hkdf.js"; import { concat, i2Osp, isCryptoKeyPair } from "../utils/misc.js"; // b"eae_prk" const LABEL_EAE_PRK = /* @__PURE__ */ new Uint8Array([ 101, 97, 101, 95, 112, 114, 107, ]); // b"shared_secret" // deno-fmt-ignore const LABEL_SHARED_SECRET = /* @__PURE__ */ new Uint8Array([ 115, 104, 97, 114, 101, 100, 95, 115, 101, 99, 114, 101, 116, ]); function concat3(a, b, c) { const ret = new Uint8Array(a.length + b.length + c.length); ret.set(a, 0); ret.set(b, a.length); ret.set(c, a.length + b.length); return ret; } export class Dhkem { constructor(id, prim, kdf) { Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "secretSize", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "encSize", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "publicKeySize", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "privateKeySize", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_prim", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_kdf", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.id = id; this._prim = prim; this._kdf = kdf; const suiteId = new Uint8Array(SUITE_ID_HEADER_KEM); suiteId.set(i2Osp(this.id, 2), 3); this._kdf.init(suiteId); } async serializePublicKey(key) { return await this._prim.serializePublicKey(key); } async deserializePublicKey(key) { return await this._prim.deserializePublicKey(toArrayBuffer(key)); } async serializePrivateKey(key) { return await this._prim.serializePrivateKey(key); } async deserializePrivateKey(key) { return await this._prim.deserializePrivateKey(toArrayBuffer(key)); } async importKey(format, key, isPublic = true) { return await this._prim.importKey(format, key, isPublic); } async generateKeyPair() { return await this._prim.generateKeyPair(); } async deriveKeyPair(ikm) { const rawIkm = toArrayBuffer(ikm); if (rawIkm.byteLength > INPUT_LENGTH_LIMIT) { throw new InvalidParamError("Too long ikm"); } return await this._prim.deriveKeyPair(rawIkm); } async encap(params) { let ke; if (params.ekm === undefined) { ke = await this.generateKeyPair(); } else if (isCryptoKeyPair(params.ekm)) { // params.ekm is only used for testing. ke = params.ekm; } else { // params.ekm is only used for testing. ke = await this.deriveKeyPair(params.ekm); } const enc = await this._prim.serializePublicKey(ke.publicKey); const pkrm = await this._prim.serializePublicKey(params.recipientPublicKey); try { let dh; if (params.senderKey === undefined) { dh = new Uint8Array(await this._prim.dh(ke.privateKey, params.recipientPublicKey)); } else { const sks = isCryptoKeyPair(params.senderKey) ? params.senderKey.privateKey : params.senderKey; const dh1 = new Uint8Array(await this._prim.dh(ke.privateKey, params.recipientPublicKey)); const dh2 = new Uint8Array(await this._prim.dh(sks, params.recipientPublicKey)); dh = concat(dh1, dh2); } let kemContext; if (params.senderKey === undefined) { kemContext = concat(new Uint8Array(enc), new Uint8Array(pkrm)); } else { const pks = isCryptoKeyPair(params.senderKey) ? params.senderKey.publicKey : await this._prim.derivePublicKey(params.senderKey); const pksm = await this._prim.serializePublicKey(pks); kemContext = concat3(new Uint8Array(enc), new Uint8Array(pkrm), new Uint8Array(pksm)); } const sharedSecret = await this._generateSharedSecret(dh, kemContext); return { enc: enc, sharedSecret: sharedSecret, }; } catch (e) { throw new EncapError(e); } } async decap(params) { const enc = toArrayBuffer(params.enc); const pke = await this._prim.deserializePublicKey(enc); const skr = isCryptoKeyPair(params.recipientKey) ? params.recipientKey.privateKey : params.recipientKey; const pkr = isCryptoKeyPair(params.recipientKey) ? params.recipientKey.publicKey : await this._prim.derivePublicKey(params.recipientKey); const pkrm = await this._prim.serializePublicKey(pkr); try { let dh; if (params.senderPublicKey === undefined) { dh = new Uint8Array(await this._prim.dh(skr, pke)); } else { const dh1 = new Uint8Array(await this._prim.dh(skr, pke)); const dh2 = new Uint8Array(await this._prim.dh(skr, params.senderPublicKey)); dh = concat(dh1, dh2); } let kemContext; if (params.senderPublicKey === undefined) { kemContext = concat(new Uint8Array(enc), new Uint8Array(pkrm)); } else { const pksm = await this._prim.serializePublicKey(params.senderPublicKey); kemContext = new Uint8Array(enc.byteLength + pkrm.byteLength + pksm.byteLength); kemContext.set(new Uint8Array(enc), 0); kemContext.set(new Uint8Array(pkrm), enc.byteLength); kemContext.set(new Uint8Array(pksm), enc.byteLength + pkrm.byteLength); } return await this._generateSharedSecret(dh, kemContext); } catch (e) { throw new DecapError(e); } } async _generateSharedSecret(dh, kemContext) { const labeledIkm = this._kdf.buildLabeledIkm(LABEL_EAE_PRK, dh); const labeledInfo = this._kdf.buildLabeledInfo(LABEL_SHARED_SECRET, kemContext, this.secretSize); return await this._kdf.extractAndExpand(EMPTY, labeledIkm, labeledInfo, this.secretSize); } }