UNPKG

@hpke/core

Version:

A Hybrid Public Key Encryption (HPKE) core module for various JavaScript runtimes

198 lines (197 loc) 6.41 kB
import { base64UrlToBytes, DeriveKeyPairError, DeserializeError, EMPTY, KEM_USAGES, LABEL_DKP_PRK, LABEL_SK, NativeAlgorithm, NotSupportedError, SerializeError, } from "@hpke/common"; const ALG_NAME = "X448"; // deno-fmt-ignore const PKCS8_ALG_ID_X448 = new Uint8Array([ 0x30, 0x46, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6f, 0x04, 0x3a, 0x04, 0x38, ]); export class X448 extends 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 = 56; this._nSk = 56; this._nDh = 56; this._pkcs8AlgId = PKCS8_ALG_ID_X448; } async serializePublicKey(key) { await this._setup(); try { return await this._api.exportKey("raw", key); } catch (e) { throw new SerializeError(e); } } async deserializePublicKey(key) { await this._setup(); try { return await this._importRawKey(key, true); } catch (e) { throw new 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 base64UrlToBytes(jwk["d"]).buffer; } catch (e) { throw new SerializeError(e); } } async deserializePrivateKey(key) { await this._setup(); try { return await this._importRawKey(key, false); } catch (e) { throw new 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 DeserializeError(e); } } async generateKeyPair() { await this._setup(); try { return await this._api.generateKey(ALG_NAME, true, KEM_USAGES); } catch (e) { throw new NotSupportedError(e); } } async deriveKeyPair(ikm) { await this._setup(); try { const dkpPrk = await this._hkdf.labeledExtract(EMPTY.buffer, LABEL_DKP_PRK, new Uint8Array(ikm)); const rawSk = await this._hkdf.labeledExpand(dkpPrk, LABEL_SK, 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 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 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 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, 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, KEM_USAGES); } }