Update to draft-ietf-openpgp-persistent-symmetric-keys-00

This commit is contained in:
Daniel Huigens 2024-11-05 12:19:32 +01:00
parent 1fb0127a6d
commit 151ae75959
12 changed files with 99 additions and 138 deletions

View File

@ -28,9 +28,7 @@ import mode from './mode';
import { getRandomBytes } from './random'; import { getRandomBytes } from './random';
import { getCipherParams } from './cipher'; import { getCipherParams } from './cipher';
import ECDHSymkey from '../type/ecdh_symkey'; import ECDHSymkey from '../type/ecdh_symkey';
import ShortByteString from '../type/short_byte_string';
import hash from './hash'; import hash from './hash';
import config from '../config';
import KDFParams from '../type/kdf_params'; import KDFParams from '../type/kdf_params';
import { SymAlgoEnum, AEADEnum, HashEnum } from '../type/enum'; import { SymAlgoEnum, AEADEnum, HashEnum } from '../type/enum';
import enums from '../enums'; import enums from '../enums';
@ -85,16 +83,16 @@ export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, pri
if (!privateParams) { if (!privateParams) {
throw new Error('Cannot encrypt with symmetric key missing private parameters'); throw new Error('Cannot encrypt with symmetric key missing private parameters');
} }
const { cipher: algo } = publicParams; const { symAlgo, aeadMode } = publicParams;
const algoValue = algo.getValue();
const { keyMaterial } = privateParams; const { keyMaterial } = privateParams;
const aeadMode = config.preferredAEADAlgorithm;
const mode = getAEADMode(config.preferredAEADAlgorithm); const mode = getAEADMode(aeadMode.getValue());
const { ivLength } = mode; const { ivLength } = mode;
const iv = getRandomBytes(ivLength); const iv = getRandomBytes(ivLength);
const modeInstance = await mode(algoValue, keyMaterial); const modeInstance = await mode(symAlgo.getValue(), keyMaterial);
const c = await modeInstance.encrypt(data, iv, new Uint8Array()); const ciphertext = await modeInstance.encrypt(data, iv, new Uint8Array());
return { aeadMode: new AEADEnum(aeadMode), iv, c: new ShortByteString(c) }; const ivAndCiphertext = util.concatUint8Array([iv, ciphertext]);
return { ivAndCiphertext };
} }
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const { eccPublicKey, mlkemPublicKey } = publicParams; const { eccPublicKey, mlkemPublicKey } = publicParams;
@ -155,15 +153,17 @@ export async function publicKeyDecrypt(keyAlgo, publicKeyParams, privateKeyParam
keyAlgo, ephemeralPublicKey, C.wrappedKey, A, k); keyAlgo, ephemeralPublicKey, C.wrappedKey, A, k);
} }
case enums.publicKey.aead: { case enums.publicKey.aead: {
const { cipher: algo } = publicKeyParams; const { symAlgo, aeadMode } = publicKeyParams;
const algoValue = algo.getValue();
const { keyMaterial } = privateKeyParams; const { keyMaterial } = privateKeyParams;
const { aeadMode, iv, c } = sessionKeyParams; const { ivAndCiphertext } = sessionKeyParams;
const mode = getAEADMode(aeadMode.getValue()); const mode = getAEADMode(aeadMode.getValue());
const modeInstance = await mode(algoValue, keyMaterial); const { ivLength } = mode;
return modeInstance.decrypt(c.data, iv, new Uint8Array()); 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: { case enums.publicKey.pqc_mlkem_x25519: {
const { eccSecretKey, mlkemSecretKey } = privateKeyParams; const { eccSecretKey, mlkemSecretKey } = privateKeyParams;
@ -235,12 +235,16 @@ export function parsePublicKeyParams(algo, bytes) {
const A = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(algo)); read += A.length; const A = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(algo)); read += A.length;
return { read, publicParams: { A } }; return { read, publicParams: { A } };
} }
case enums.publicKey.hmac: 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: { case enums.publicKey.aead: {
const algo = new SymAlgoEnum(); read += algo.read(bytes); const symAlgo = new SymAlgoEnum(); read += symAlgo.read(bytes);
const digestLength = hash.getHashByteLength(enums.hash.sha256); const aeadMode = new AEADEnum(); read += aeadMode.read(bytes.subarray(read));
const digest = bytes.subarray(read, read + digestLength); read += digestLength; const fpSeed = bytes.subarray(read, read + 32); read += 32;
return { read: read, publicParams: { cipher: algo, digest } }; return { read: read, publicParams: { symAlgo, aeadMode, fpSeed } };
} }
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccPublicKey.length; const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccPublicKey.length;
@ -310,18 +314,16 @@ export async function parsePrivateKeyParams(algo, bytes, publicParams) {
return { read, privateParams: { k } }; return { read, privateParams: { k } };
} }
case enums.publicKey.hmac: { case enums.publicKey.hmac: {
const { cipher: algo } = publicParams; const { hashAlgo } = publicParams;
const keySize = hash.getHashByteLength(algo.getValue()); const keySize = hash.getHashByteLength(hashAlgo.getValue());
const hashSeed = bytes.subarray(read, read + 32); read += 32;
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize; const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
return { read, privateParams: { hashSeed, keyMaterial } }; return { read, privateParams: { keyMaterial } };
} }
case enums.publicKey.aead: { case enums.publicKey.aead: {
const { cipher: algo } = publicParams; const { symAlgo } = publicParams;
const hashSeed = bytes.subarray(read, read + 32); read += 32; const { keySize } = getCipherParams(symAlgo.getValue());
const { keySize } = getCipherParams(algo.getValue());
const keyMaterial = bytes.subarray(read, read + keySize); read += keySize; const keyMaterial = bytes.subarray(read, read + keySize); read += keySize;
return { read, privateParams: { hashSeed, keyMaterial } }; return { read, privateParams: { keyMaterial } };
} }
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccSecretKey.length; const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccSecretKey.length;
@ -385,18 +387,13 @@ export function parseEncSessionKeyParams(algo, bytes) {
return { ephemeralPublicKey, C }; return { ephemeralPublicKey, C };
} }
// Algorithm-Specific Fields for symmetric AEAD encryption: // Algorithm-Specific Fields for symmetric AEAD encryption:
// - AEAD algorithm
// - Starting initialization vector // - Starting initialization vector
// - Symmetric key encryption of "m" dependent on cipher and AEAD mode prefixed with a one-octet length // - Symmetric key encryption of "m" using cipher and AEAD mode
// - An authentication tag generated by the AEAD mode. // - An authentication tag generated by the AEAD mode.
case enums.publicKey.aead: { case enums.publicKey.aead: {
const aeadMode = new AEADEnum(); read += aeadMode.read(bytes.subarray(read)); const ivAndCiphertext = bytes;
const { ivLength } = getAEADMode(aeadMode.getValue());
const iv = bytes.subarray(read, read + ivLength); read += ivLength; return { ivAndCiphertext };
const c = new ShortByteString(); read += c.read(bytes.subarray(read));
return { aeadMode, iv, c };
} }
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const eccCipherText = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccCipherText.length; const eccCipherText = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.x25519)); read += eccCipherText.length;
@ -451,10 +448,11 @@ export function serializeParams(algo, params) {
* @param {Integer} bits - Bit length for RSA keys * @param {Integer} bits - Bit length for RSA keys
* @param {module:type/oid} oid - Object identifier for ECC 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.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. * @returns {Promise<{ publicParams: {Object}, privateParams: {Object} }>} The parameters referenced by name.
* @async * @async
*/ */
export async function generateParams(algo, bits, oid, symmetric) { export async function generateParams(algo, bits, oid, symmetric, aeadMode) {
switch (algo) { switch (algo) {
case enums.publicKey.rsaEncrypt: case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign: case enums.publicKey.rsaEncryptSign:
@ -496,11 +494,30 @@ export async function generateParams(algo, bits, oid, symmetric) {
})); }));
case enums.publicKey.hmac: { case enums.publicKey.hmac: {
const keyMaterial = await publicKey.hmac.generate(symmetric); const keyMaterial = await publicKey.hmac.generate(symmetric);
return createSymmetricParams(keyMaterial, new HashEnum(symmetric)); const fpSeed = getRandomBytes(32);
return {
privateParams: {
keyMaterial
},
publicParams: {
hashAlgo: new HashEnum(symmetric),
fpSeed
}
};
} }
case enums.publicKey.aead: { case enums.publicKey.aead: {
const keyMaterial = generateSessionKey(symmetric); const keyMaterial = generateSessionKey(symmetric);
return createSymmetricParams(keyMaterial, new SymAlgoEnum(symmetric)); const fpSeed = getRandomBytes(32);
return {
privateParams: {
keyMaterial
},
publicParams: {
symAlgo: new SymAlgoEnum(symmetric),
aeadMode: new AEADEnum(aeadMode),
fpSeed
}
};
} }
case enums.publicKey.pqc_mlkem_x25519: case enums.publicKey.pqc_mlkem_x25519:
return publicKey.postQuantum.kem.generate(algo).then(({ eccSecretKey, eccPublicKey, mlkemSeed, mlkemSecretKey, mlkemPublicKey }) => ({ return publicKey.postQuantum.kem.generate(algo).then(({ eccSecretKey, eccPublicKey, mlkemSeed, mlkemSecretKey, mlkemPublicKey }) => ({
@ -520,21 +537,6 @@ export async function generateParams(algo, bits, oid, symmetric) {
} }
} }
async function createSymmetricParams(key, algo) {
const seed = getRandomBytes(32);
const bindingHash = await hash.sha256(seed);
return {
privateParams: {
hashSeed: seed,
keyMaterial: key
},
publicParams: {
cipher: algo,
digest: bindingHash
}
};
}
/** /**
* Validate algorithm-specific key parameters * Validate algorithm-specific key parameters
* @param {module:enums.publicKey} algo - The public key algorithm * @param {module:enums.publicKey} algo - The public key algorithm
@ -589,20 +591,9 @@ export async function validateParams(algo, publicParams, privateParams) {
const { k } = privateParams; const { k } = privateParams;
return publicKey.elliptic.ecdhX.validateParams(algo, A, k); return publicKey.elliptic.ecdhX.validateParams(algo, A, k);
} }
case enums.publicKey.hmac: { case enums.publicKey.hmac:
const { cipher: algo, digest } = publicParams; case enums.publicKey.aead:
const { hashSeed, keyMaterial } = privateParams; throw new Error('Persistent symmetric keys must be encrypted using AEAD');
const keySize = hash.getHashByteLength(algo.getValue());
return keySize === keyMaterial.length &&
util.equalsUint8Array(digest, await hash.sha256(hashSeed));
}
case enums.publicKey.aead: {
const { cipher: algo, digest } = publicParams;
const { hashSeed, keyMaterial } = privateParams;
const { keySize } = getCipherParams(algo.getValue());
return keySize === keyMaterial.length &&
util.equalsUint8Array(digest, await hash.sha256(hashSeed));
}
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const { eccSecretKey, mlkemSeed } = privateParams; const { eccSecretKey, mlkemSeed } = privateParams;
const { eccPublicKey, mlkemPublicKey } = publicParams; const { eccPublicKey, mlkemPublicKey } = publicParams;

View File

@ -1,5 +1,6 @@
import enums from '../../enums'; import enums from '../../enums';
import util from '../../util'; import util from '../../util';
import hash from '../hash';
const supportedHashAlgos = new Set([enums.hash.sha1, enums.hash.sha256, enums.hash.sha512]); const supportedHashAlgos = new Set([enums.hash.sha1, enums.hash.sha256, enums.hash.sha512]);
@ -11,12 +12,14 @@ export async function generate(hashAlgo) {
throw new Error('Unsupported hash algorithm.'); throw new Error('Unsupported hash algorithm.');
} }
const hashName = enums.read(enums.webHash, hashAlgo); const hashName = enums.read(enums.webHash, hashAlgo);
const keySize = hash.getHashByteLength(hashAlgo);
const crypto = webCrypto || nodeCrypto.webcrypto.subtle; const crypto = webCrypto || nodeCrypto.webcrypto.subtle;
const key = await crypto.generateKey( const key = await crypto.generateKey(
{ {
name: 'HMAC', name: 'HMAC',
hash: { name: hashName } hash: { name: hashName },
length: keySize * 8
}, },
true, true,
['sign', 'verify'] ['sign', 'verify']

View File

@ -6,7 +6,6 @@
import publicKey from './public_key'; import publicKey from './public_key';
import enums from '../enums'; import enums from '../enums';
import util from '../util'; import util from '../util';
import ShortByteString from '../type/short_byte_string';
import { UnsupportedError } from '../packet/packet'; import { UnsupportedError } from '../packet/packet';
/** /**
@ -67,7 +66,7 @@ export function parseSignatureParams(algo, signature) {
return { read, signatureParams: { RS } }; return { read, signatureParams: { RS } };
} }
case enums.publicKey.hmac: { case enums.publicKey.hmac: {
const mac = new ShortByteString(); read += mac.read(signature.subarray(read)); const mac = signature; read += signature.length;
return { read, signatureParams: { mac } }; return { read, signatureParams: { mac } };
} }
case enums.publicKey.pqc_mldsa_ed25519: { case enums.publicKey.pqc_mldsa_ed25519: {
@ -136,9 +135,9 @@ export async function verify(algo, hashAlgo, signature, publicParams, privatePar
if (!privateParams) { if (!privateParams) {
throw new Error('Cannot verify HMAC signature with symmetric key missing private parameters'); throw new Error('Cannot verify HMAC signature with symmetric key missing private parameters');
} }
const { cipher: algo } = publicParams; const { hashAlgo } = publicParams;
const { keyMaterial } = privateParams; const { keyMaterial } = privateParams;
return publicKey.hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed); return publicKey.hmac.verify(hashAlgo.getValue(), keyMaterial, signature.mac, hashed);
} }
case enums.publicKey.pqc_mldsa_ed25519: { case enums.publicKey.pqc_mldsa_ed25519: {
const { eccPublicKey, mldsaPublicKey } = publicParams; const { eccPublicKey, mldsaPublicKey } = publicParams;
@ -200,10 +199,10 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da
return publicKey.elliptic.eddsa.sign(algo, hashAlgo, data, A, seed, hashed); return publicKey.elliptic.eddsa.sign(algo, hashAlgo, data, A, seed, hashed);
} }
case enums.publicKey.hmac: { case enums.publicKey.hmac: {
const { cipher: algo } = publicKeyParams; const { hashAlgo } = publicKeyParams;
const { keyMaterial } = privateKeyParams; const { keyMaterial } = privateKeyParams;
const mac = await publicKey.hmac.sign(algo.getValue(), keyMaterial, hashed); const mac = await publicKey.hmac.sign(hashAlgo.getValue(), keyMaterial, hashed);
return { mac: new ShortByteString(mac) }; return { mac };
} }
case enums.publicKey.pqc_mldsa_ed25519: { case enums.publicKey.pqc_mldsa_ed25519: {
const { eccPublicKey } = publicKeyParams; const { eccPublicKey } = publicKeyParams;

View File

@ -102,9 +102,9 @@ export default {
pqc_mldsa_ed25519: 107, pqc_mldsa_ed25519: 107,
/** Persistent symmetric keys: encryption algorithm */ /** Persistent symmetric keys: encryption algorithm */
aead: 100, aead: 128,
/** Persistent symmetric keys: authentication algorithm */ /** Persistent symmetric keys: authentication algorithm */
hmac: 101 hmac: 129
}, },
/** {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2} /** {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2}

View File

@ -17,7 +17,7 @@ export async function generateSecretSubkey(options, config) {
const secretSubkeyPacket = new SecretSubkeyPacket(options.date, config); const secretSubkeyPacket = new SecretSubkeyPacket(options.date, config);
secretSubkeyPacket.packets = null; secretSubkeyPacket.packets = null;
secretSubkeyPacket.algorithm = enums.write(enums.publicKey, options.algorithm); secretSubkeyPacket.algorithm = enums.write(enums.publicKey, options.algorithm);
await secretSubkeyPacket.generate(options.rsaBits, options.curve, options.symmetric); await secretSubkeyPacket.generate(options.rsaBits, options.curve, options.symmetric, config.preferredAEADAlgorithm);
await secretSubkeyPacket.computeFingerprintAndKeyID(); await secretSubkeyPacket.computeFingerprintAndKeyID();
return secretSubkeyPacket; return secretSubkeyPacket;
} }
@ -26,7 +26,7 @@ export async function generateSecretKey(options, config) {
const secretKeyPacket = new SecretKeyPacket(options.date, config); const secretKeyPacket = new SecretKeyPacket(options.date, config);
secretKeyPacket.packets = null; secretKeyPacket.packets = null;
secretKeyPacket.algorithm = enums.write(enums.publicKey, options.algorithm); secretKeyPacket.algorithm = enums.write(enums.publicKey, options.algorithm);
await secretKeyPacket.generate(options.rsaBits, options.curve, options.symmetric); await secretKeyPacket.generate(options.rsaBits, options.curve, options.symmetric, config.preferredAEADAlgorithm);
await secretKeyPacket.computeFingerprintAndKeyID(); await secretKeyPacket.computeFingerprintAndKeyID();
return secretKeyPacket; return secretKeyPacket;
} }
@ -506,6 +506,7 @@ export function validateDecryptionKeyPacket(keyPacket, signature, config) {
case enums.publicKey.ecdh: case enums.publicKey.ecdh:
case enums.publicKey.x25519: case enums.publicKey.x25519:
case enums.publicKey.x448: case enums.publicKey.x448:
case enums.publicKey.aead:
case enums.publicKey.pqc_mlkem_x25519: { case enums.publicKey.pqc_mlkem_x25519: {
const isValidSigningKeyPacket = !signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0; const isValidSigningKeyPacket = !signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0;
if (isValidSigningKeyPacket && config.allowInsecureDecryptionWithSigningKeys) { if (isValidSigningKeyPacket && config.allowInsecureDecryptionWithSigningKeys) {

View File

@ -50,7 +50,7 @@ class PrivateKey extends PublicKey {
switch (keyPacket.constructor.tag) { switch (keyPacket.constructor.tag) {
case enums.packet.secretKey: { case enums.packet.secretKey: {
if (keyPacket.algorithm === enums.publicKey.aead || keyPacket.algorithm === enums.publicKey.hmac) { if (keyPacket.algorithm === enums.publicKey.aead || keyPacket.algorithm === enums.publicKey.hmac) {
throw new Error('Cannot create public key from symmetric private'); throw new Error('Cannot create public key from symmetric private key');
} }
const pubKeyPacket = PublicKeyPacket.fromSecretKeyPacket(keyPacket); const pubKeyPacket = PublicKeyPacket.fromSecretKeyPacket(keyPacket);
packetlist.push(pubKeyPacket); packetlist.push(pubKeyPacket);
@ -238,7 +238,7 @@ class PrivateKey extends PublicKey {
* @param {String} options.curve (optional) Elliptic curve for ECC keys * @param {String} options.curve (optional) Elliptic curve for ECC keys
* @param {Integer} options.rsaBits (optional) Number of bits for RSA subkeys * @param {Integer} options.rsaBits (optional) Number of bits for RSA subkeys
* @param {String} options.symmetricCipher (optional) Symmetric algorithm for persistent symmetric aead keys * @param {String} options.symmetricCipher (optional) Symmetric algorithm for persistent symmetric aead keys
* @param {String} options.symmetricHash (optional)Hash lgorithm for persistent symmetric hmac keys * @param {String} options.symmetricHash (optional) Hash algorithm for persistent symmetric hmac keys
* @param {Number} options.keyExpirationTime (optional) Number of seconds from the key creation time after which the key expires * @param {Number} options.keyExpirationTime (optional) Number of seconds from the key creation time after which the key expires
* @param {Date} options.date (optional) Override the creation date of the key and the key signatures * @param {Date} options.date (optional) Override the creation date of the key and the key signatures
* @param {Boolean} options.sign (optional) Indicates whether the subkey should sign rather than encrypt. Defaults to false * @param {Boolean} options.sign (optional) Indicates whether the subkey should sign rather than encrypt. Defaults to false

View File

@ -45,7 +45,7 @@ import { checkKeyRequirements } from './key/helper';
* curve25519Legacy (default), nistP256, nistP384, nistP521, secp256k1, * curve25519Legacy (default), nistP256, nistP384, nistP521, secp256k1,
* brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1 * brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1
* @param {String} options.symmetricCipher (optional) Symmetric algorithm for persistent symmetric aead keys * @param {String} options.symmetricCipher (optional) Symmetric algorithm for persistent symmetric aead keys
* @param {String} options.symmetricHash (optional)Hash lgorithm for persistent symmetric hmac keys * @param {String} options.symmetricHash (optional) Hash algorithm for persistent symmetric hmac keys
* @param {Date} [options.date=current date] - Override the creation date of the key and the key signatures * @param {Date} [options.date=current date] - Override the creation date of the key and the key signatures
* @param {Number} [options.keyExpirationTime=0 (never expires)] - Number of seconds from the key creation time after which the key expires * @param {Number} [options.keyExpirationTime=0 (never expires)] - Number of seconds from the key creation time after which the key expires
* @param {Array<Object>} [options.subkeys=a single encryption subkey] - Options for each subkey e.g. `[{sign: true, passphrase: '123'}]` * @param {Array<Object>} [options.subkeys=a single encryption subkey] - Options for each subkey e.g. `[{sign: true, passphrase: '123'}]`

View File

@ -283,8 +283,11 @@ class PublicKeyPacket {
result.bits = util.uint8ArrayBitLength(modulo); result.bits = util.uint8ArrayBitLength(modulo);
} else if (this.publicParams.oid) { } else if (this.publicParams.oid) {
result.curve = this.publicParams.oid.getName(); result.curve = this.publicParams.oid.getName();
} else if (this.publicParams.cipher) { } else if (this.publicParams.symAlgo) {
result.symmetric = this.publicParams.cipher.getName(); result.symmetric = this.publicParams.symAlgo.getName();
result.aeadMode = this.publicParams.aeadMode.getName();
} else if (this.publicParams.hashAlgo) {
result.symmetric = this.publicParams.hashAlgo.getName();
} }
return result; return result;
} }

View File

@ -523,7 +523,7 @@ class SecretKeyPacket extends PublicKeyPacket {
} }
} }
async generate(bits, curve, symmetric) { async generate(bits, curve, symmetric, aeadMode) {
// The deprecated OIDs for Ed25519Legacy and Curve25519Legacy are used in legacy version 4 keys and signatures. // The deprecated OIDs for Ed25519Legacy and Curve25519Legacy are used in legacy version 4 keys and signatures.
// Implementations MUST NOT accept or generate v6 key material using the deprecated OIDs. // Implementations MUST NOT accept or generate v6 key material using the deprecated OIDs.
if (this.version === 6 && ( if (this.version === 6 && (
@ -538,7 +538,7 @@ class SecretKeyPacket extends PublicKeyPacket {
)) { )) {
throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`); throw new Error(`Cannot generate v${this.version} keys of type 'pqc'. Generate a v6 key instead`);
} }
const { privateParams, publicParams } = await crypto.generateParams(this.algorithm, bits, curve, symmetric); const { privateParams, publicParams } = await crypto.generateParams(this.algorithm, bits, curve, symmetric, aeadMode);
this.privateParams = privateParams; this.privateParams = privateParams;
this.publicParams = publicParams; this.publicParams = publicParams;
this.isEncrypted = false; this.isEncrypted = false;

View File

@ -1,32 +0,0 @@
import util from '../util';
class ShortByteString {
constructor(data) {
if (typeof data === 'undefined') {
data = new Uint8Array([]);
}
if (!util.isUint8Array(data)) {
throw new Error('data must be in the form of a Uint8Array');
}
this.data = data;
this.length = this.data.byteLength;
}
write() {
return util.concatUint8Array([new Uint8Array([this.length]), this.data]);
}
read(input) {
if (input.length >= 1) {
const length = input[0];
if (input.length >= length + 1) {
this.data = input.subarray(1, 1 + length);
this.length = length;
return 1 + length;
}
}
throw new Error('Invalid octet string');
}
}
export default ShortByteString;

View File

@ -4757,7 +4757,7 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
expect(subKey).to.exist; expect(subKey).to.exist;
expect(newPrivateKey.getSubkeys().length).to.be.equal(total + 1); expect(newPrivateKey.getSubkeys().length).to.be.equal(total + 1);
expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256'); expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256');
expect(subKey.keyPacket.publicParams.digest).to.exist.and.to.have.length(32); expect(subKey.keyPacket.publicParams.fpSeed).to.exist.and.to.have.length(32);
expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32); expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32);
await subKey.verify(privateKey.primaryKey); await subKey.verify(privateKey.primaryKey);
}); });
@ -4773,9 +4773,9 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
expect(newKey.getSubkeys().length).to.be.equal(1); expect(newKey.getSubkeys().length).to.be.equal(1);
expect(subKey).to.exist; expect(subKey).to.exist;
expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256'); expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256');
expect(subKey.keyPacket.publicParams.cipher).to.exist; expect(subKey.keyPacket.publicParams.symAlgo).to.exist;
expect(subKey.keyPacket.publicParams.cipher.getValue()).to.be.equal(openpgp.enums.symmetric.aes256); expect(subKey.keyPacket.publicParams.symAlgo.getValue()).to.be.equal(openpgp.enums.symmetric.aes256);
expect(subKey.keyPacket.publicParams.digest).to.exist.and.to.have.length(32); expect(subKey.keyPacket.publicParams.fpSeed).to.exist.and.to.have.length(32);
expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32); expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32);
await subKey.verify(newKey.primaryKey); await subKey.verify(newKey.primaryKey);
}); });
@ -4789,9 +4789,9 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
expect(privateKey.getSubkeys().length).to.be.equal(1); expect(privateKey.getSubkeys().length).to.be.equal(1);
expect(subKey).to.exist; expect(subKey).to.exist;
expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256'); expect(subKey.getAlgorithmInfo().symmetric).to.be.equal('aes256');
expect(subKey.keyPacket.publicParams.cipher).to.exist; expect(subKey.keyPacket.publicParams.symAlgo).to.exist;
expect(subKey.keyPacket.publicParams.cipher.getValue()).to.be.equal(openpgp.enums.symmetric.aes256); expect(subKey.keyPacket.publicParams.symAlgo.getValue()).to.be.equal(openpgp.enums.symmetric.aes256);
expect(subKey.keyPacket.publicParams.digest).to.exist.and.to.have.length(32); expect(subKey.keyPacket.publicParams.fpSeed).to.exist.and.to.have.length(32);
expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32); expect(subKey.keyPacket.privateParams.keyMaterial).to.exist.and.to.have.length(32);
await subKey.verify(privateKey.primaryKey); await subKey.verify(privateKey.primaryKey);
}); });

View File

@ -1229,19 +1229,17 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
it('Parse persistent key encrypted session key packet (PKESK encrypted with persistent symmetric key)', () => { it('Parse persistent key encrypted session key packet (PKESK encrypted with persistent symmetric key)', () => {
const serializedPacket = new Uint8Array([ const serializedPacket = new Uint8Array([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x64, 0x01, 0x4c, 0x80, 0x3d, 0x9b, 0xb6, 0x00, 0x80, 0x4c, 0x80, 0x3d, 0x9b, 0xb6,
0x44, 0x13, 0x25, 0x90, 0x24, 0x6e, 0x59, 0x0d, 0x44, 0x13, 0x25, 0x90, 0x24, 0x6e, 0x59, 0x0d,
0x68, 0xee, 0x1e, 0x23, 0x75, 0x8a, 0x90, 0x85, 0x68, 0xee, 0x1e, 0x75, 0x8a, 0x90, 0x85,
0x78, 0x1b, 0xf7, 0xc4, 0x4d, 0x58, 0xc7, 0x64, 0x78, 0x1b, 0xf7, 0xc4, 0x4d, 0x58, 0xc7, 0x64,
0xd2, 0xe5, 0xb3, 0x4f, 0xf6, 0x6e, 0xef, 0x53, 0xd2, 0xe5, 0xb3, 0x4f, 0xf6, 0x6e, 0xef, 0x53,
0xc4, 0xc3, 0x76, 0x7b, 0xba, 0xf9, 0x03, 0x86, 0xc4, 0xc3, 0x76, 0x7b, 0xba, 0xf9, 0x03, 0x86,
0xee, 0xc0, 0x9d, 0x60, 0x23, 0xa6, 0x8a 0xee, 0xc0, 0x9d, 0x60, 0x23, 0xa6, 0x8a
]); ]);
const expectedIV = new Uint8Array([ const expectedIVAndCiphertext = new Uint8Array([
0x4c, 0x80, 0x3d, 0x9b, 0xb6, 0x44, 0x13, 0x25, 0x4c, 0x80, 0x3d, 0x9b, 0xb6, 0x44, 0x13, 0x25,
0x90, 0x24, 0x6e, 0x59, 0x0d, 0x68, 0xee, 0x1e 0x90, 0x24, 0x6e, 0x59, 0x0d, 0x68, 0xee, 0x1e,
]);
const expectedCiphertext = new Uint8Array([
0x75, 0x8a, 0x90, 0x85, 0x78, 0x1b, 0xf7, 0xc4, 0x75, 0x8a, 0x90, 0x85, 0x78, 0x1b, 0xf7, 0xc4,
0x4d, 0x58, 0xc7, 0x64, 0xd2, 0xe5, 0xb3, 0x4f, 0x4d, 0x58, 0xc7, 0x64, 0xd2, 0xe5, 0xb3, 0x4f,
0xf6, 0x6e, 0xef, 0x53, 0xc4, 0xc3, 0x76, 0x7b, 0xf6, 0x6e, 0xef, 0x53, 0xc4, 0xc3, 0x76, 0x7b,
@ -1252,17 +1250,15 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
packet.read(serializedPacket); packet.read(serializedPacket);
expect(packet.publicKeyAlgorithm).to.equal(openpgp.enums.publicKey.aead); expect(packet.publicKeyAlgorithm).to.equal(openpgp.enums.publicKey.aead);
expect(packet.encrypted.aeadMode.data).to.equal(openpgp.enums.aead.eax); expect(util.equalsUint8Array(packet.encrypted.ivAndCiphertext, expectedIVAndCiphertext)).to.be.true;
expect(util.equalsUint8Array(packet.encrypted.iv, expectedIV)).to.be.true;
expect(util.equalsUint8Array(packet.encrypted.c.data, expectedCiphertext)).to.be.true;
}); });
it('Parse signature packet from persistent symmetric key', () => { it('Parse signature packet from persistent symmetric key', () => {
const serializedPacket = new Uint8Array([ const serializedPacket = new Uint8Array([
0x04, 0x00, 0x65, 0x08, 0x00, 0x10, 0x05, 0x02, 0x04, 0x00, 0x81, 0x08, 0x00, 0x10, 0x05, 0x02,
0x60, 0x64, 0x52, 0x28, 0x09, 0x10, 0x00, 0x00, 0x60, 0x64, 0x52, 0x28, 0x09, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6b, 0x1d, 0x40, 0x76, 0xf9, 0x35, 0xf8, 0x6b, 0x1d, 0x76, 0xf9, 0x35, 0xf8,
0xe4, 0xc5, 0x2f, 0x49, 0xce, 0xf7, 0x91, 0x23, 0xe4, 0xc5, 0x2f, 0x49, 0xce, 0xf7, 0x91, 0x23,
0xb4, 0x00, 0x3b, 0x77, 0x92, 0x60, 0x2a, 0xfe, 0xb4, 0x00, 0x3b, 0x77, 0x92, 0x60, 0x2a, 0xfe,
0x2e, 0x4c, 0xf5, 0x5f, 0x6c, 0x75, 0x80, 0x5a, 0x2e, 0x4c, 0xf5, 0x5f, 0x6c, 0x75, 0x80, 0x5a,
@ -1287,7 +1283,7 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
const packet = new openpgp.SignaturePacket(); const packet = new openpgp.SignaturePacket();
packet.read(serializedPacket); packet.read(serializedPacket);
const readMAC = packet.params.mac.data; const readMAC = packet.params.mac;
expect(packet.publicKeyAlgorithm).to.equal(openpgp.enums.publicKey.hmac); expect(packet.publicKeyAlgorithm).to.equal(openpgp.enums.publicKey.hmac);
expect(util.equalsUint8Array(readMAC, expectedMAC)).to.be.true; expect(util.equalsUint8Array(readMAC, expectedMAC)).to.be.true;
@ -1297,7 +1293,7 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
const persistentSymmetricKeyPacket = new openpgp.SecretSubkeyPacket(); const persistentSymmetricKeyPacket = new openpgp.SecretSubkeyPacket();
persistentSymmetricKeyPacket.version = 4; persistentSymmetricKeyPacket.version = 4;
persistentSymmetricKeyPacket.algorithm = openpgp.enums.publicKey.aead; persistentSymmetricKeyPacket.algorithm = openpgp.enums.publicKey.aead;
await persistentSymmetricKeyPacket.generate(null, null, openpgp.enums.symmetric.aes256); await persistentSymmetricKeyPacket.generate(null, null, openpgp.enums.symmetric.aes256, openpgp.enums.aead.gcm);
await persistentSymmetricKeyPacket.computeFingerprintAndKeyID(); await persistentSymmetricKeyPacket.computeFingerprintAndKeyID();
const sessionKey = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); const sessionKey = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);