Ensure primary key meets strength and algo requirements when encrypting/verifying/signing using subkeys (#1719)

Breaking change: the requirements of `config.minRSABits`, `rejectPublicKeyAlgorithms` and `rejectCurves`
are now applied to the primary key, aside from the selected subkey.

The motivation is that the subkeys are certified by the primary key, but if the latter is
weak, arbitrary subkeys could potentially be added.

Note that the change does not affect decryption, to allow decrypting older messages.
This commit is contained in:
larabr 2024-02-02 14:50:32 +01:00 committed by GitHub
parent f64dc3f35f
commit 22c2682574
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 34 additions and 10 deletions

View File

@ -259,6 +259,11 @@ class Key {
async getSigningKey(keyID = null, date = new Date(), userID = {}, config = defaultConfig) {
await this.verifyPrimaryKey(date, userID, config);
const primaryKey = this.keyPacket;
try {
helper.checkKeyRequirements(primaryKey, config);
} catch (err) {
throw util.wrapError('Could not verify primary key', err);
}
const subkeys = this.subkeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
let exception;
for (const subkey of subkeys) {
@ -313,6 +318,11 @@ class Key {
async getEncryptionKey(keyID, date = new Date(), userID = {}, config = defaultConfig) {
await this.verifyPrimaryKey(date, userID, config);
const primaryKey = this.keyPacket;
try {
helper.checkKeyRequirements(primaryKey, config);
} catch (err) {
throw util.wrapError('Could not verify primary key', err);
}
// V4: by convention subkeys are preferred for encryption service
const subkeys = this.subkeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created);
let exception;

View File

@ -300,10 +300,25 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI
message, encryptionKeys: [key], config: { rejectCurves: new Set([openpgp.enums.curve.curve25519Legacy]) }
})).to.be.eventually.rejectedWith(/Support for ecdh keys using curve curve25519Legacy is disabled/);
const echdEncrypted = await openpgp.encrypt({
await expect(openpgp.encrypt({
message, encryptionKeys: [key], config: { rejectCurves: new Set([openpgp.enums.curve.ed25519Legacy]) }
});
expect(echdEncrypted).to.match(/---BEGIN PGP MESSAGE---/);
})).to.be.eventually.rejectedWith(/Could not verify primary key: Support for eddsaLegacy keys using curve ed25519Legacy is disabled/);
// RSA 512 bits primary key, ECC subkey
const weakPrimaryKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
xk0EZbuUkQECAOVRTj4yGjMTk94lHaJGJZpwAnPJzSLr0lsqRzbsaeL+JeUr
HtKQyv8wEnqN0o7j39DXdFBI8f2/T0DkC4gkbQsAEQEAAc0OPHRlc3RAdGVz
dC5pdD7CiwQQAQgAPwWCZbuUkQMLCQcJkL/AJzZtSewrBRUICgwOBBYAAgEC
GQECmwMCHgEWIQSUuxkscrvIncEIj8K/wCc2bUnsKwAABpYCAMsq3UDscj6W
IVz8+VubCuJma95dgMXjqDGd2XGLUthYzKQ+k0USut3nwrt5aJOiQGse7W9O
Mjr/KnRCNGrJdm7OOARlu5SREgorBgEEAZdVAQUBAQdAkDQHPjXorB969PXZ
p09HqVCOqcOAzKi4KLL7I3QosmsDAQgHwnYEGAEIACoFgmW7lJEJkL/AJzZt
SewrApsMFiEElLsZLHK7yJ3BCI/Cv8AnNm1J7CsAAJ6VAf9uBYUWIM2LFx1L
c1HGHD56KA0Mu4eQksKNEugotEyBuWiZCVO+LBrDUFztC1IwXaNPL3bCjYaD
5f5A+c8qOY1f
-----END PGP PUBLIC KEY BLOCK-----` });
await expect(openpgp.encrypt({ message, encryptionKeys: weakPrimaryKey, config: { minRSABits: 2048 } })).to.be.rejectedWith(/Could not verify primary key: RSA keys shorter than 2048 bits are considered too weak./);
} finally {
openpgp.config.aeadProtect = aeadProtectVal;
openpgp.config.preferredCompressionAlgorithm = preferredCompressionAlgorithmVal;

View File

@ -4284,11 +4284,9 @@ VYGdb3eNlV8CfoEC
it('Reject encryption with key revoked with appended revocation cert', async function() {
const key = await openpgp.readKey({ armoredKey: pub_revoked_with_cert });
return openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }) }).then(() => {
throw new Error('encryptSessionKey should not encrypt with revoked public key');
}).catch(function(error) {
expect(error.message).to.equal('Error encrypting message: Primary key is revoked');
});
await expect(
openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }) })
).to.be.rejectedWith(/Primary key is revoked/);
});
it('Merge key with another key with non-ID user attributes', async function() {

View File

@ -3990,7 +3990,7 @@ XfA3pqV4mTzF
}).then(function() {
throw new Error('Should not encrypt with revoked key');
}).catch(function(error) {
expect(error.message).to.match(/Error encrypting message: Primary key is revoked/);
expect(error.message).to.match(/Primary key is revoked/);
});
});
});

View File

@ -895,7 +895,8 @@ DQmhI0SZoTKy4EGhS0bNJ+g2+dJ8Y22fKzLWXwo=
await expect(openpgp.sign({
signingKeys: key,
date: new Date(key.keyPacket.created - 3600),
message: await openpgp.createMessage({ text: 'Hello World' })
message: await openpgp.createMessage({ text: 'Hello World' }),
config: { minRSABits: 1024 }
})).to.be.rejectedWith(/Signature creation time is in the future/);
});