From 8b3a6491eda62285c2be63c1f17bdb45e36ee9ad Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 29 Oct 2022 15:12:12 +0200 Subject: [PATCH] Properly handle failed decryption caused by removed private keys --- .../OpenPgpMessageInputStream.java | 25 +++++++++++++++++++ .../java/org/gnupg/GnuPGDummyKeyUtilTest.java | 24 ++++++++---------- ...DecryptWithUnavailableGnuDummyKeyTest.java | 3 ++- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java index 1b11744c..a509cde2 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java @@ -18,6 +18,7 @@ import javax.annotation.Nonnull; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.S2K; import org.bouncycastle.bcpg.UnsupportedPacketVersionException; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPEncryptedData; @@ -507,6 +508,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream { } PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId); SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKeys, secretKey.getKeyID()); + S2K s2K = secretKey.getS2K(); + if (s2K != null) { + int s2kType = s2K.getType(); + if (s2kType >= 100 && s2kType <= 110) { + LOGGER.debug("Skipping PKESK because key " + decryptionKeyId + " has unsupported private S2K specifier " + s2kType); + continue; + } + } LOGGER.debug("Attempt decryption using secret key " + decryptionKeyId); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys); @@ -532,6 +541,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream { PGPSecretKeyRing decryptionKeys = decryptionKeyCandidate.getA(); PGPSecretKey secretKey = decryptionKeyCandidate.getB(); SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKeys, secretKey.getKeyID()); + S2K s2K = secretKey.getS2K(); + if (s2K != null) { + int s2kType = s2K.getType(); + if (s2kType >= 100 && s2kType <= 110) { + LOGGER.debug("Skipping PKESK because key " + decryptionKeyId + " has unsupported private S2K specifier " + s2kType); + continue; + } + } LOGGER.debug("Attempt decryption of anonymous PKESK with key " + decryptionKeyId); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA()); if (!protector.hasPassphraseFor(secretKey.getKeyID())) { @@ -567,6 +584,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream { long keyId = secretKey.getKeyID(); PGPSecretKeyRing decryptionKey = getDecryptionKey(keyId); SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKey, keyId); + S2K s2K = secretKey.getS2K(); + if (s2K != null) { + int s2kType = s2K.getType(); + if (s2kType >= 100 && s2kType <= 110) { + LOGGER.debug("Skipping PKESK because key " + decryptionKeyId + " has unsupported private S2K specifier " + s2kType); + continue; + } + } LOGGER.debug("Attempt decryption with key " + decryptionKeyId + " while interactively requesting its passphrase"); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKey); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector.getDecryptor(keyId)); diff --git a/pgpainless-core/src/test/java/org/gnupg/GnuPGDummyKeyUtilTest.java b/pgpainless-core/src/test/java/org/gnupg/GnuPGDummyKeyUtilTest.java index b9562f60..87c5b02e 100644 --- a/pgpainless-core/src/test/java/org/gnupg/GnuPGDummyKeyUtilTest.java +++ b/pgpainless-core/src/test/java/org/gnupg/GnuPGDummyKeyUtilTest.java @@ -4,25 +4,23 @@ package org.gnupg; -import org.bouncycastle.bcpg.S2K; -import org.bouncycastle.bcpg.SecretKeyPacket; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.gnupg.GnuPGDummyExtension; -import org.gnupg.GnuPGDummyKeyUtil; -import org.junit.jupiter.api.Test; -import org.pgpainless.PGPainless; -import org.pgpainless.key.SubkeyIdentifier; -import org.pgpainless.key.util.KeyIdUtil; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SecretKeyPacket; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.key.SubkeyIdentifier; +import org.pgpainless.key.util.KeyIdUtil; public class GnuPGDummyKeyUtilTest { // normal, non-hw-backed key diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TryDecryptWithUnavailableGnuDummyKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TryDecryptWithUnavailableGnuDummyKeyTest.java index 419c529d..640025b1 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TryDecryptWithUnavailableGnuDummyKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TryDecryptWithUnavailableGnuDummyKeyTest.java @@ -14,6 +14,7 @@ import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.ProducerOptions; import org.gnupg.GnuPGDummyKeyUtil; +import org.pgpainless.exception.MissingDecryptionMethodException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -45,7 +46,7 @@ public class TryDecryptWithUnavailableGnuDummyKeyTest { .removePrivateKeys(GnuPGDummyKeyUtil.KeyFilter.any()); ByteArrayInputStream ciphertextIn = new ByteArrayInputStream(ciphertextOut.toByteArray()); - assertThrows(PGPException.class, () -> PGPainless.decryptAndOrVerify() + assertThrows(MissingDecryptionMethodException.class, () -> PGPainless.decryptAndOrVerify() .onInputStream(ciphertextIn) .withOptions(ConsumerOptions.get().addDecryptionKey(removedKeys))); }