mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-07-01 18:42:30 +00:00
703 lines
29 KiB
JavaScript
703 lines
29 KiB
JavaScript
// GPG4Browsers - An OpenPGP implementation in javascript
|
|
// Copyright (C) 2011 Recurity Labs GmbH
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 3.0 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
// The GPG4Browsers crypto interface
|
|
|
|
/**
|
|
* @fileoverview Provides functions for asymmetric encryption and decryption as
|
|
* well as key generation and parameter handling for all public-key cryptosystems.
|
|
* @module crypto/crypto
|
|
*/
|
|
|
|
import publicKey from './public_key';
|
|
import mode from './mode';
|
|
import { getRandomBytes } from './random';
|
|
import { getCipherParams } from './cipher';
|
|
import ECDHSymkey from '../type/ecdh_symkey';
|
|
import hash from './hash';
|
|
import KDFParams from '../type/kdf_params';
|
|
import { SymAlgoEnum, AEADEnum, HashEnum } from '../type/enum';
|
|
import enums from '../enums';
|
|
import util from '../util';
|
|
import OID from '../type/oid';
|
|
import { UnsupportedError } from '../packet/packet';
|
|
import ECDHXSymmetricKey from '../type/ecdh_x_symkey';
|
|
|
|
/**
|
|
* Encrypts data using specified algorithm and public key parameters.
|
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
|
|
* @param {module:enums.publicKey} keyAlgo - Public key algorithm
|
|
* @param {module:enums.symmetric|null} symmetricAlgo - Cipher algorithm (v3 only)
|
|
* @param {Object} publicParams - Algorithm-specific public key parameters
|
|
* @param {Object} privateParams - Algorithm-specific private key parameters
|
|
* @param {Uint8Array} data - Data to be encrypted
|
|
* @param {Uint8Array} fingerprint - Recipient fingerprint
|
|
* @returns {Promise<Object>} Encrypted session key parameters.
|
|
* @async
|
|
*/
|
|
export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, privateParams, data, fingerprint) {
|
|
switch (keyAlgo) {
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign: {
|
|
const { n, e } = publicParams;
|
|
const c = await publicKey.rsa.encrypt(data, n, e);
|
|
return { c };
|
|
}
|
|
case enums.publicKey.elgamal: {
|
|
const { p, g, y } = publicParams;
|
|
return publicKey.elgamal.encrypt(data, p, g, y);
|
|
}
|
|
case enums.publicKey.ecdh: {
|
|
const { oid, Q, kdfParams } = publicParams;
|
|
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
|
oid, kdfParams, data, Q, fingerprint);
|
|
return { V, C: new ECDHSymkey(C) };
|
|
}
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
if (symmetricAlgo && !util.isAES(symmetricAlgo)) {
|
|
// see https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/276
|
|
throw new Error('X25519 and X448 keys can only encrypt AES session keys');
|
|
}
|
|
const { A } = publicParams;
|
|
const { ephemeralPublicKey, wrappedKey } = await publicKey.elliptic.ecdhX.encrypt(
|
|
keyAlgo, data, A);
|
|
const C = ECDHXSymmetricKey.fromObject({ algorithm: symmetricAlgo, wrappedKey });
|
|
return { ephemeralPublicKey, C };
|
|
}
|
|
case enums.publicKey.aead: {
|
|
if (!privateParams) {
|
|
throw new Error('Cannot encrypt with symmetric key missing private parameters');
|
|
}
|
|
const { symAlgo, aeadMode } = publicParams;
|
|
const { keyMaterial } = privateParams;
|
|
|
|
const mode = getAEADMode(aeadMode.getValue());
|
|
const { ivLength } = mode;
|
|
const iv = getRandomBytes(ivLength);
|
|
const modeInstance = await mode(symAlgo.getValue(), keyMaterial);
|
|
const ciphertext = await modeInstance.encrypt(data, iv, new Uint8Array());
|
|
const ivAndCiphertext = util.concatUint8Array([iv, ciphertext]);
|
|
return { ivAndCiphertext };
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const { eccPublicKey, mlkemPublicKey } = publicParams;
|
|
const { eccCipherText, mlkemCipherText, wrappedKey } = await publicKey.postQuantum.kem.encrypt(keyAlgo, eccPublicKey, mlkemPublicKey, data);
|
|
const C = ECDHXSymmetricKey.fromObject({ algorithm: symmetricAlgo, wrappedKey });
|
|
return { eccCipherText, mlkemCipherText, C };
|
|
}
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decrypts data using specified algorithm and private key parameters.
|
|
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
|
|
* @param {module:enums.publicKey} algo - Public key algorithm
|
|
* @param {Object} publicKeyParams - Algorithm-specific public key parameters
|
|
* @param {Object} privateKeyParams - Algorithm-specific private key parameters
|
|
* @param {Object} sessionKeyParams - Encrypted session key parameters
|
|
* @param {Uint8Array} fingerprint - Recipient fingerprint
|
|
* @param {Uint8Array} [randomPayload] - Data to return on decryption error, instead of throwing
|
|
* (needed for constant-time processing in RSA and ElGamal)
|
|
* @returns {Promise<Uint8Array>} Decrypted data.
|
|
* @throws {Error} on sensitive decryption error, unless `randomPayload` is given
|
|
* @async
|
|
*/
|
|
export async function publicKeyDecrypt(keyAlgo, publicKeyParams, privateKeyParams, sessionKeyParams, fingerprint, randomPayload) {
|
|
switch (keyAlgo) {
|
|
case enums.publicKey.rsaEncryptSign:
|
|
case enums.publicKey.rsaEncrypt: {
|
|
const { c } = sessionKeyParams;
|
|
const { n, e } = publicKeyParams;
|
|
const { d, p, q, u } = privateKeyParams;
|
|
return publicKey.rsa.decrypt(c, n, e, d, p, q, u, randomPayload);
|
|
}
|
|
case enums.publicKey.elgamal: {
|
|
const { c1, c2 } = sessionKeyParams;
|
|
const p = publicKeyParams.p;
|
|
const x = privateKeyParams.x;
|
|
return publicKey.elgamal.decrypt(c1, c2, p, x, randomPayload);
|
|
}
|
|
case enums.publicKey.ecdh: {
|
|
const { oid, Q, kdfParams } = publicKeyParams;
|
|
const { d } = privateKeyParams;
|
|
const { V, C } = sessionKeyParams;
|
|
return publicKey.elliptic.ecdh.decrypt(
|
|
oid, kdfParams, V, C.data, Q, d, fingerprint);
|
|
}
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
const { A } = publicKeyParams;
|
|
const { k } = privateKeyParams;
|
|
const { ephemeralPublicKey, C } = sessionKeyParams;
|
|
if (C.algorithm !== null && !util.isAES(C.algorithm)) {
|
|
throw new Error('AES session key expected');
|
|
}
|
|
return publicKey.elliptic.ecdhX.decrypt(
|
|
keyAlgo, ephemeralPublicKey, C.wrappedKey, A, k);
|
|
}
|
|
case enums.publicKey.aead: {
|
|
const { symAlgo, aeadMode } = publicKeyParams;
|
|
const { keyMaterial } = privateKeyParams;
|
|
|
|
const { ivAndCiphertext } = sessionKeyParams;
|
|
|
|
const mode = getAEADMode(aeadMode.getValue());
|
|
const { ivLength } = mode;
|
|
const modeInstance = await mode(symAlgo.getValue(), keyMaterial);
|
|
const iv = ivAndCiphertext.subarray(0, ivLength);
|
|
const ciphertext = ivAndCiphertext.subarray(ivLength);
|
|
return modeInstance.decrypt(ciphertext, iv, new Uint8Array());
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const { eccSecretKey, mlkemSecretKey } = privateKeyParams;
|
|
const { eccPublicKey, mlkemPublicKey } = publicKeyParams;
|
|
const { eccCipherText, mlkemCipherText, C } = sessionKeyParams;
|
|
return publicKey.postQuantum.kem.decrypt(keyAlgo, eccCipherText, mlkemCipherText, eccSecretKey, eccPublicKey, mlkemSecretKey, mlkemPublicKey, C.wrappedKey);
|
|
}
|
|
default:
|
|
throw new Error('Unknown public key encryption algorithm.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse public key material in binary form to get the key parameters
|
|
* @param {module:enums.publicKey} algo - The key algorithm
|
|
* @param {Uint8Array} bytes - The key material to parse
|
|
* @returns {{ read: Number, publicParams: Object }} Number of read bytes plus key parameters referenced by name.
|
|
*/
|
|
export function parsePublicKeyParams(algo, bytes) {
|
|
let read = 0;
|
|
switch (algo) {
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign:
|
|
case enums.publicKey.rsaSign: {
|
|
const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
|
|
const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
|
|
return { read, publicParams: { n, e } };
|
|
}
|
|
case enums.publicKey.dsa: {
|
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
|
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
|
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
|
|
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
|
|
return { read, publicParams: { p, q, g, y } };
|
|
}
|
|
case enums.publicKey.elgamal: {
|
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
|
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
|
|
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
|
|
return { read, publicParams: { p, g, y } };
|
|
}
|
|
case enums.publicKey.ecdsa: {
|
|
const oid = new OID(); read += oid.read(bytes);
|
|
checkSupportedCurve(oid);
|
|
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
|
return { read: read, publicParams: { oid, Q } };
|
|
}
|
|
case enums.publicKey.eddsaLegacy: {
|
|
const oid = new OID(); read += oid.read(bytes);
|
|
checkSupportedCurve(oid);
|
|
if (oid.getName() !== enums.curve.ed25519Legacy) {
|
|
throw new Error('Unexpected OID for eddsaLegacy');
|
|
}
|
|
let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
|
Q = util.leftPad(Q, 33);
|
|
return { read: read, publicParams: { oid, Q } };
|
|
}
|
|
case enums.publicKey.ecdh: {
|
|
const oid = new OID(); read += oid.read(bytes);
|
|
checkSupportedCurve(oid);
|
|
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
|
const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
|
|
return { read: read, publicParams: { oid, Q, kdfParams } };
|
|
}
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448:
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
const A = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(algo)); read += A.length;
|
|
return { read, publicParams: { A } };
|
|
}
|
|
case enums.publicKey.hmac: {
|
|
const hashAlgo = new HashEnum(); read += hashAlgo.read(bytes);
|
|
const fpSeed = bytes.subarray(read, read + 32); read += 32;
|
|
return { read: read, publicParams: { hashAlgo, fpSeed } };
|
|
}
|
|
case enums.publicKey.aead: {
|
|
const symAlgo = new SymAlgoEnum(); read += symAlgo.read(bytes);
|
|
const aeadMode = new AEADEnum(); read += aeadMode.read(bytes.subarray(read));
|
|
const fpSeed = bytes.subarray(read, read + 32); read += 32;
|
|
return { read: read, publicParams: { symAlgo, aeadMode, fpSeed } };
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccPublicKey.length;
|
|
const mlkemPublicKey = util.readExactSubarray(bytes, read, read + 1184); read += mlkemPublicKey.length;
|
|
return { read, publicParams: { eccPublicKey, mlkemPublicKey } };
|
|
}
|
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
|
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccPublicKey.length;
|
|
const mldsaPublicKey = util.readExactSubarray(bytes, read, read + 1952); read += mldsaPublicKey.length;
|
|
return { read, publicParams: { eccPublicKey, mldsaPublicKey } };
|
|
}
|
|
default:
|
|
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse private key material in binary form to get the key parameters
|
|
* @param {module:enums.publicKey} algo - The key algorithm
|
|
* @param {Uint8Array} bytes - The key material to parse
|
|
* @param {Object} publicParams - (ECC and symmetric only) public params, needed to format some private params
|
|
* @returns {{ read: Number, privateParams: Object }} Number of read bytes plus the key parameters referenced by name.
|
|
*/
|
|
export async function parsePrivateKeyParams(algo, bytes, publicParams) {
|
|
let read = 0;
|
|
switch (algo) {
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign:
|
|
case enums.publicKey.rsaSign: {
|
|
const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
|
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
|
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
|
const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
|
|
return { read, privateParams: { d, p, q, u } };
|
|
}
|
|
case enums.publicKey.dsa:
|
|
case enums.publicKey.elgamal: {
|
|
const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
|
|
return { read, privateParams: { x } };
|
|
}
|
|
case enums.publicKey.ecdsa:
|
|
case enums.publicKey.ecdh: {
|
|
const payloadSize = getCurvePayloadSize(algo, publicParams.oid);
|
|
let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
|
|
d = util.leftPad(d, payloadSize);
|
|
return { read, privateParams: { d } };
|
|
}
|
|
case enums.publicKey.eddsaLegacy: {
|
|
const payloadSize = getCurvePayloadSize(algo, publicParams.oid);
|
|
if (publicParams.oid.getName() !== enums.curve.ed25519Legacy) {
|
|
throw new Error('Unexpected OID for eddsaLegacy');
|
|
}
|
|
let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
|
|
seed = util.leftPad(seed, payloadSize);
|
|
return { read, privateParams: { seed } };
|
|
}
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448: {
|
|
const payloadSize = getCurvePayloadSize(algo);
|
|
const seed = util.readExactSubarray(bytes, read, read + payloadSize); read += seed.length;
|
|
return { read, privateParams: { seed } };
|
|
}
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
const payloadSize = getCurvePayloadSize(algo);
|
|
const k = util.readExactSubarray(bytes, read, read + payloadSize); read += k.length;
|
|
return { read, privateParams: { k } };
|
|
}
|
|
case enums.publicKey.hmac: {
|
|
const { hashAlgo } = publicParams;
|
|
const keySize = hash.getHashByteLength(hashAlgo.getValue());
|
|
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
|
|
return { read, privateParams: { keyMaterial } };
|
|
}
|
|
case enums.publicKey.aead: {
|
|
const { symAlgo } = publicParams;
|
|
const { keySize } = getCipherParams(symAlgo.getValue());
|
|
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
|
|
return { read, privateParams: { keyMaterial } };
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccSecretKey.length;
|
|
const mlkemSeed = util.readExactSubarray(bytes, read, read + 64); read += mlkemSeed.length;
|
|
const { mlkemSecretKey } = await publicKey.postQuantum.kem.mlkemExpandSecretSeed(algo, mlkemSeed);
|
|
return { read, privateParams: { eccSecretKey, mlkemSecretKey, mlkemSeed } };
|
|
}
|
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
|
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
|
|
const mldsaSeed = util.readExactSubarray(bytes, read, read + 32); read += mldsaSeed.length;
|
|
const { mldsaSecretKey } = await publicKey.postQuantum.signature.mldsaExpandSecretSeed(algo, mldsaSeed);
|
|
return { read, privateParams: { eccSecretKey, mldsaSecretKey, mldsaSeed } };
|
|
}
|
|
default:
|
|
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
|
}
|
|
}
|
|
|
|
/** Returns the types comprising the encrypted session key of an algorithm
|
|
* @param {module:enums.publicKey} algo - The key algorithm
|
|
* @param {Uint8Array} bytes - The key material to parse
|
|
* @returns {Object} The session key parameters referenced by name.
|
|
*/
|
|
export function parseEncSessionKeyParams(algo, bytes) {
|
|
let read = 0;
|
|
switch (algo) {
|
|
// Algorithm-Specific Fields for RSA encrypted session keys:
|
|
// - MPI of RSA encrypted value m**e mod n.
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign: {
|
|
const c = util.readMPI(bytes.subarray(read));
|
|
return { c };
|
|
}
|
|
|
|
// Algorithm-Specific Fields for Elgamal encrypted session keys:
|
|
// - MPI of Elgamal value g**k mod p
|
|
// - MPI of Elgamal value m * y**k mod p
|
|
case enums.publicKey.elgamal: {
|
|
const c1 = util.readMPI(bytes.subarray(read)); read += c1.length + 2;
|
|
const c2 = util.readMPI(bytes.subarray(read));
|
|
return { c1, c2 };
|
|
}
|
|
// Algorithm-Specific Fields for ECDH encrypted session keys:
|
|
// - MPI containing the ephemeral key used to establish the shared secret
|
|
// - ECDH Symmetric Key
|
|
case enums.publicKey.ecdh: {
|
|
const V = util.readMPI(bytes.subarray(read)); read += V.length + 2;
|
|
const C = new ECDHSymkey(); C.read(bytes.subarray(read));
|
|
return { V, C };
|
|
}
|
|
// Algorithm-Specific Fields for X25519 or X448 encrypted session keys:
|
|
// - 32 octets representing an ephemeral X25519 public key (or 57 octets for X448).
|
|
// - A one-octet size of the following fields.
|
|
// - The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
|
|
// - The encrypted session key.
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
const pointSize = getCurvePayloadSize(algo);
|
|
const ephemeralPublicKey = util.readExactSubarray(bytes, read, read + pointSize); read += ephemeralPublicKey.length;
|
|
const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read));
|
|
return { ephemeralPublicKey, C };
|
|
}
|
|
// Algorithm-Specific Fields for symmetric AEAD encryption:
|
|
// - Starting initialization vector
|
|
// - Symmetric key encryption of "m" using cipher and AEAD mode
|
|
// - An authentication tag generated by the AEAD mode.
|
|
case enums.publicKey.aead: {
|
|
const ivAndCiphertext = bytes;
|
|
|
|
return { ivAndCiphertext };
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const eccCipherText = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccCipherText.length;
|
|
const mlkemCipherText = util.readExactSubarray(bytes, read, read + 1088); read += mlkemCipherText.length;
|
|
const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read));
|
|
return { eccCipherText, mlkemCipherText, C }; // eccCipherText || mlkemCipherText || len(C) || C
|
|
}
|
|
default:
|
|
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert params to MPI and serializes them in the proper order
|
|
* @param {module:enums.publicKey} algo - The public key algorithm
|
|
* @param {Object} params - The key parameters indexed by name
|
|
* @returns {Uint8Array} The array containing the MPIs.
|
|
*/
|
|
export function serializeParams(algo, params) {
|
|
// Some algorithms do not rely on MPIs to store the binary params
|
|
const algosWithNativeRepresentation = new Set([
|
|
enums.publicKey.ed25519,
|
|
enums.publicKey.x25519,
|
|
enums.publicKey.ed448,
|
|
enums.publicKey.x448,
|
|
enums.publicKey.aead,
|
|
enums.publicKey.hmac,
|
|
enums.publicKey.pqc_mlkem_x25519,
|
|
enums.publicKey.pqc_mldsa_ed25519
|
|
]);
|
|
|
|
const excludedFields = {
|
|
[enums.publicKey.pqc_mlkem_x25519]: new Set(['mlkemSecretKey']), // only `mlkemSeed` is serialized
|
|
[enums.publicKey.pqc_mldsa_ed25519]: new Set(['mldsaSecretKey']) // only `mldsaSeed` is serialized
|
|
};
|
|
|
|
const orderedParams = Object.keys(params).map(name => {
|
|
if (excludedFields[algo]?.has(name)) {
|
|
return new Uint8Array();
|
|
}
|
|
|
|
const param = params[name];
|
|
if (!util.isUint8Array(param)) return param.write();
|
|
return algosWithNativeRepresentation.has(algo) ? param : util.uint8ArrayToMPI(param);
|
|
});
|
|
return util.concatUint8Array(orderedParams);
|
|
}
|
|
|
|
/**
|
|
* Generate algorithm-specific key parameters
|
|
* @param {module:enums.publicKey} algo - The public key algorithm
|
|
* @param {Integer} bits - Bit length for RSA keys
|
|
* @param {module:type/oid} oid - Object identifier for ECC keys
|
|
* @param {module:enums.symmetric|enums.hash} symmetric - Hash or cipher algorithm for symmetric keys
|
|
* @param {module:enums.aead} aeadMode - AEAD mode for AEAD keys
|
|
* @returns {Promise<{ publicParams: {Object}, privateParams: {Object} }>} The parameters referenced by name.
|
|
* @async
|
|
*/
|
|
export async function generateParams(algo, bits, oid, symmetric, aeadMode) {
|
|
switch (algo) {
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign:
|
|
case enums.publicKey.rsaSign:
|
|
return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
|
|
privateParams: { d, p, q, u },
|
|
publicParams: { n, e }
|
|
}));
|
|
case enums.publicKey.ecdsa:
|
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
|
|
privateParams: { d: secret },
|
|
publicParams: { oid: new OID(oid), Q }
|
|
}));
|
|
case enums.publicKey.eddsaLegacy:
|
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
|
|
privateParams: { seed: secret },
|
|
publicParams: { oid: new OID(oid), Q }
|
|
}));
|
|
case enums.publicKey.ecdh:
|
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
|
|
privateParams: { d: secret },
|
|
publicParams: {
|
|
oid: new OID(oid),
|
|
Q,
|
|
kdfParams: new KDFParams({ hash, cipher })
|
|
}
|
|
}));
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448:
|
|
return publicKey.elliptic.eddsa.generate(algo).then(({ A, seed }) => ({
|
|
privateParams: { seed },
|
|
publicParams: { A }
|
|
}));
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448:
|
|
return publicKey.elliptic.ecdhX.generate(algo).then(({ A, k }) => ({
|
|
privateParams: { k },
|
|
publicParams: { A }
|
|
}));
|
|
case enums.publicKey.hmac: {
|
|
const keyMaterial = await publicKey.hmac.generate(symmetric);
|
|
const fpSeed = getRandomBytes(32);
|
|
return {
|
|
privateParams: {
|
|
keyMaterial
|
|
},
|
|
publicParams: {
|
|
hashAlgo: new HashEnum(symmetric),
|
|
fpSeed
|
|
}
|
|
};
|
|
}
|
|
case enums.publicKey.aead: {
|
|
const keyMaterial = generateSessionKey(symmetric);
|
|
const fpSeed = getRandomBytes(32);
|
|
return {
|
|
privateParams: {
|
|
keyMaterial
|
|
},
|
|
publicParams: {
|
|
symAlgo: new SymAlgoEnum(symmetric),
|
|
aeadMode: new AEADEnum(aeadMode),
|
|
fpSeed
|
|
}
|
|
};
|
|
}
|
|
case enums.publicKey.pqc_mlkem_x25519:
|
|
return publicKey.postQuantum.kem.generate(algo).then(({ eccSecretKey, eccPublicKey, mlkemSeed, mlkemSecretKey, mlkemPublicKey }) => ({
|
|
privateParams: { eccSecretKey, mlkemSeed, mlkemSecretKey },
|
|
publicParams: { eccPublicKey, mlkemPublicKey }
|
|
}));
|
|
case enums.publicKey.pqc_mldsa_ed25519:
|
|
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey }) => ({
|
|
privateParams: { eccSecretKey, mldsaSeed, mldsaSecretKey },
|
|
publicParams: { eccPublicKey, mldsaPublicKey }
|
|
}));
|
|
case enums.publicKey.dsa:
|
|
case enums.publicKey.elgamal:
|
|
throw new Error('Unsupported algorithm for key generation.');
|
|
default:
|
|
throw new Error('Unknown public key algorithm.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate algorithm-specific key parameters
|
|
* @param {module:enums.publicKey} algo - The public key algorithm
|
|
* @param {Object} publicParams - Algorithm-specific public key parameters
|
|
* @param {Object} privateParams - Algorithm-specific private key parameters
|
|
* @returns {Promise<Boolean>} Whether the parameters are valid.
|
|
* @async
|
|
*/
|
|
export async function validateParams(algo, publicParams, privateParams) {
|
|
if (!publicParams || !privateParams) {
|
|
throw new Error('Missing key parameters');
|
|
}
|
|
switch (algo) {
|
|
case enums.publicKey.rsaEncrypt:
|
|
case enums.publicKey.rsaEncryptSign:
|
|
case enums.publicKey.rsaSign: {
|
|
const { n, e } = publicParams;
|
|
const { d, p, q, u } = privateParams;
|
|
return publicKey.rsa.validateParams(n, e, d, p, q, u);
|
|
}
|
|
case enums.publicKey.dsa: {
|
|
const { p, q, g, y } = publicParams;
|
|
const { x } = privateParams;
|
|
return publicKey.dsa.validateParams(p, q, g, y, x);
|
|
}
|
|
case enums.publicKey.elgamal: {
|
|
const { p, g, y } = publicParams;
|
|
const { x } = privateParams;
|
|
return publicKey.elgamal.validateParams(p, g, y, x);
|
|
}
|
|
case enums.publicKey.ecdsa:
|
|
case enums.publicKey.ecdh: {
|
|
const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
|
|
const { oid, Q } = publicParams;
|
|
const { d } = privateParams;
|
|
return algoModule.validateParams(oid, Q, d);
|
|
}
|
|
case enums.publicKey.eddsaLegacy: {
|
|
const { Q, oid } = publicParams;
|
|
const { seed } = privateParams;
|
|
return publicKey.elliptic.eddsaLegacy.validateParams(oid, Q, seed);
|
|
}
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448: {
|
|
const { A } = publicParams;
|
|
const { seed } = privateParams;
|
|
return publicKey.elliptic.eddsa.validateParams(algo, A, seed);
|
|
}
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448: {
|
|
const { A } = publicParams;
|
|
const { k } = privateParams;
|
|
return publicKey.elliptic.ecdhX.validateParams(algo, A, k);
|
|
}
|
|
case enums.publicKey.hmac:
|
|
case enums.publicKey.aead:
|
|
throw new Error('Persistent symmetric keys must be encrypted using AEAD');
|
|
case enums.publicKey.pqc_mlkem_x25519: {
|
|
const { eccSecretKey, mlkemSeed } = privateParams;
|
|
const { eccPublicKey, mlkemPublicKey } = publicParams;
|
|
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed);
|
|
}
|
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
|
const { eccSecretKey, mldsaSeed } = privateParams;
|
|
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
|
return publicKey.postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed);
|
|
}
|
|
default:
|
|
throw new Error('Unknown public key algorithm.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a random byte prefix for the specified algorithm
|
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
|
|
* @param {module:enums.symmetric} algo - Symmetric encryption algorithm
|
|
* @returns {Promise<Uint8Array>} Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.
|
|
* @async
|
|
*/
|
|
export async function getPrefixRandom(algo) {
|
|
const { blockSize } = getCipherParams(algo);
|
|
const prefixrandom = await getRandomBytes(blockSize);
|
|
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
|
|
return util.concat([prefixrandom, repeat]);
|
|
}
|
|
|
|
/**
|
|
* Generating a session key for the specified symmetric algorithm
|
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
|
|
* @param {module:enums.symmetric} algo - Symmetric encryption algorithm
|
|
* @returns {Uint8Array} Random bytes as a string to be used as a key.
|
|
*/
|
|
export function generateSessionKey(algo) {
|
|
const { keySize } = getCipherParams(algo);
|
|
return getRandomBytes(keySize);
|
|
}
|
|
|
|
/**
|
|
* Get implementation of the given AEAD mode
|
|
* @param {enums.aead} algo
|
|
* @returns {Object}
|
|
* @throws {Error} on invalid algo
|
|
*/
|
|
export function getAEADMode(algo) {
|
|
const algoName = enums.read(enums.aead, algo);
|
|
return mode[algoName];
|
|
}
|
|
|
|
/**
|
|
* Check whether the given curve OID is supported
|
|
* @param {module:type/oid} oid - EC object identifier
|
|
* @throws {UnsupportedError} if curve is not supported
|
|
*/
|
|
function checkSupportedCurve(oid) {
|
|
try {
|
|
oid.getName();
|
|
} catch (e) {
|
|
throw new UnsupportedError('Unknown curve OID');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get encoded secret size for a given elliptic algo
|
|
* @param {module:enums.publicKey} algo - alrogithm identifier
|
|
* @param {module:type/oid} [oid] - curve OID if needed by algo
|
|
*/
|
|
export function getCurvePayloadSize(algo, oid) {
|
|
switch (algo) {
|
|
case enums.publicKey.ecdsa:
|
|
case enums.publicKey.ecdh:
|
|
case enums.publicKey.eddsaLegacy:
|
|
return new publicKey.elliptic.CurveWithOID(oid).payloadSize;
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448:
|
|
return publicKey.elliptic.eddsa.getPayloadSize(algo);
|
|
case enums.publicKey.x25519:
|
|
case enums.publicKey.x448:
|
|
return publicKey.elliptic.ecdhX.getPayloadSize(algo);
|
|
default:
|
|
throw new Error('Unknown elliptic algo');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get preferred signing hash algo for a given elliptic algo
|
|
* @param {module:enums.publicKey} algo - alrogithm identifier
|
|
* @param {module:type/oid} [oid] - curve OID if needed by algo
|
|
*/
|
|
export function getPreferredCurveHashAlgo(algo, oid) {
|
|
switch (algo) {
|
|
case enums.publicKey.ecdsa:
|
|
case enums.publicKey.eddsaLegacy:
|
|
return publicKey.elliptic.getPreferredHashAlgo(oid);
|
|
case enums.publicKey.ed25519:
|
|
case enums.publicKey.ed448:
|
|
return publicKey.elliptic.eddsa.getPreferredHashAlgo(algo);
|
|
default:
|
|
throw new Error('Unknown elliptic signing algo');
|
|
}
|
|
}
|
|
|
|
|
|
export { getCipherParams };
|