@hpke/core
Version:
A Hybrid Public Key Encryption (HPKE) core module for various JavaScript runtimes
212 lines (211 loc) • 7.72 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", "@hpke/common"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.X25519 = void 0;
const common_1 = require("@hpke/common");
const ALG_NAME = "X25519";
// deno-fmt-ignore
const PKCS8_ALG_ID_X25519 = new Uint8Array([
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
0x03, 0x2b, 0x65, 0x6e, 0x04, 0x22, 0x04, 0x20,
]);
class X25519 extends common_1.NativeAlgorithm {
constructor(hkdf) {
super();
Object.defineProperty(this, "_hkdf", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_alg", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_nPk", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_nSk", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_nDh", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_pkcs8AlgId", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this._alg = { name: ALG_NAME };
this._hkdf = hkdf;
this._nPk = 32;
this._nSk = 32;
this._nDh = 32;
this._pkcs8AlgId = PKCS8_ALG_ID_X25519;
}
async serializePublicKey(key) {
await this._setup();
try {
return await this._api.exportKey("raw", key);
}
catch (e) {
throw new common_1.SerializeError(e);
}
}
async deserializePublicKey(key) {
await this._setup();
try {
return await this._importRawKey(key, true);
}
catch (e) {
throw new common_1.DeserializeError(e);
}
}
async serializePrivateKey(key) {
await this._setup();
try {
const jwk = await this._api.exportKey("jwk", key);
if (!("d" in jwk)) {
throw new Error("Not private key");
}
return (0, common_1.base64UrlToBytes)(jwk["d"]).buffer;
}
catch (e) {
throw new common_1.SerializeError(e);
}
}
async deserializePrivateKey(key) {
await this._setup();
try {
return await this._importRawKey(key, false);
}
catch (e) {
throw new common_1.DeserializeError(e);
}
}
async importKey(format, key, isPublic) {
await this._setup();
try {
if (format === "raw") {
return await this._importRawKey(key, isPublic);
}
// jwk
if (key instanceof ArrayBuffer) {
throw new Error("Invalid jwk key format");
}
return await this._importJWK(key, isPublic);
}
catch (e) {
throw new common_1.DeserializeError(e);
}
}
async generateKeyPair() {
await this._setup();
try {
return await this._api.generateKey(ALG_NAME, true, common_1.KEM_USAGES);
}
catch (e) {
throw new common_1.NotSupportedError(e);
}
}
async deriveKeyPair(ikm) {
await this._setup();
try {
const dkpPrk = await this._hkdf.labeledExtract(common_1.EMPTY.buffer, common_1.LABEL_DKP_PRK, new Uint8Array(ikm));
const rawSk = await this._hkdf.labeledExpand(dkpPrk, common_1.LABEL_SK, common_1.EMPTY, this._nSk);
const rawSkBytes = new Uint8Array(rawSk);
const sk = await this._deserializePkcs8Key(rawSkBytes);
rawSkBytes.fill(0);
return {
privateKey: sk,
publicKey: await this.derivePublicKey(sk),
};
}
catch (e) {
throw new common_1.DeriveKeyPairError(e);
}
}
async derivePublicKey(key) {
await this._setup();
try {
const jwk = await this._api.exportKey("jwk", key);
delete jwk["d"];
delete jwk["key_ops"];
return await this._api.importKey("jwk", jwk, this._alg, true, []);
}
catch (e) {
throw new common_1.DeserializeError(e);
}
}
async dh(sk, pk) {
await this._setup();
try {
const bits = await this._api.deriveBits({
name: ALG_NAME,
public: pk,
}, sk, this._nDh * 8);
return bits;
}
catch (e) {
throw new common_1.SerializeError(e);
}
}
async _importRawKey(key, isPublic) {
if (isPublic && key.byteLength !== this._nPk) {
throw new Error("Invalid public key for the ciphersuite");
}
if (!isPublic && key.byteLength !== this._nSk) {
throw new Error("Invalid private key for the ciphersuite");
}
if (isPublic) {
return await this._api.importKey("raw", key, this._alg, true, []);
}
return await this._deserializePkcs8Key(new Uint8Array(key));
}
async _importJWK(key, isPublic) {
if (typeof key.kty === "undefined" || key.kty !== "OKP") {
throw new Error(`Invalid kty: ${key.crv}`);
}
if (typeof key.crv === "undefined" || key.crv !== ALG_NAME) {
throw new Error(`Invalid crv: ${key.crv}`);
}
if (isPublic) {
if (typeof key.d !== "undefined") {
throw new Error("Invalid key: `d` should not be set");
}
return await this._api.importKey("jwk", key, this._alg, true, []);
}
if (typeof key.d === "undefined") {
throw new Error("Invalid key: `d` not found");
}
return await this._api.importKey("jwk", key, this._alg, true, common_1.KEM_USAGES);
}
async _deserializePkcs8Key(k) {
const pkcs8Key = new Uint8Array(this._pkcs8AlgId.length + k.length);
pkcs8Key.set(this._pkcs8AlgId, 0);
pkcs8Key.set(k, this._pkcs8AlgId.length);
return await this._api.importKey("pkcs8", pkcs8Key, this._alg, true, common_1.KEM_USAGES);
}
}
exports.X25519 = X25519;
});