1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-25 04:17:59 +01:00

Work on postponed keys

This commit is contained in:
Paul Schaub 2022-10-20 15:35:15 +02:00
parent f2bd51a2c6
commit e1f46659c5

View file

@ -10,7 +10,9 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.Stack; import java.util.Stack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -49,6 +51,7 @@ import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStra
import org.pgpainless.exception.MalformedOpenPgpMessageException; import org.pgpainless.exception.MalformedOpenPgpMessageException;
import org.pgpainless.exception.MessageNotIntegrityProtectedException; import org.pgpainless.exception.MessageNotIntegrityProtectedException;
import org.pgpainless.exception.MissingDecryptionMethodException; import org.pgpainless.exception.MissingDecryptionMethodException;
import org.pgpainless.exception.MissingPassphraseException;
import org.pgpainless.exception.SignatureValidationException; import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.exception.UnacceptableAlgorithmException; import org.pgpainless.exception.UnacceptableAlgorithmException;
import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.implementation.ImplementationFactory;
@ -60,6 +63,7 @@ import org.pgpainless.policy.Policy;
import org.pgpainless.signature.SignatureUtils; import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.consumer.OnePassSignatureCheck; import org.pgpainless.signature.consumer.OnePassSignatureCheck;
import org.pgpainless.signature.consumer.SignatureCheck; import org.pgpainless.signature.consumer.SignatureCheck;
import org.pgpainless.signature.consumer.SignatureValidator;
import org.pgpainless.signature.consumer.SignatureVerifier; import org.pgpainless.signature.consumer.SignatureVerifier;
import org.pgpainless.util.ArmoredInputStreamFactory; import org.pgpainless.util.ArmoredInputStreamFactory;
import org.pgpainless.util.Passphrase; import org.pgpainless.util.Passphrase;
@ -127,8 +131,8 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
} }
protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream, protected OpenPgpMessageInputStream(@Nonnull InputStream inputStream,
@Nonnull Policy policy, @Nonnull Policy policy,
@Nonnull ConsumerOptions options) { @Nonnull ConsumerOptions options) {
super(OpenPgpMetadata.getBuilder()); super(OpenPgpMetadata.getBuilder());
this.policy = policy; this.policy = policy;
this.options = options; this.options = options;
@ -371,6 +375,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
} }
} }
List<Tuple<PGPSecretKey, PGPPublicKeyEncryptedData>> postponedDueToMissingPassphrase = new ArrayList<>();
// Try (known) secret keys // Try (known) secret keys
for (PGPPublicKeyEncryptedData pkesk : esks.pkesks) { for (PGPPublicKeyEncryptedData pkesk : esks.pkesks) {
long keyId = pkesk.getKeyID(); long keyId = pkesk.getKeyID();
@ -378,7 +383,15 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
if (decryptionKeys == null) { if (decryptionKeys == null) {
continue; continue;
} }
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeys);
// Postpone keys with missing passphrase
if (!protector.hasPassphraseFor(keyId)) {
postponedDueToMissingPassphrase.add(new Tuple<>(secretKey, pkesk));
continue;
}
PGPSecretKey decryptionKey = decryptionKeys.getSecretKey(keyId); PGPSecretKey decryptionKey = decryptionKeys.getSecretKey(keyId);
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, protector); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, protector);
@ -408,7 +421,12 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
// try anonymous secret keys // try anonymous secret keys
for (PGPPublicKeyEncryptedData pkesk : esks.anonPkesks) { for (PGPPublicKeyEncryptedData pkesk : esks.anonPkesks) {
for (Tuple<PGPSecretKeyRing, PGPSecretKey> decryptionKeyCandidate : findPotentialDecryptionKeys(pkesk)) { for (Tuple<PGPSecretKeyRing, PGPSecretKey> decryptionKeyCandidate : findPotentialDecryptionKeys(pkesk)) {
PGPSecretKey secretKey = decryptionKeyCandidate.getB();
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA()); SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKeyCandidate.getA());
if (!protector.hasPassphraseFor(secretKey.getKeyID())) {
postponedDueToMissingPassphrase.add(new Tuple<>(secretKey, pkesk));
continue;
}
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKeyCandidate.getB(), protector); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKeyCandidate.getB(), protector);
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance() PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
.getPublicKeyDataDecryptorFactory(privateKey); .getPublicKeyDataDecryptorFactory(privateKey);
@ -433,10 +451,64 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
} }
} }
if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.THROW_EXCEPTION) {
// Non-interactive mode: Throw an exception with all locked decryption keys
Set<SubkeyIdentifier> keyIds = new HashSet<>();
for (Tuple<PGPSecretKey, PGPPublicKeyEncryptedData> k : postponedDueToMissingPassphrase) {
PGPSecretKey key = k.getA();
keyIds.add(new SubkeyIdentifier(getDecryptionKey(key.getKeyID()), key.getKeyID()));
}
if (!keyIds.isEmpty()) {
throw new MissingPassphraseException(keyIds);
}
} else if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.INTERACTIVE) {
for (PGPPublicKeyEncryptedData pkesk : esks.pkesks) {
// Interactive mode: Fire protector callbacks to get passphrases interactively
for (Tuple<PGPSecretKey, PGPPublicKeyEncryptedData> missingPassphrases : postponedDueToMissingPassphrase) {
PGPSecretKey secretKey = missingPassphrases.getA();
long keyId = secretKey.getKeyID();
PGPSecretKeyRing decryptionKey = getDecryptionKey(keyId);
SecretKeyRingProtector protector = options.getSecretKeyProtector(decryptionKey);
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector.getDecryptor(keyId));
PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance()
.getPublicKeyDataDecryptorFactory(privateKey);
try {
InputStream decrypted = pkesk.getDataStream(decryptorFactory);
SessionKey sessionKey = new SessionKey(pkesk.getSessionKey(decryptorFactory));
throwIfUnacceptable(sessionKey.getAlgorithm());
MessageMetadata.EncryptedData encryptedData = new MessageMetadata.EncryptedData(
SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory)),
metadata.depth + 1);
encryptedData.decryptionKey = new SubkeyIdentifier(decryptionKey, keyId);
encryptedData.sessionKey = sessionKey;
IntegrityProtectedInputStream integrityProtected = new IntegrityProtectedInputStream(decrypted, pkesk, options);
nestedInputStream = new OpenPgpMessageInputStream(buffer(integrityProtected), options, encryptedData, policy);
return true;
} catch (PGPException e) {
// hm :/
}
}
}
} else {
throw new IllegalStateException("Invalid PostponedKeysStrategy set in consumer options.");
}
// we did not yet succeed in decrypting any session key :/ // we did not yet succeed in decrypting any session key :/
return false; return false;
} }
private PGPSecretKey getDecryptionKey(PGPSecretKeyRing decryptionKeys, long keyId) {
KeyRingInfo info = PGPainless.inspectKeyRing(decryptionKeys);
if (info.getEncryptionSubkeys(EncryptionPurpose.ANY).contains(info.getPublicKey(keyId))) {
return info.getSecretKey(keyId);
}
return null;
}
private void throwIfUnacceptable(SymmetricKeyAlgorithm algorithm) private void throwIfUnacceptable(SymmetricKeyAlgorithm algorithm)
throws UnacceptableAlgorithmException { throws UnacceptableAlgorithmException {
if (!policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) { if (!policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) {
@ -497,10 +569,12 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
collectMetadata(); collectMetadata();
nestedInputStream = null; nestedInputStream = null;
try { if (packetInputStream != null) {
consumePackets(); try {
} catch (PGPException e) { consumePackets();
throw new RuntimeException(e); } catch (PGPException e) {
throw new RuntimeException(e);
}
} }
signatures.finish(metadata, policy); signatures.finish(metadata, policy);
} }
@ -512,23 +586,26 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
throws IOException { throws IOException {
if (nestedInputStream == null) { if (nestedInputStream == null) {
automaton.assertValid(); if (packetInputStream != null) {
automaton.assertValid();
}
return -1; return -1;
} }
int r = nestedInputStream.read(b, off, len); int r = nestedInputStream.read(b, off, len);
if (r != -1) { if (r != -1) {
signatures.updateLiteral(b, off, r); signatures.updateLiteral(b, off, r);
} } else {
else {
nestedInputStream.close(); nestedInputStream.close();
collectMetadata(); collectMetadata();
nestedInputStream = null; nestedInputStream = null;
try { if (packetInputStream != null) {
consumePackets(); try {
} catch (PGPException e) { consumePackets();
throw new RuntimeException(e); } catch (PGPException e) {
throw new RuntimeException(e);
}
} }
signatures.finish(metadata, policy); signatures.finish(metadata, policy);
} }
@ -539,7 +616,9 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
public void close() throws IOException { public void close() throws IOException {
super.close(); super.close();
if (closed) { if (closed) {
automaton.assertValid(); if (packetInputStream != null) {
automaton.assertValid();
}
return; return;
} }
@ -555,9 +634,11 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
automaton.next(InputAlphabet.EndOfSequence); if (packetInputStream != null) {
automaton.assertValid(); automaton.next(InputAlphabet.EndOfSequence);
packetInputStream.close(); automaton.assertValid();
packetInputStream.close();
}
closed = true; closed = true;
} }
@ -734,6 +815,8 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
new SubkeyIdentifier(onePassSignature.getVerificationKeys(), onePassSignature.getOnePassSignature().getKeyID())); new SubkeyIdentifier(onePassSignature.getVerificationKeys(), onePassSignature.getOnePassSignature().getKeyID()));
try { try {
SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(signature);
SignatureVerifier.verifyOnePassSignature(signature, onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID()), onePassSignature, policy); SignatureVerifier.verifyOnePassSignature(signature, onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID()), onePassSignature, policy);
layer.addVerifiedOnePassSignature(verification); layer.addVerifiedOnePassSignature(verification);
} catch (SignatureValidationException e) { } catch (SignatureValidationException e) {
@ -835,6 +918,8 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
for (SignatureCheck detached : detachedSignatures) { for (SignatureCheck detached : detachedSignatures) {
SignatureVerification verification = new SignatureVerification(detached.getSignature(), detached.getSigningKeyIdentifier()); SignatureVerification verification = new SignatureVerification(detached.getSignature(), detached.getSigningKeyIdentifier());
try { try {
SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(detached.getSignature());
SignatureVerifier.verifyInitializedSignature( SignatureVerifier.verifyInitializedSignature(
detached.getSignature(), detached.getSignature(),
detached.getSigningKeyRing().getPublicKey(detached.getSigningKeyIdentifier().getKeyId()), detached.getSigningKeyRing().getPublicKey(detached.getSigningKeyIdentifier().getKeyId()),
@ -848,6 +933,8 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
for (SignatureCheck prepended : prependedSignatures) { for (SignatureCheck prepended : prependedSignatures) {
SignatureVerification verification = new SignatureVerification(prepended.getSignature(), prepended.getSigningKeyIdentifier()); SignatureVerification verification = new SignatureVerification(prepended.getSignature(), prepended.getSigningKeyIdentifier());
try { try {
SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(prepended.getSignature());
SignatureVerifier.verifyInitializedSignature( SignatureVerifier.verifyInitializedSignature(
prepended.getSignature(), prepended.getSignature(),
prepended.getSigningKeyRing().getPublicKey(prepended.getSigningKeyIdentifier().getKeyId()), prepended.getSigningKeyRing().getPublicKey(prepended.getSigningKeyIdentifier().getKeyId()),