mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +00:00
Refuse to use keys without key flags, add config.allowMissingKeyFlags
Key flags are needed to restrict key usage to specific purposes: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.29 . Some older keys (e.g. from OpenPGP.js v1) do not declare any key flags. In previous OpenPGP.js versions, we've allowed such keys to be used for any operation for which they were compatible. This behaviour has now changed, and these keys are not allowed to be used for any operation. The setting `config.allowMissingKeyFlags` has been added to selectively revert to the past behaviour.
This commit is contained in:
parent
9a547b4553
commit
690346a854
1
openpgp.d.ts
vendored
1
openpgp.d.ts
vendored
@ -326,6 +326,7 @@ interface Config {
|
||||
commentString: string;
|
||||
allowInsecureDecryptionWithSigningKeys: boolean;
|
||||
allowInsecureVerificationWithReformattedKeys: boolean;
|
||||
allowMissingKeyFlags: boolean;
|
||||
constantTimePKCS1Decryption: boolean;
|
||||
constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: Set<enums.symmetric>;
|
||||
v6Keys: boolean;
|
||||
|
@ -174,7 +174,14 @@ export default {
|
||||
* @property {Boolean} allowInsecureDecryptionWithSigningKeys
|
||||
*/
|
||||
allowInsecureVerificationWithReformattedKeys: false,
|
||||
|
||||
/**
|
||||
* Allow using keys that do not have any key flags set.
|
||||
* Key flags are needed to restrict key usage to specific purposes: for instance, a signing key could only be allowed to certify other keys, and not sign messages
|
||||
* (see https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.29).
|
||||
* Some older keys do not declare any key flags, which means they are not allowed to be used for any operation.
|
||||
* This setting allows using such keys for any operation for which they are compatible, based on their public key algorithm.
|
||||
*/
|
||||
allowMissingKeyFlags: false,
|
||||
/**
|
||||
* Enable constant-time decryption of RSA- and ElGamal-encrypted session keys, to hinder Bleichenbacher-like attacks (https://link.springer.com/chapter/10.1007/BFb0055716).
|
||||
* This setting has measurable performance impact and it is only helpful in application scenarios where both of the following conditions apply:
|
||||
|
@ -357,32 +357,51 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) {
|
||||
return options;
|
||||
}
|
||||
|
||||
export function isValidSigningKeyPacket(keyPacket, signature) {
|
||||
const keyAlgo = keyPacket.algorithm;
|
||||
return keyAlgo !== enums.publicKey.rsaEncrypt &&
|
||||
keyAlgo !== enums.publicKey.elgamal &&
|
||||
keyAlgo !== enums.publicKey.ecdh &&
|
||||
keyAlgo !== enums.publicKey.x25519 &&
|
||||
keyAlgo !== enums.publicKey.x448 &&
|
||||
(!signature.keyFlags ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.signData) !== 0);
|
||||
export function isValidSigningKeyPacket(keyPacket, signature, config) {
|
||||
switch (keyPacket.algorithm) {
|
||||
case enums.publicKey.rsaEncryptSign:
|
||||
case enums.publicKey.rsaSign:
|
||||
case enums.publicKey.dsa:
|
||||
case enums.publicKey.ecdsa:
|
||||
case enums.publicKey.eddsaLegacy:
|
||||
case enums.publicKey.ed25519:
|
||||
case enums.publicKey.ed448: {
|
||||
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
||||
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
||||
}
|
||||
|
||||
return !signature.keyFlags ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.signData) !== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isValidEncryptionKeyPacket(keyPacket, signature) {
|
||||
const keyAlgo = keyPacket.algorithm;
|
||||
return keyAlgo !== enums.publicKey.dsa &&
|
||||
keyAlgo !== enums.publicKey.rsaSign &&
|
||||
keyAlgo !== enums.publicKey.ecdsa &&
|
||||
keyAlgo !== enums.publicKey.eddsaLegacy &&
|
||||
keyAlgo !== enums.publicKey.ed25519 &&
|
||||
keyAlgo !== enums.publicKey.ed448 &&
|
||||
(!signature.keyFlags ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0);
|
||||
export function isValidEncryptionKeyPacket(keyPacket, signature, config) {
|
||||
switch (keyPacket.algorithm) {
|
||||
case enums.publicKey.rsaEncryptSign:
|
||||
case enums.publicKey.rsaEncrypt:
|
||||
case enums.publicKey.elgamal:
|
||||
case enums.publicKey.ecdh:
|
||||
case enums.publicKey.x25519:
|
||||
case enums.publicKey.x448: {
|
||||
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
||||
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
||||
}
|
||||
|
||||
return !signature.keyFlags ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 ||
|
||||
(signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isValidDecryptionKeyPacket(signature, config) {
|
||||
if (config.allowInsecureDecryptionWithSigningKeys) {
|
||||
if (!signature.keyFlags && !config.allowMissingKeyFlags) {
|
||||
throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`');
|
||||
}
|
||||
|
||||
const isValidSigningKeyPacket = !signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0;
|
||||
if (isValidSigningKeyPacket && config.allowInsecureDecryptionWithSigningKeys) {
|
||||
// This is only relevant for RSA keys, all other signing algorithms cannot decrypt
|
||||
return true;
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ class Key {
|
||||
const bindingSignature = await helper.getLatestValidSignature(
|
||||
subkey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config
|
||||
);
|
||||
if (!helper.isValidSigningKeyPacket(subkey.keyPacket, bindingSignature)) {
|
||||
if (!helper.isValidSigningKeyPacket(subkey.keyPacket, bindingSignature, config)) {
|
||||
continue;
|
||||
}
|
||||
if (!bindingSignature.embeddedSignature) {
|
||||
@ -322,7 +322,7 @@ class Key {
|
||||
await subkey.verify(date, config);
|
||||
const dataToVerify = { key: primaryKey, bind: subkey.keyPacket };
|
||||
const bindingSignature = await helper.getLatestValidSignature(subkey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config);
|
||||
if (helper.isValidEncryptionKeyPacket(subkey.keyPacket, bindingSignature)) {
|
||||
if (helper.isValidEncryptionKeyPacket(subkey.keyPacket, bindingSignature, config)) {
|
||||
helper.checkKeyRequirements(subkey.keyPacket, config);
|
||||
return subkey;
|
||||
}
|
||||
@ -336,7 +336,7 @@ class Key {
|
||||
// if no valid subkey for encryption, evaluate primary key
|
||||
const selfCertification = await this.getPrimarySelfSignature(date, userID, config);
|
||||
if ((!keyID || primaryKey.getKeyID().equals(keyID)) &&
|
||||
helper.isValidEncryptionKeyPacket(primaryKey, selfCertification)) {
|
||||
helper.isValidEncryptionKeyPacket(primaryKey, selfCertification, config)) {
|
||||
helper.checkKeyRequirements(primaryKey, config);
|
||||
return this;
|
||||
}
|
||||
|
@ -3402,7 +3402,7 @@ PzIEeL7UH3trraFmi+Gq8u4kAA==
|
||||
expect(selfCertification.valid).to.be.true;
|
||||
|
||||
const certifyingKey = await openpgp.readKey({ armoredKey: certifying_key });
|
||||
const certifyingSigningKey = await certifyingKey.getSigningKey();
|
||||
const certifyingSigningKey = await certifyingKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, allowMissingKeyFlags: true });
|
||||
const signatures = await pubKey.verifyPrimaryUser([certifyingKey]);
|
||||
expect(signatures.length).to.equal(2);
|
||||
expect(signatures[0].keyID.toHex()).to.equal(publicSigningKey.getKeyID().toHex());
|
||||
@ -3411,7 +3411,9 @@ PzIEeL7UH3trraFmi+Gq8u4kAA==
|
||||
expect(signatures[1].valid).to.be.false;
|
||||
|
||||
const { user } = await pubKey.getPrimaryUser();
|
||||
await expect(user.verifyCertificate(user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked');
|
||||
await expect(
|
||||
user.verifyCertificate(user.otherCertifications[0], [certifyingKey], undefined, { ...openpgp.config, allowMissingKeyFlags: true })
|
||||
).to.be.rejectedWith('User certificate is revoked');
|
||||
} finally {
|
||||
openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
|
||||
}
|
||||
|
@ -2188,7 +2188,7 @@ uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw=
|
||||
|
||||
const message = await openpgp.createMessage({ text: content });
|
||||
await message.appendSignature(detachedSig);
|
||||
const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024 } });
|
||||
const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024, allowMissingKeyFlags: true } });
|
||||
expect(data).to.equal(content);
|
||||
expect(signatures).to.have.length(1);
|
||||
expect(await signatures[0].verified).to.be.true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user