@hpke/common
Version:
A Hybrid Public Key Encryption (HPKE) internal-use common module for @hpke family modules.
242 lines (241 loc) • 10.7 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", "../identifiers.js", "../interfaces/dhkemPrimitives.js", "../interfaces/kemInterface.js", "../utils/misc.js", "../xCryptoKey.js"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hybridkem = void 0;
const consts_js_1 = require("../consts.js");
const errors_js_1 = require("../errors.js");
const identifiers_js_1 = require("../identifiers.js");
const dhkemPrimitives_js_1 = require("../interfaces/dhkemPrimitives.js");
const kemInterface_js_1 = require("../interfaces/kemInterface.js");
const misc_js_1 = require("../utils/misc.js");
const xCryptoKey_js_1 = require("../xCryptoKey.js");
class Hybridkem {
constructor(id, a, b, kdf) {
Object.defineProperty(this, "id", {
enumerable: true,
configurable: true,
writable: true,
value: identifiers_js_1.KemId.NotAssigned
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: ""
});
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, "_a", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_b", {
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._a = a;
this._b = b;
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) {
try {
return await this._serializePublicKey(key);
}
catch (e) {
throw new errors_js_1.SerializeError(e);
}
}
async deserializePublicKey(key) {
try {
return await this._deserializePublicKey(key);
}
catch (e) {
throw new errors_js_1.DeserializeError(e);
}
}
async serializePrivateKey(key) {
try {
return await this._serializePrivateKey(key);
}
catch (e) {
throw new errors_js_1.SerializeError(e);
}
}
async deserializePrivateKey(key) {
try {
return await this._deserializePrivateKey(key);
}
catch (e) {
throw new errors_js_1.DeserializeError(e);
}
}
async generateKeyPair() {
const kpA = await this._a.generateKeyPair();
const kpB = await this._b.generateKeyPair();
const pkA = await this._a.serializePublicKey(kpA.publicKey);
const skA = await this._a.serializePrivateKey(kpA.privateKey);
const pkB = await this._b.serializePublicKey(kpB.publicKey);
const skB = await this._b.serializePrivateKey(kpB.privateKey);
return {
publicKey: await this.deserializePublicKey((0, misc_js_1.concat)(new Uint8Array(pkA), new Uint8Array(pkB)).buffer),
privateKey: await this.deserializePrivateKey((0, misc_js_1.concat)(new Uint8Array(skA), new Uint8Array(skB)).buffer),
};
}
async deriveKeyPair(ikm) {
const dkpPrk = await this._kdf.labeledExtract(consts_js_1.EMPTY.buffer, dhkemPrimitives_js_1.LABEL_DKP_PRK, new Uint8Array(ikm));
const seed = new Uint8Array(await this._kdf.labeledExpand(dkpPrk, dhkemPrimitives_js_1.LABEL_SK, consts_js_1.EMPTY, 32 + 64));
const seed1 = seed.slice(0, 32);
const seed2 = seed.slice(32, 96);
const kpA = await this._a.deriveKeyPair(seed1.buffer);
const kpB = await this._b.deriveKeyPair(seed2.buffer);
const pkA = await this._a.serializePublicKey(kpA.publicKey);
const skA = await this._a.serializePrivateKey(kpA.privateKey);
const pkB = await this._b.serializePublicKey(kpB.publicKey);
const skB = await this._b.serializePrivateKey(kpB.privateKey);
return {
publicKey: await this.deserializePublicKey((0, misc_js_1.concat)(new Uint8Array(pkA), new Uint8Array(pkB)).buffer),
privateKey: await this.deserializePrivateKey((0, misc_js_1.concat)(new Uint8Array(skA), new Uint8Array(skB)).buffer),
};
}
async importKey(format, key, isPublic = true) {
if (format !== "raw") {
throw new errors_js_1.NotSupportedError("'jwk' is not supported");
}
if (!(key instanceof ArrayBuffer)) {
throw new errors_js_1.InvalidParamError("Invalid type of key");
}
if (isPublic) {
return await this.deserializePublicKey(key);
}
return await this.deserializePrivateKey(key);
}
async encap(params) {
let ekmA = undefined;
let ekmB = undefined;
if (params.ekm !== undefined && !(0, misc_js_1.isCryptoKeyPair)(params.ekm)) {
if (params.ekm.byteLength !== 64) {
throw new errors_js_1.InvalidParamError("ekm must be 64 bytes in length");
}
ekmA = params.ekm.slice(0, 32);
ekmB = params.ekm.slice(32);
}
const pkR = new Uint8Array(await this.serializePublicKey(params.recipientPublicKey));
const pkRA = await this._a.deserializePublicKey(pkR.slice(0, this._a.publicKeySize).buffer);
const pkRB = await this._b.deserializePublicKey(pkR.slice(this._a.publicKeySize).buffer);
const resA = await this._a.encap({ recipientPublicKey: pkRA, ekm: ekmA });
const resB = await this._b.encap({ recipientPublicKey: pkRB, ekm: ekmB });
return {
sharedSecret: (0, misc_js_1.concat)(new Uint8Array(resA.sharedSecret), new Uint8Array(resB.sharedSecret)).buffer,
enc: (0, misc_js_1.concat)(new Uint8Array(resA.enc), new Uint8Array(resB.enc))
.buffer,
};
}
async decap(params) {
const sk = (0, misc_js_1.isCryptoKeyPair)(params.recipientKey)
? params.recipientKey.privateKey
: params.recipientKey;
const skR = new Uint8Array(await this.serializePrivateKey(sk));
const skRA = await this._a.deserializePrivateKey(skR.slice(0, this._a.privateKeySize).buffer);
const skRB = await this._b.deserializePrivateKey(skR.slice(this._a.privateKeySize).buffer);
const ssA = await this._a.decap({
recipientKey: skRA,
enc: params.enc.slice(0, this._a.encSize),
});
const ssB = await this._b.decap({
recipientKey: skRB,
enc: params.enc.slice(this._a.encSize),
});
return (0, misc_js_1.concat)(new Uint8Array(ssA), new Uint8Array(ssB))
.buffer;
}
_serializePublicKey(k) {
return new Promise((resolve, reject) => {
if (k.type !== "public") {
reject(new Error("Not public key"));
}
if (k.algorithm.name !== this.name) {
reject(new Error(`Invalid algorithm name: ${k.algorithm.name}`));
}
if (k.key.byteLength !== this.publicKeySize) {
reject(new Error(`Invalid key length: ${k.key.byteLength}`));
}
resolve(k.key.buffer);
});
}
_deserializePublicKey(k) {
return new Promise((resolve, reject) => {
if (k.byteLength !== this.publicKeySize) {
reject(new Error(`Invalid key length: ${k.byteLength}`));
}
resolve(new xCryptoKey_js_1.XCryptoKey(this.name, new Uint8Array(k), "public"));
});
}
_serializePrivateKey(k) {
return new Promise((resolve, reject) => {
if (k.type !== "private") {
reject(new Error("Not private key"));
}
if (k.algorithm.name !== this.name) {
reject(new Error(`Invalid algorithm name: ${k.algorithm.name}`));
}
if (k.key.byteLength !== this.privateKeySize) {
reject(new Error(`Invalid key length: ${k.key.byteLength}`));
}
resolve(k.key.buffer);
});
}
_deserializePrivateKey(k) {
return new Promise((resolve, reject) => {
if (k.byteLength !== this.privateKeySize) {
reject(new Error(`Invalid key length: ${k.byteLength}`));
}
resolve(new xCryptoKey_js_1.XCryptoKey(this.name, new Uint8Array(k), "private", ["deriveBits"]));
});
}
}
exports.Hybridkem = Hybridkem;
});