Add debug output and change API

This commit is contained in:
Paul Schaub 2018-06-19 17:14:37 +02:00
parent 47074e50b9
commit 396590f4c2
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
5 changed files with 68 additions and 30 deletions

View File

@ -46,7 +46,7 @@ public enum SymmetricKeyAlgorithm {
} }
} }
public static SymmetricKeyAlgorithm forId(int id) { public static SymmetricKeyAlgorithm fromId(int id) {
return MAP.get(id); return MAP.get(id);
} }

View File

@ -33,7 +33,6 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
private PGPSecretKeyRingCollection decryptionKeys; private PGPSecretKeyRingCollection decryptionKeys;
private SecretKeyRingProtector decryptionKeyDecryptor; private SecretKeyRingProtector decryptionKeyDecryptor;
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>(); private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private Set<Long> trustedKeyIds = new HashSet<>();
private MissingPublicKeyCallback missingPublicKeyCallback = null; private MissingPublicKeyCallback missingPublicKeyCallback = null;
@Override @Override
@ -66,22 +65,23 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
PGPPublicKeyRingCollection publicKeyRingCollection) { PGPPublicKeyRingCollection publicKeyRingCollection) {
Set<PGPPublicKeyRing> publicKeyRings = new HashSet<>(); Set<PGPPublicKeyRing> publicKeyRings = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) { for (Iterator<PGPPublicKeyRing> i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) {
publicKeyRings.add(i.next()); PGPPublicKeyRing p = i.next();
if (trustedKeyIds.contains(p.getPublicKey().getKeyID())) {
publicKeyRings.add(p);
}
} }
return verifyWith(trustedKeyIds, publicKeyRings); return verifyWith(publicKeyRings);
} }
@Override @Override
public MissingPublicKeyFeedback verifyWith(Set<Long> trustedKeyIds, Set<PGPPublicKeyRing> publicKeyRings) { public MissingPublicKeyFeedback verifyWith(Set<PGPPublicKeyRing> publicKeyRings) {
DecryptionBuilder.this.verificationKeys = publicKeyRings; DecryptionBuilder.this.verificationKeys = publicKeyRings;
DecryptionBuilder.this.trustedKeyIds = trustedKeyIds;
return new MissingPublicKeyFeedbackImpl(); return new MissingPublicKeyFeedbackImpl();
} }
@Override @Override
public Build doNotVerify() { public Build doNotVerify() {
DecryptionBuilder.this.verificationKeys = null; DecryptionBuilder.this.verificationKeys = null;
DecryptionBuilder.this.trustedKeyIds = null;
return new BuildImpl(); return new BuildImpl();
} }
} }
@ -105,7 +105,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
@Override @Override
public DecryptionStream build() throws IOException, PGPException { public DecryptionStream build() throws IOException, PGPException {
return DecryptionStreamFactory.create(inputStream, return DecryptionStreamFactory.create(inputStream,
decryptionKeys, decryptionKeyDecryptor, verificationKeys, trustedKeyIds, missingPublicKeyCallback); decryptionKeys, decryptionKeyDecryptor, verificationKeys, missingPublicKeyCallback);
} }
} }
} }

View File

@ -41,7 +41,7 @@ public interface DecryptionBuilderInterface {
MissingPublicKeyFeedback verifyWith(Set<Long> trustedFingerprints, PGPPublicKeyRingCollection publicKeyRings); MissingPublicKeyFeedback verifyWith(Set<Long> trustedFingerprints, PGPPublicKeyRingCollection publicKeyRings);
MissingPublicKeyFeedback verifyWith(Set<Long> trustedFingerprints, Set<PGPPublicKeyRing> publicKeyRings); MissingPublicKeyFeedback verifyWith(Set<PGPPublicKeyRing> publicKeyRings);
Build doNotVerify(); Build doNotVerify();

View File

@ -23,8 +23,10 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.vanitasvitae.crypto.pgpainless.PainlessStream;
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
@ -51,10 +53,12 @@ import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
public class DecryptionStreamFactory { public class DecryptionStreamFactory {
private static final Logger LOGGER = Logger.getLogger(DecryptionStreamFactory.class.getName());
private static final Level LEVEL = Level.INFO;
private final PGPSecretKeyRingCollection decryptionKeys; private final PGPSecretKeyRingCollection decryptionKeys;
private final SecretKeyRingProtector decryptionKeyDecryptor; private final SecretKeyRingProtector decryptionKeyDecryptor;
private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>(); private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private final Set<Long> trustedKeyIds = new HashSet<>();
private final MissingPublicKeyCallback missingPublicKeyCallback; private final MissingPublicKeyCallback missingPublicKeyCallback;
private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder(); private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder();
@ -65,12 +69,10 @@ public class DecryptionStreamFactory {
private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys, private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingProtector decryptor, SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys, Set<PGPPublicKeyRing> verificationKeys,
Set<Long> trustedKeyIds,
MissingPublicKeyCallback missingPublicKeyCallback) { MissingPublicKeyCallback missingPublicKeyCallback) {
this.decryptionKeys = decryptionKeys; this.decryptionKeys = decryptionKeys;
this.decryptionKeyDecryptor = decryptor; this.decryptionKeyDecryptor = decryptor;
this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList()); this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList());
this.trustedKeyIds.addAll(trustedKeyIds != null ? trustedKeyIds : Collections.emptyList());
this.missingPublicKeyCallback = missingPublicKeyCallback; this.missingPublicKeyCallback = missingPublicKeyCallback;
} }
@ -78,14 +80,12 @@ public class DecryptionStreamFactory {
PGPSecretKeyRingCollection decryptionKeys, PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingProtector decryptor, SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys, Set<PGPPublicKeyRing> verificationKeys,
Set<Long> trustedKeyIds,
MissingPublicKeyCallback missingPublicKeyCallback) MissingPublicKeyCallback missingPublicKeyCallback)
throws IOException, PGPException { throws IOException, PGPException {
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys,
decryptor, decryptor,
verificationKeys, verificationKeys,
trustedKeyIds,
missingPublicKeyCallback); missingPublicKeyCallback);
PGPObjectFactory objectFactory = new PGPObjectFactory( PGPObjectFactory objectFactory = new PGPObjectFactory(
@ -100,6 +100,7 @@ public class DecryptionStreamFactory {
while ((pgpObj = objectFactory.nextObject()) != null) { while ((pgpObj = objectFactory.nextObject()) != null) {
if (pgpObj instanceof PGPEncryptedDataList) { if (pgpObj instanceof PGPEncryptedDataList) {
LOGGER.log(LEVEL, "Encountered PGPEncryptedDataList");
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpObj; PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpObj;
InputStream nextStream = decrypt(encDataList); InputStream nextStream = decrypt(encDataList);
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc); objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
@ -111,20 +112,25 @@ public class DecryptionStreamFactory {
InputStream nextStream = compressedData.getDataStream(); InputStream nextStream = compressedData.getDataStream();
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm())); resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc); objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
LOGGER.log(LEVEL, "Encountered PGPCompressedData: " +
CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
return wrap(objectFactory); return wrap(objectFactory);
} }
if (pgpObj instanceof PGPOnePassSignatureList) { if (pgpObj instanceof PGPOnePassSignatureList) {
PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) pgpObj; PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) pgpObj;
LOGGER.log(LEVEL, "Encountered PGPOnePassSignatureList of size " + onePassSignatures.size());
initOnePassSignatures(onePassSignatures); initOnePassSignatures(onePassSignatures);
return wrap(objectFactory); return wrap(objectFactory);
} }
if (pgpObj instanceof PGPLiteralData) { if (pgpObj instanceof PGPLiteralData) {
LOGGER.log(LEVEL, "Encountered PGPLiteralData");
PGPLiteralData literalData = (PGPLiteralData) pgpObj; PGPLiteralData literalData = (PGPLiteralData) pgpObj;
InputStream literalDataInputStream = literalData.getInputStream(); InputStream literalDataInputStream = literalData.getInputStream();
if (verifiableOnePassSignatures.isEmpty()) { if (verifiableOnePassSignatures.isEmpty()) {
LOGGER.log(LEVEL, "No OnePassSignatures found -> We are done");
return literalDataInputStream; return literalDataInputStream;
} }
@ -133,14 +139,14 @@ public class DecryptionStreamFactory {
} }
} }
throw new PGPException("No Literal Data Packet found!"); throw new PGPException("No Literal Data Packet found");
} }
private InputStream decrypt(PGPEncryptedDataList encryptedDataList) private InputStream decrypt(PGPEncryptedDataList encryptedDataList)
throws PGPException { throws PGPException {
Iterator<?> iterator = encryptedDataList.getEncryptedDataObjects(); Iterator<?> iterator = encryptedDataList.getEncryptedDataObjects();
if (!iterator.hasNext()) { if (!iterator.hasNext()) {
throw new PGPException("Decryption failed - No encrypted data found!"); throw new PGPException("Decryption failed - EncryptedDataList has no items");
} }
PGPPrivateKey decryptionKey = null; PGPPrivateKey decryptionKey = null;
@ -150,27 +156,37 @@ public class DecryptionStreamFactory {
long keyId = encryptedSessionKey.getKeyID(); long keyId = encryptedSessionKey.getKeyID();
resultBuilder.addRecipientKeyId(keyId); resultBuilder.addRecipientKeyId(keyId);
LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId));
if (decryptionKey != null) { if (decryptionKey != null) {
continue; continue;
} }
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId); PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
if (secretKey != null) { if (secretKey != null) {
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId)); decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId));
resultBuilder.setDecryptionKeyId(keyId); resultBuilder.setDecryptionKeyId(keyId);
} }
} }
if (decryptionKey == null) { if (decryptionKey == null) {
throw new PGPException("Decryption failed - No suitable decryption key found!"); throw new PGPException("Decryption failed - No suitable decryption key found");
} }
PublicKeyDataDecryptorFactory keyDecryptor = new BcPublicKeyDataDecryptorFactory(decryptionKey); PublicKeyDataDecryptorFactory keyDecryptor = new BcPublicKeyDataDecryptorFactory(decryptionKey);
resultBuilder.setSymmetricKeyAlgorithm( SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm
SymmetricKeyAlgorithm.forId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor))); .fromId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor));
resultBuilder.setIntegrityProtected(encryptedSessionKey.isIntegrityProtected());
LOGGER.log(LEVEL, "Message is encrypted using " + symmetricKeyAlgorithm);
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
if (encryptedSessionKey.isIntegrityProtected()) {
LOGGER.log(LEVEL, "Message is integrity protected");
resultBuilder.setIntegrityProtected(true);
} else {
LOGGER.log(LEVEL, "Message is not integrity protected");
resultBuilder.setIntegrityProtected(false);
}
InputStream decryptionStream = encryptedSessionKey.getDataStream(keyDecryptor); InputStream decryptionStream = encryptedSessionKey.getDataStream(keyDecryptor);
return decryptionStream; return decryptionStream;
@ -179,7 +195,7 @@ public class DecryptionStreamFactory {
private void initOnePassSignatures(PGPOnePassSignatureList onePassSignatureList) throws PGPException { private void initOnePassSignatures(PGPOnePassSignatureList onePassSignatureList) throws PGPException {
Iterator<PGPOnePassSignature> iterator = onePassSignatureList.iterator(); Iterator<PGPOnePassSignature> iterator = onePassSignatureList.iterator();
if (!iterator.hasNext()) { if (!iterator.hasNext()) {
throw new PGPException("Verification failed - No OnePassSignatures found!"); throw new PGPException("Verification failed - No OnePassSignatures found");
} }
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -187,10 +203,16 @@ public class DecryptionStreamFactory {
long keyId = signature.getKeyID(); long keyId = signature.getKeyID();
resultBuilder.addSignatureKeyId(keyId); resultBuilder.addSignatureKeyId(keyId);
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
// Find public key // Find public key
PGPPublicKey verificationKey = null; PGPPublicKey verificationKey = null;
for (PGPPublicKeyRing publicKeyRing : verificationKeys) { for (PGPPublicKeyRing publicKeyRing : verificationKeys) {
verificationKey = publicKeyRing.getPublicKey(signature.getKeyID()); verificationKey = publicKeyRing.getPublicKey(keyId);
if (verificationKey != null) {
LOGGER.log(LEVEL, "Found respective public key " + Long.toHexString(keyId));
break;
}
} }
if (verificationKey != null) { if (verificationKey != null) {

View File

@ -20,6 +20,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Map; import java.util.Map;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -32,11 +34,14 @@ import org.bouncycastle.openpgp.PGPSignatureList;
public class SignatureVerifyingInputStream extends FilterInputStream { public class SignatureVerifyingInputStream extends FilterInputStream {
private static final Logger LOGGER = Logger.getLogger(SignatureVerifyingInputStream.class.getName()); private static final Logger LOGGER = Logger.getLogger(SignatureVerifyingInputStream.class.getName());
private static final Level LEVEL = Level.INFO;
private final PGPObjectFactory objectFactory; private final PGPObjectFactory objectFactory;
private final Map<Long, PGPOnePassSignature> onePassSignatures; private final Map<Long, PGPOnePassSignature> onePassSignatures;
private final PainlessResult.Builder resultBuilder; private final PainlessResult.Builder resultBuilder;
private boolean validated = false;
protected SignatureVerifyingInputStream(InputStream inputStream, protected SignatureVerifyingInputStream(InputStream inputStream,
PGPObjectFactory objectFactory, PGPObjectFactory objectFactory,
Map<Long, PGPOnePassSignature> onePassSignatures, Map<Long, PGPOnePassSignature> onePassSignatures,
@ -45,6 +50,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
this.objectFactory = objectFactory; this.objectFactory = objectFactory;
this.resultBuilder = resultBuilder; this.resultBuilder = resultBuilder;
this.onePassSignatures = onePassSignatures; this.onePassSignatures = onePassSignatures;
LOGGER.log(LEVEL, "Begin verifying OnePassSignatures");
} }
private void updateOnePassSignatures(byte data) { private void updateOnePassSignatures(byte data) {
@ -60,8 +67,16 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
} }
private void validateOnePassSignatures() throws IOException { private void validateOnePassSignatures() throws IOException {
if (validated) {
LOGGER.log(LEVEL, "Validated signatures already. Skip");
return;
}
validated = true;
if (onePassSignatures.isEmpty()) { if (onePassSignatures.isEmpty()) {
LOGGER.log(Level.FINE, "No One-Pass-Signatures found -> No validation."); LOGGER.log(LEVEL, "No One-Pass-Signatures found -> No validation");
return; return;
} }
@ -77,19 +92,20 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
} }
if (signatureList == null || signatureList.isEmpty()) { if (signatureList == null || signatureList.isEmpty()) {
throw new IOException("Verification failed - No Signatures found!"); throw new IOException("Verification failed - No Signatures found");
} }
for (PGPSignature signature : signatureList) { for (PGPSignature signature : signatureList) {
PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID()); PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID());
if (onePassSignature == null) { if (onePassSignature == null) {
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
continue; continue;
} }
if (!onePassSignature.verify(signature)) { if (!onePassSignature.verify(signature)) {
throw new SignatureException("Bad Signature of key " + signature.getKeyID()); throw new SignatureException("Bad Signature of key " + signature.getKeyID());
} else { } else {
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID()); resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID());
onePassSignatures.remove(signature.getKeyID());
} }
} }
} catch (PGPException | SignatureException e) { } catch (PGPException | SignatureException e) {
@ -130,17 +146,17 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
@Override @Override
public long skip(long n) { public long skip(long n) {
throw new UnsupportedOperationException("skip() is not supported."); throw new UnsupportedOperationException("skip() is not supported");
} }
@Override @Override
public synchronized void mark(int readlimit) { public synchronized void mark(int readlimit) {
throw new UnsupportedOperationException("mark() not supported."); throw new UnsupportedOperationException("mark() not supported");
} }
@Override @Override
public synchronized void reset() { public synchronized void reset() {
throw new UnsupportedOperationException("reset() is not supported."); throw new UnsupportedOperationException("reset() is not supported");
} }
@Override @Override