diff --git a/src/config/config.js b/src/config/config.js index 03dd16b0..9d90747e 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -120,6 +120,14 @@ export default { * @property {Boolean} revocations_expire If true, expired revocation signatures are ignored */ revocations_expire: false, + /** + * Allow decryption using RSA keys without `encrypt` flag. + * This setting is potentially insecure, but it is needed to get around an old openpgpjs bug + * where key flags were ignored when selecting a key for encryption. + * @memberof module:config + * @property {Boolean} allow_insecure_decryption_with_signing_keys + */ + allow_insecure_decryption_with_signing_keys: false, /** * @memberof module:config diff --git a/src/key/helper.js b/src/key/helper.js index 7d5ec053..d13bc32c 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -366,6 +366,12 @@ export function isValidDecryptionKeyPacket(signature) { if (!signature.verified) { // Sanity check throw new Error('Signature not verified'); } + + if (config.allow_insecure_decryption_with_signing_keys) { + // This is only relevant for RSA keys, all other signing ciphers cannot decrypt + return true; + } + return !signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 || (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0; diff --git a/test/general/key.js b/test/general/key.js index 7db4f4a7..f0e96765 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -1916,6 +1916,55 @@ vqBGKJzmO5q3cECw =X9kJ -----END PGP PRIVATE KEY BLOCK-----`; + +const rsaSignOnly = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xcLYBF9Gl+MBCACc09O3gjyO0B1ledGxGFSUpPmhhJzkxKoY1WDX8VlASCHz +bAA/BytgYBXHTe7N+N3yJ6uiN3DIQ2j5uGWk/h5jyIOsRuzQxJ40n8AdK/71 +SGDCG1X5l1h9vmVTJxkQ3pcOxqRg55EEuJWKN1v7B1hIPxhaM5hgH/7s+PNn +lQddckQJqYkpm9Hy6EI7f9oHrOtWJWZoCHkWZVld7+9ZVPi34ex5ofYOuvNL +AIKZCc7lAiUiDJYQ+hIJRoYwLYhjIshpYoHgNeG4snlupNO32BiwDbHFDjeu +eoBLQ0rxZV7B664ceCmIl+VRht9G20hfGoTjAiop5tyrN1ZeL4EaI+aTABEB +AAEAB/oCKTQnftvHwrkBVlyzSN6tfXylF2551Q3n4CZGg3efI/9PCa9wF58+ +WApqmgsUqcNbVnDfl2T58ow05FLMxnFFNnHJq8ltfnXl+gG6c7iy94p79SQE +AGCOL7xNassXrDAQZhqWkCdiLK3b6r9F8Y3URb/AYbWH2BkFkS0oWQDav+Tw +lABt5vG2L5QtnShdqi8CCitcHGEKHocPHp0yAQlp3oAMq09YubgrzESDJ7Pe +l93cT35NlyimAZ6IYk/gumX0/6spqcw7205KfG6P84WlMp3WmE0IUWtiOp+7 +rjMjDki0WeVKtuLbHBhOwKvxcILWz+0vQf3uu6aXOKQ3JlsVBADHoXa6QjrT +RmKD9ch65Pkd+EZiKhe+pqqIArVj4QsVBEnaggR59SD8uXhtpyBnvOp3xpof +Vut3SKWl/jmH7vKansFbHOo8xLUyVctu7lCL2/v85FcRJxfPK00MBic+z/vf +mWOAY1VBoi5I9qi6o8vVHA5BJ/xw2uV9VpxfiLG0vwQAyRxHmoZl/OxaZUsm +J9eDYV9xyYumkTCYvHPk9X+ehS+XeYh24z1q9a/1jEnSR3A5XE67UCLaspiA ++Px7nSU1+ftJ9bC2bnRR0Upop+3UkPeCBVp4tYAhsNnPXhSWC0gCgeGU7EmW +DechFg29LId35LXKgmXls9u5yDy2w978Hy0D/jbKZaxNMMwlx/XCFCoBEcXS +DBzg7GHNXdillJqy215j46lfVqOCB3IiffNKjHua2l6fQc0BoiWIZnElMnIa +faEBBSpOVqKhktDFacHa5xChjqXZVyw68qc0I36xkCfcwvYCpNKKkXv90r8A +tRI6gpBLeMJvkL3VkmKd6AZymxFxRGjNEkJvYiA8aW5mb0Bib2IuY29tPsLA +jQQQAQgAIAUCX0aX4wYLCQcIAwIEFQgKAgQWAgEAAhkBAhsDAh4BACEJEAr9 +x5ZY6oZmFiEEm+B7p+lshgEOwGGZCv3HlljqhmaUWgf/efmGSpOKIGQ3Kh32 +HUqn/4ARvUmqMtZz4xUA9P3GAPY8XwJf00jSQlAo4//3aA1eEOJFHCr2qzCk +/4gIoZEScTTZp4itfL/Fer3UX+bV/VeTNgZGi+MRylSDQxLRQNpRgu+FmRAi +E6fr8D8GMvEcGb0jTRgWGj1EVtfOHfDg+EyPrtw+Z8u/bErUJ+Fnxz+KOGSN +SBQVAOflUYFoQhUNgZiq1s8WFD55sfes3UdBwsmHquDtYGo9dvWLJXxTEF8q +QCyKHYdk25ShIlNpRUqOH3CHqY/38z7QeV7INwtZaQvoES08RlD6ZMtczYLj +BZou86lozq7ISvRg1RSIWZ0ZRA== +=A9Ts +-----END PGP PRIVATE KEY BLOCK----- +`; + +const encryptedRsaSignOnly = `-----BEGIN PGP MESSAGE----- + +wcBMAwr9x5ZY6oZmAQf+Lxghg4keIFpEq8a65gFkIfW+chHTDPlfI8xnx6U9 +HdsICX3Oye5V0ToCVKkEWDxfN1yCfXiYalSNo7ScRZKR7C+j02/pC+FfR6AJ +2cvdFoGIrLaXdjXXc/oXbsCCZA4C1DhQqpdORo2qGF0Q6Sm8659B0CfOgYSL +fBfKQ5VJngUT5JG8Uek3YuXBufPNhzdmXLHyB2Y2CwKkldi2vo4YNAukDhrR +2TojxdNoouhnMm+gloCE1n8huY1vw5F78/uiHen0tmHQ0dxtfk8cc1burgl/ +zUdJ3Sg6Eu+OC2ae5II63iB5fG+lCwZtfuepWnePDv8RDKNHCVP/LoBNpGOZ +U9I6AUkZWdcsueib9ghKDDy+HbUbf2kCJWUnuyeOCKqQifDb8bsLmdQY4Wb6 +EBeLgD8oZHVsH3NLjPakPw== +=STqy +-----END PGP MESSAGE-----` + function versionSpecificTests() { it('Preferences of generated key', function() { const testPref = function(key) { @@ -2667,6 +2716,26 @@ describe('Key', function() { await expect(pubKey.getEncryptionKey()).to.be.rejectedWith('Could not find valid encryption key packet in key c076e634d32b498d'); }); + it('should not decrypt using a sign-only RSA key, unless explicitly configured', async function () { + const allowSigningKeyDecryption = openpgp.config.allow_insecure_decryption_with_signing_keys; + const { keys: [key] } = await openpgp.key.readArmored(rsaSignOnly); + try { + openpgp.config.allow_insecure_decryption_with_signing_keys = false; + await expect(openpgp.decrypt({ + message: await openpgp.message.readArmored(encryptedRsaSignOnly), + privateKeys: key + })).to.be.rejectedWith(/Session key decryption failed/); + + openpgp.config.allow_insecure_decryption_with_signing_keys = true; + await expect(openpgp.decrypt({ + message: await openpgp.message.readArmored(encryptedRsaSignOnly), + privateKeys: key + })).to.be.fulfilled; + } finally { + openpgp.config.allow_insecure_decryption_with_signing_keys = allowSigningKeyDecryption; + } + }); + it('Method getExpirationTime V4 Key', async function() { const pubKey = (await openpgp.key.readArmored(twoKeys)).keys[1]; expect(pubKey).to.exist;