diff --git a/src/packet/public_key.js b/src/packet/public_key.js index a7de9be9d..c7ae4bb5e 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -138,6 +138,10 @@ class PublicKeyPacket { ) { throw new Error('Legacy curve25519 cannot be used with v6 keys'); } + // The composite ML-DSA + EdDSA schemes MUST be used only with v6 keys + if (this.version !== 6 && this.algorithm === enums.publicKey.pqc_mldsa_ed25519) { + throw new Error('Unexpected key version for ML-DSA + EdDSA key'); + } this.publicParams = publicParams; pos += read; diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 70a16b8e0..043a36229 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -532,6 +532,9 @@ class SecretKeyPacket extends PublicKeyPacket { )) { 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_mldsa_ed25519) { + throw new Error(`Cannot generate v${this.version} signing keys of type 'pqc'. Generate a v6 key instead`); + } const { privateParams, publicParams } = await crypto.generateParams(this.algorithm, bits, curve, symmetric); this.privateParams = privateParams; this.publicParams = publicParams; diff --git a/test/crypto/validate.js b/test/crypto/validate.js index 8881e45a3..ca448c74e 100644 --- a/test/crypto/validate.js +++ b/test/crypto/validate.js @@ -81,7 +81,7 @@ async function cloneKeyPacket(key) { } async function generatePrivateKeyObject(options) { - const config = { rejectCurves: new Set() }; + const config = { rejectCurves: new Set(), ...options.config }; const { privateKey } = await openpgp.generateKey({ ...options, userIDs: [{ name: 'Test', email: 'test@test.com' }], format: 'object', config }); return privateKey; } @@ -318,7 +318,7 @@ export default () => { let pqcSigningKey; let pqcEncryptionSubkey; before(async () => { - pqcSigningKey = await generatePrivateKeyObject({ type: 'pqc' }); + pqcSigningKey = await generatePrivateKeyObject({ type: 'pqc', config: { v6Keys: true } }); pqcEncryptionSubkey = pqcSigningKey.subkeys[0]; }); diff --git a/test/general/key.js b/test/general/key.js index 65dcf6267..40f51fbda 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -4525,6 +4525,17 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== expect(v6Key.subkeys).to.have.length(1); }); + it('should throw when trying to add a ML-DSA PQC key to a v4 key', async function() { + const v4Key = await openpgp.decryptKey({ + privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }), + passphrase: 'hello world' + }); + expect(v4Key.keyPacket.version).to.equal(4); + expect(v4Key.subkeys).to.have.length(1); + await expect(v4Key.addSubkey({ type: 'pqc', sign: true })).to.be.rejectedWith(/Cannot generate v4 signing 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() { const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),