1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-07-01 07:46:43 +02:00

Properly handle failed decryption caused by removed private keys

This commit is contained in:
Paul Schaub 2022-10-29 15:12:12 +02:00
parent 25fd3fa1d6
commit 3e120fbf7f
3 changed files with 38 additions and 14 deletions

View file

@ -18,6 +18,7 @@ import javax.annotation.Nonnull;
import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.UnsupportedPacketVersionException; import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedData;
@ -507,6 +508,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
} }
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId); PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKeys, secretKey.getKeyID()); 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); LOGGER.debug("Attempt decryption using secret key " + decryptionKeyId);
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys);
@ -532,6 +541,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
PGPSecretKeyRing decryptionKeys = decryptionKeyCandidate.getA(); PGPSecretKeyRing decryptionKeys = decryptionKeyCandidate.getA();
PGPSecretKey secretKey = decryptionKeyCandidate.getB(); PGPSecretKey secretKey = decryptionKeyCandidate.getB();
SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKeys, secretKey.getKeyID()); 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); LOGGER.debug("Attempt decryption of anonymous PKESK with key " + decryptionKeyId);
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA()); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA());
if (!protector.hasPassphraseFor(secretKey.getKeyID())) { if (!protector.hasPassphraseFor(secretKey.getKeyID())) {
@ -567,6 +584,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
long keyId = secretKey.getKeyID(); long keyId = secretKey.getKeyID();
PGPSecretKeyRing decryptionKey = getDecryptionKey(keyId); PGPSecretKeyRing decryptionKey = getDecryptionKey(keyId);
SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKey, 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"); LOGGER.debug("Attempt decryption with key " + decryptionKeyId + " while interactively requesting its passphrase");
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKey); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKey);
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector.getDecryptor(keyId)); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector.getDecryptor(keyId));

View file

@ -4,25 +4,23 @@
package org.gnupg; package org.gnupg;
import org.bouncycastle.bcpg.S2K; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import org.bouncycastle.bcpg.SecretKeyPacket; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.bouncycastle.openpgp.PGPSecretKey; import static org.junit.jupiter.api.Assertions.assertTrue;
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 java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import org.bouncycastle.bcpg.S2K;
import static org.junit.jupiter.api.Assertions.assertEquals; import org.bouncycastle.bcpg.SecretKeyPacket;
import static org.junit.jupiter.api.Assertions.assertTrue; 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 { public class GnuPGDummyKeyUtilTest {
// normal, non-hw-backed key // normal, non-hw-backed key

View file

@ -14,6 +14,7 @@ import org.pgpainless.encryption_signing.EncryptionOptions;
import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.encryption_signing.ProducerOptions;
import org.gnupg.GnuPGDummyKeyUtil; import org.gnupg.GnuPGDummyKeyUtil;
import org.pgpainless.exception.MissingDecryptionMethodException;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -45,7 +46,7 @@ public class TryDecryptWithUnavailableGnuDummyKeyTest {
.removePrivateKeys(GnuPGDummyKeyUtil.KeyFilter.any()); .removePrivateKeys(GnuPGDummyKeyUtil.KeyFilter.any());
ByteArrayInputStream ciphertextIn = new ByteArrayInputStream(ciphertextOut.toByteArray()); ByteArrayInputStream ciphertextIn = new ByteArrayInputStream(ciphertextOut.toByteArray());
assertThrows(PGPException.class, () -> PGPainless.decryptAndOrVerify() assertThrows(MissingDecryptionMethodException.class, () -> PGPainless.decryptAndOrVerify()
.onInputStream(ciphertextIn) .onInputStream(ciphertextIn)
.withOptions(ConsumerOptions.get().addDecryptionKey(removedKeys))); .withOptions(ConsumerOptions.get().addDecryptionKey(removedKeys)));
} }