@hpke/common
Version:
A Hybrid Public Key Encryption (HPKE) internal-use common module for @hpke family modules.
200 lines (199 loc) • 8.56 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../consts.js", "../errors.js", "../interfaces/kemInterface.js", "../utils/misc.js"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dhkem = void 0;
const consts_js_1 = require("../consts.js");
const errors_js_1 = require("../errors.js");
const kemInterface_js_1 = require("../interfaces/kemInterface.js");
const misc_js_1 = require("../utils/misc.js");
// b"eae_prk"
const LABEL_EAE_PRK = new Uint8Array([101, 97, 101, 95, 112, 114, 107]);
// b"shared_secret"
// deno-fmt-ignore
const LABEL_SHARED_SECRET = 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;
}
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(kemInterface_js_1.SUITE_ID_HEADER_KEM);
suiteId.set((0, misc_js_1.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(key);
}
async serializePrivateKey(key) {
return await this._prim.serializePrivateKey(key);
}
async deserializePrivateKey(key) {
return await this._prim.deserializePrivateKey(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) {
if (ikm.byteLength > consts_js_1.INPUT_LENGTH_LIMIT) {
throw new errors_js_1.InvalidParamError("Too long ikm");
}
return await this._prim.deriveKeyPair(ikm);
}
async encap(params) {
let ke;
if (params.ekm === undefined) {
ke = await this.generateKeyPair();
}
else if ((0, misc_js_1.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 = (0, misc_js_1.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 = (0, misc_js_1.concat)(dh1, dh2);
}
let kemContext;
if (params.senderKey === undefined) {
kemContext = (0, misc_js_1.concat)(new Uint8Array(enc), new Uint8Array(pkrm));
}
else {
const pks = (0, misc_js_1.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 errors_js_1.EncapError(e);
}
}
async decap(params) {
const pke = await this._prim.deserializePublicKey(params.enc);
const skr = (0, misc_js_1.isCryptoKeyPair)(params.recipientKey)
? params.recipientKey.privateKey
: params.recipientKey;
const pkr = (0, misc_js_1.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 = (0, misc_js_1.concat)(dh1, dh2);
}
let kemContext;
if (params.senderPublicKey === undefined) {
kemContext = (0, misc_js_1.concat)(new Uint8Array(params.enc), new Uint8Array(pkrm));
}
else {
const pksm = await this._prim.serializePublicKey(params.senderPublicKey);
kemContext = new Uint8Array(params.enc.byteLength + pkrm.byteLength + pksm.byteLength);
kemContext.set(new Uint8Array(params.enc), 0);
kemContext.set(new Uint8Array(pkrm), params.enc.byteLength);
kemContext.set(new Uint8Array(pksm), params.enc.byteLength + pkrm.byteLength);
}
return await this._generateSharedSecret(dh, kemContext);
}
catch (e) {
throw new errors_js_1.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(consts_js_1.EMPTY.buffer, labeledIkm.buffer, labeledInfo.buffer, this.secretSize);
}
}
exports.Dhkem = Dhkem;
});