From 2654b5807d40eea4fb8616e19456e62e2e9059d0 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:21:34 +0200 Subject: [PATCH] Disallow generating and parsing v4 keys of type ML-DSA As per spec: https://openpgp-pqc.github.io/draft-openpgp-pqc/draft-ietf-openpgp-pqc.html#section-5.3.2-1 --- src/packet/public_key.js | 4 ++++ src/packet/secret_key.js | 3 +++ test/crypto/validate.js | 4 ++-- test/general/key.js | 11 +++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) 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 }),