mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-07-05 04:22:31 +00:00
WIP: Add ML-DSA
This commit is contained in:
parent
b9b5667a3f
commit
0468a41f98
5
package-lock.json
generated
5
package-lock.json
generated
@ -3698,7 +3698,6 @@
|
|||||||
"url": "https://feross.org/support"
|
"url": "https://feross.org/support"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base64-js": "^1.3.1",
|
"base64-js": "^1.3.1",
|
||||||
"ieee754": "^1.1.13"
|
"ieee754": "^1.1.13"
|
||||||
@ -3709,7 +3708,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
|
||||||
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
|
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
@ -7028,8 +7026,7 @@
|
|||||||
"type": "consulting",
|
"type": "consulting",
|
||||||
"url": "https://feross.org/support"
|
"url": "https://feross.org/support"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"license": "BSD-3-Clause"
|
|
||||||
},
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
|
@ -331,8 +331,9 @@ export async function parsePrivateKeyParams(algo, bytes, publicParams) {
|
|||||||
}
|
}
|
||||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
|
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
|
||||||
const mldsaSecretKey = util.readExactSubarray(bytes, read, read + 4032); read += mldsaSecretKey.length;
|
const mldsaSeed = util.readExactSubarray(bytes, read, read + 32); read += mldsaSeed.length;
|
||||||
return { read, privateParams: { eccSecretKey, mldsaSecretKey } };
|
const { mldsaSecretKey } = await publicKey.postQuantum.signature.mldsaExpandSecretSeed(algo, mldsaSeed);
|
||||||
|
return { read, privateParams: { eccSecretKey, mldsaSecretKey, mldsaSeed } };
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
throw new UnsupportedError('Unknown public key encryption algorithm.');
|
||||||
@ -429,6 +430,7 @@ export function serializeParams(algo, params) {
|
|||||||
|
|
||||||
const excludedFields = {
|
const excludedFields = {
|
||||||
[enums.publicKey.pqc_mlkem_x25519]: new Set(['mlkemSecretKey']), // only `mlkemSeed` is serialized
|
[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 => {
|
const orderedParams = Object.keys(params).map(name => {
|
||||||
@ -506,8 +508,8 @@ export async function generateParams(algo, bits, oid, symmetric) {
|
|||||||
publicParams: { eccPublicKey, mlkemPublicKey }
|
publicParams: { eccPublicKey, mlkemPublicKey }
|
||||||
}));
|
}));
|
||||||
case enums.publicKey.pqc_mldsa_ed25519:
|
case enums.publicKey.pqc_mldsa_ed25519:
|
||||||
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSecretKey, mldsaPublicKey }) => ({
|
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey }) => ({
|
||||||
privateParams: { eccSecretKey, mldsaSecretKey },
|
privateParams: { eccSecretKey, mldsaSeed, mldsaSecretKey },
|
||||||
publicParams: { eccPublicKey, mldsaPublicKey }
|
publicParams: { eccPublicKey, mldsaPublicKey }
|
||||||
}));
|
}));
|
||||||
case enums.publicKey.dsa:
|
case enums.publicKey.dsa:
|
||||||
@ -607,9 +609,9 @@ export async function validateParams(algo, publicParams, privateParams) {
|
|||||||
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed);
|
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSeed);
|
||||||
}
|
}
|
||||||
case enums.publicKey.pqc_mldsa_ed25519: {
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
const { eccSecretKey, mldsaSecretKey } = privateParams;
|
const { eccSecretKey, mldsaSeed } = privateParams;
|
||||||
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
||||||
return publicKey.postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSecretKey);
|
return publicKey.postQuantum.signature.validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown public key algorithm.');
|
throw new Error('Unknown public key algorithm.');
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import * as kem from './kem/index';
|
import * as kem from './kem/index';
|
||||||
|
import * as signature from './signature';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
kem
|
kem,
|
||||||
|
signature
|
||||||
};
|
};
|
||||||
|
49
src/crypto/public_key/post_quantum/signature/ecc_dsa.js
Normal file
49
src/crypto/public_key/post_quantum/signature/ecc_dsa.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// TODOOOOO is this file needed? vs inlining calls in signature.js?
|
||||||
|
|
||||||
|
|
||||||
|
import * as eddsa from '../../elliptic/eddsa';
|
||||||
|
import enums from '../../../../enums';
|
||||||
|
|
||||||
|
export async function generate(algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { A, seed } = await eddsa.generate(enums.publicKey.ed25519);
|
||||||
|
return {
|
||||||
|
eccPublicKey: A,
|
||||||
|
eccSecretKey: seed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest) {
|
||||||
|
switch (signatureAlgo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { RS: eccSignature } = await eddsa.sign(enums.publicKey.ed25519, hashAlgo, null, eccPublicKey, eccSecretKey, dataDigest);
|
||||||
|
|
||||||
|
return { eccSignature };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature) {
|
||||||
|
switch (signatureAlgo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519:
|
||||||
|
return eddsa.verify(enums.publicKey.ed25519, hashAlgo, { RS: eccSignature }, null, eccPublicKey, dataDigest);
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateParams(algo, eccPublicKey, eccSecretKey) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519:
|
||||||
|
return eddsa.validateParams(enums.publicKey.ed25519, eccPublicKey, eccSecretKey);
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
2
src/crypto/public_key/post_quantum/signature/index.js
Normal file
2
src/crypto/public_key/post_quantum/signature/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { generate, sign, verify, validateParams } from './signature';
|
||||||
|
export { expandSecretSeed as mldsaExpandSecretSeed } from './ml_dsa';
|
71
src/crypto/public_key/post_quantum/signature/ml_dsa.js
Normal file
71
src/crypto/public_key/post_quantum/signature/ml_dsa.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import enums from '../../../../enums';
|
||||||
|
import util from '../../../../util';
|
||||||
|
import { getRandomBytes } from '../../../random';
|
||||||
|
|
||||||
|
export async function generate(algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const mldsaSeed = getRandomBytes(32);
|
||||||
|
const { mldsaSecretKey, mldsaPublicKey } = await expandSecretSeed(algo, mldsaSeed);
|
||||||
|
|
||||||
|
return { mldsaSeed, mldsaSecretKey, mldsaPublicKey };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand ML-DSA secret seed and retrieve the secret and public key material
|
||||||
|
* @param {module:enums.publicKey} algo - Public key algorithm
|
||||||
|
* @param {Uint8Array} seed - secret seed to expand
|
||||||
|
* @returns {Promise<{ mldsaPublicKey: Uint8Array, mldsaSecretKey: Uint8Array }>}
|
||||||
|
*/
|
||||||
|
export async function expandSecretSeed(algo, seed) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||||
|
const { secretKey: mldsaSecretKey, publicKey: mldsaPublicKey } = ml_dsa65.keygen(seed);
|
||||||
|
|
||||||
|
return { mldsaSecretKey, mldsaPublicKey };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sign(algo, mldsaSecretKey, dataDigest) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||||
|
const dataDigestWithContext = util.concatUint8Array([new Uint8Array([0, 0]), dataDigest]);
|
||||||
|
const mldsaSignature = ml_dsa65.sign(mldsaSecretKey, dataDigestWithContext);
|
||||||
|
return { mldsaSignature };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verify(algo, mldsaPublicKey, dataDigest, mldsaSignature) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa');
|
||||||
|
const dataDigestWithContext = util.concatUint8Array([new Uint8Array([0, 0]), dataDigest]);
|
||||||
|
return ml_dsa65.verify(mldsaPublicKey, dataDigestWithContext, mldsaSignature);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateParams(algo, mldsaPublicKey, mldsaSeed) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { mldsaPublicKey: expectedPublicKey } = await expandSecretSeed(algo, mldsaSeed);
|
||||||
|
return util.equalsUint8Array(mldsaPublicKey, expectedPublicKey);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
48
src/crypto/public_key/post_quantum/signature/signature.js
Normal file
48
src/crypto/public_key/post_quantum/signature/signature.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import enums from '../../../../enums';
|
||||||
|
import * as mldsa from './ml_dsa';
|
||||||
|
import * as eccdsa from './ecc_dsa';
|
||||||
|
|
||||||
|
export async function generate(algo) {
|
||||||
|
switch (algo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { eccSecretKey, eccPublicKey } = await eccdsa.generate(algo);
|
||||||
|
const { mldsaSeed, mldsaSecretKey, mldsaPublicKey } = await mldsa.generate(algo);
|
||||||
|
return { eccSecretKey, eccPublicKey, mldsaSeed, mldsaSecretKey, mldsaPublicKey };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
|
||||||
|
switch (signatureAlgo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { eccSignature } = await eccdsa.sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
|
||||||
|
const { mldsaSignature } = await mldsa.sign(signatureAlgo, mldsaSecretKey, dataDigest);
|
||||||
|
|
||||||
|
return { eccSignature, mldsaSignature };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verify(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
|
||||||
|
switch (signatureAlgo) {
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const eccVerifiedPromise = eccdsa.verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
|
||||||
|
const mldsaVerifiedPromise = mldsa.verify(signatureAlgo, mldsaPublicKey, dataDigest, mldsaSignature);
|
||||||
|
const verified = await eccVerifiedPromise && await mldsaVerifiedPromise;
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported signature algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateParams(algo, eccPublicKey, eccSecretKey, mldsaPublicKey, mldsaSeed) {
|
||||||
|
const eccValidationPromise = eccdsa.validateParams(algo, eccPublicKey, eccSecretKey);
|
||||||
|
const mldsaValidationPromise = mldsa.validateParams(algo, mldsaPublicKey, mldsaSeed);
|
||||||
|
const valid = await eccValidationPromise && await mldsaValidationPromise;
|
||||||
|
return valid;
|
||||||
|
}
|
@ -70,6 +70,12 @@ export function parseSignatureParams(algo, signature) {
|
|||||||
const mac = new ShortByteString(); read += mac.read(signature.subarray(read));
|
const mac = new ShortByteString(); read += mac.read(signature.subarray(read));
|
||||||
return { read, signatureParams: { mac } };
|
return { read, signatureParams: { mac } };
|
||||||
}
|
}
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const eccSignatureSize = 2 * publicKey.elliptic.eddsa.getPayloadSize(enums.publicKey.ed25519);
|
||||||
|
const eccSignature = util.readExactSubarray(signature, read, read + eccSignatureSize); read += eccSignature.length;
|
||||||
|
const mldsaSignature = util.readExactSubarray(signature, read, read + 3309); read += mldsaSignature.length;
|
||||||
|
return { read, signatureParams: { eccSignature, mldsaSignature } };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedError('Unknown signature algorithm.');
|
throw new UnsupportedError('Unknown signature algorithm.');
|
||||||
}
|
}
|
||||||
@ -134,6 +140,10 @@ export async function verify(algo, hashAlgo, signature, publicParams, privatePar
|
|||||||
const { keyMaterial } = privateParams;
|
const { keyMaterial } = privateParams;
|
||||||
return publicKey.hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed);
|
return publicKey.hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed);
|
||||||
}
|
}
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { eccPublicKey, mldsaPublicKey } = publicParams;
|
||||||
|
return publicKey.postQuantum.signature.verify(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown signature algorithm.');
|
throw new Error('Unknown signature algorithm.');
|
||||||
}
|
}
|
||||||
@ -195,6 +205,11 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da
|
|||||||
const mac = await publicKey.hmac.sign(algo.getValue(), keyMaterial, hashed);
|
const mac = await publicKey.hmac.sign(algo.getValue(), keyMaterial, hashed);
|
||||||
return { mac: new ShortByteString(mac) };
|
return { mac: new ShortByteString(mac) };
|
||||||
}
|
}
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519: {
|
||||||
|
const { eccPublicKey } = publicKeyParams;
|
||||||
|
const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
|
||||||
|
return publicKey.postQuantum.signature.sign(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown signature algorithm.');
|
throw new Error('Unknown signature algorithm.');
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,9 @@ export default {
|
|||||||
ed448: 28,
|
ed448: 28,
|
||||||
/** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
|
/** Post-quantum ML-KEM-768 + X25519 (Encrypt only) */
|
||||||
pqc_mlkem_x25519: 105,
|
pqc_mlkem_x25519: 105,
|
||||||
|
/** Post-quantum ML-DSA-64 + Ed25519 (Sign only) */
|
||||||
|
pqc_mldsa_ed25519: 107,
|
||||||
|
|
||||||
/** Persistent symmetric keys: encryption algorithm */
|
/** Persistent symmetric keys: encryption algorithm */
|
||||||
aead: 100,
|
aead: 100,
|
||||||
/** Persistent symmetric keys: authentication algorithm */
|
/** Persistent symmetric keys: authentication algorithm */
|
||||||
|
@ -399,7 +399,7 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) {
|
|||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
case 'pqc':
|
case 'pqc':
|
||||||
if (options.sign) {
|
if (options.sign) {
|
||||||
throw new Error('Post-quantum signing algorithms are not yet supported.');
|
options.algorithm = enums.publicKey.pqc_mldsa_ed25519;
|
||||||
} else {
|
} else {
|
||||||
options.algorithm = enums.publicKey.pqc_mlkem_x25519;
|
options.algorithm = enums.publicKey.pqc_mlkem_x25519;
|
||||||
}
|
}
|
||||||
@ -462,6 +462,7 @@ export function validateSigningKeyPacket(keyPacket, signature, config) {
|
|||||||
case enums.publicKey.ed25519:
|
case enums.publicKey.ed25519:
|
||||||
case enums.publicKey.ed448:
|
case enums.publicKey.ed448:
|
||||||
case enums.publicKey.hmac:
|
case enums.publicKey.hmac:
|
||||||
|
case enums.publicKey.pqc_mldsa_ed25519:
|
||||||
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
||||||
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,13 @@ class PublicKeyPacket {
|
|||||||
) {
|
) {
|
||||||
throw new Error('Legacy curve25519 cannot be used with v6 keys');
|
throw new Error('Legacy curve25519 cannot be used with v6 keys');
|
||||||
}
|
}
|
||||||
|
// The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys.
|
||||||
// The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
|
// The composite ML-KEM + ECDH schemes MUST be used only with v6 keys.
|
||||||
if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mlkem_x25519) {
|
if (this.version !== 6 && (
|
||||||
throw new Error('Unexpected key version: ML-KEM algorithms can only be used with v6 keys');
|
this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
|
||||||
|
this.algorithm === enums.publicKey.pqc_mlkem_x25519
|
||||||
|
)) {
|
||||||
|
throw new Error('Unexpected key version: ML-DSA and ML-KEM algorithms can only be used with v6 keys');
|
||||||
}
|
}
|
||||||
this.publicParams = publicParams;
|
this.publicParams = publicParams;
|
||||||
pos += read;
|
pos += read;
|
||||||
|
@ -532,7 +532,10 @@ class SecretKeyPacket extends PublicKeyPacket {
|
|||||||
)) {
|
)) {
|
||||||
throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
|
throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`);
|
||||||
}
|
}
|
||||||
if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mlkem_x25519) {
|
if (this.version !== 6 && (
|
||||||
|
this.algorithm === enums.publicKey.pqc_mldsa_ed25519 ||
|
||||||
|
this.algorithm === enums.publicKey.pqc_mlkem_x25519
|
||||||
|
)) {
|
||||||
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);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -315,31 +315,41 @@ export default () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PQC parameter validation', function() {
|
describe('PQC parameter validation', function() {
|
||||||
|
let pqcSigningKey;
|
||||||
let pqcEncryptionSubkey;
|
let pqcEncryptionSubkey;
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const key = await generatePrivateKeyObject({ type: 'symmetric', subkeys: [{ type: 'pqc', config: { v6Keys: true } }] });
|
pqcSigningKey = await generatePrivateKeyObject({ type: 'pqc', config: { v6Keys: true } });
|
||||||
pqcEncryptionSubkey = key.subkeys[0];
|
pqcEncryptionSubkey = pqcSigningKey.subkeys[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
async function cloneSubeyPacket(subkey) {
|
|
||||||
const subkeyPacket = new openpgp.SecretSubkeyPacket();
|
|
||||||
await subkeyPacket.read(subkey.keyPacket.write());
|
|
||||||
return subkeyPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('generated params are valid', async function() {
|
it('generated params are valid', async function() {
|
||||||
|
await expect(pqcSigningKey.keyPacket.validate()).to.not.be.rejected;
|
||||||
await expect(pqcEncryptionSubkey.keyPacket.validate()).to.not.be.rejected;
|
await expect(pqcEncryptionSubkey.keyPacket.validate()).to.not.be.rejected;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid ML-KEM public key part', async function() {
|
it('detect invalid ML-KEM public key part', async function() {
|
||||||
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
|
const keyPacket = await cloneKeyPacket(pqcEncryptionSubkey);
|
||||||
const { mlkemPublicKey } = keyPacket.publicParams;
|
const { mlkemPublicKey } = keyPacket.publicParams;
|
||||||
mlkemPublicKey[0]++;
|
mlkemPublicKey[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid ECC-KEM key part', async function() {
|
it('detect invalid ECC-KEM key part', async function() {
|
||||||
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
|
const keyPacket = await cloneKeyPacket(pqcEncryptionSubkey);
|
||||||
|
const { eccPublicKey } = keyPacket.publicParams;
|
||||||
|
eccPublicKey[0]++;
|
||||||
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detect invalid ML-DSA public key part', async function() {
|
||||||
|
const keyPacket = await cloneKeyPacket(pqcSigningKey);
|
||||||
|
const { mldsaPublicKey } = keyPacket.publicParams;
|
||||||
|
mldsaPublicKey[0]++;
|
||||||
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detect invalid ECC part', async function() {
|
||||||
|
const keyPacket = await cloneKeyPacket(pqcSigningKey);
|
||||||
const { eccPublicKey } = keyPacket.publicParams;
|
const { eccPublicKey } = keyPacket.publicParams;
|
||||||
eccPublicKey[0]++;
|
eccPublicKey[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
@ -4607,15 +4607,19 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
|
|||||||
expect(v6Key.subkeys).to.have.length(1);
|
expect(v6Key.subkeys).to.have.length(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw when trying to add a ML-KEM PQC key to a v4 key', async function() {
|
it('should throw when trying to add a ML-KEM or ML-DSA PQC key to a v4 key', async function() {
|
||||||
const v4Key = await openpgp.decryptKey({
|
const v4Key = await openpgp.decryptKey({
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
||||||
passphrase: 'hello world'
|
passphrase: 'hello world'
|
||||||
});
|
});
|
||||||
expect(v4Key.keyPacket.version).to.equal(4);
|
expect(v4Key.keyPacket.version).to.equal(4);
|
||||||
expect(v4Key.subkeys).to.have.length(1);
|
expect(v4Key.subkeys).to.have.length(1);
|
||||||
|
// try adding an ML-KEM subkey
|
||||||
await expect(v4Key.addSubkey({ type: 'pqc', sign: false })).to.be.rejectedWith(/Cannot generate v4 keys of type 'pqc'/);
|
await expect(v4Key.addSubkey({ type: 'pqc', sign: false })).to.be.rejectedWith(/Cannot generate v4 keys of type 'pqc'/);
|
||||||
expect(v4Key.subkeys).to.have.length(1);
|
expect(v4Key.subkeys).to.have.length(1);
|
||||||
|
// try adding an ML-DSA subkey
|
||||||
|
await expect(v4Key.addSubkey({ type: 'pqc', sign: true })).to.be.rejectedWith(/Cannot generate v4 keys of type 'pqc'/);
|
||||||
|
expect(v4Key.subkeys).to.have.length(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw when trying to encrypt a subkey separately from key', async function() {
|
it('should throw when trying to encrypt a subkey separately from key', async function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user