From 70c4dcd1d2d46d4022c6c2cf63895894e55f9ec8 Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Tue, 15 Jun 2021 17:08:40 +0200 Subject: [PATCH 1/8] Begin introducing new Decryption API --- .../ConsumerOptions.java | 188 ++++++++++++++++++ .../DecryptionBuilder.java | 32 ++- .../DecryptionBuilderInterface.java | 2 + .../DecryptionStreamFactory.java | 132 ++++++------ .../SymmetricEncryptionTest.java | 2 +- 5 files changed, 293 insertions(+), 63 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java new file mode 100644 index 00000000..e9f6c916 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java @@ -0,0 +1,188 @@ +package org.pgpainless.decryption_verification; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.util.Passphrase; + +/** + * Options for decryption and signature verification. + */ +public class ConsumerOptions { + + private Date verifyNotBefore; + private Date verifyNotAfter; + + // Set of verification keys + private Set<PGPPublicKeyRing> certificates = new HashSet<>(); + private Set<PGPSignature> detachedSignatures = new HashSet<>(); + private MissingPublicKeyCallback missingCertificateCallback = null; + + // Session key for decryption without passphrase/key + private byte[] sessionKey = null; + + private final Map<PGPSecretKeyRing, SecretKeyRingProtector> decryptionKeys = new HashMap<>(); + private final Set<Passphrase> decryptionPassphrases = new HashSet<>(); + + + /** + * Consider signatures made before the given timestamp invalid. + * + * @param timestamp timestamp + * @return options + */ + public ConsumerOptions verifyNotBefore(Date timestamp) { + this.verifyNotBefore = timestamp; + return this; + } + + /** + * Consider signatures made after the given timestamp invalid. + * + * @param timestamp timestamp + * @return options + */ + public ConsumerOptions verifyNotAfter(Date timestamp) { + this.verifyNotAfter = timestamp; + return this; + } + + /** + * Add a certificate (public key ring) for signature verification. + * + * @param verificationCert certificate for signature verification + * @return options + */ + public ConsumerOptions addVerificationCert(PGPPublicKeyRing verificationCert) { + this.certificates.add(verificationCert); + return this; + } + + /** + * Add a set of certificates (public key rings) for signature verification. + * + * @param verificationCerts certificates for signature verification + * @return options + */ + public ConsumerOptions addVerificationCerts(PGPPublicKeyRingCollection verificationCerts) { + for (PGPPublicKeyRing certificate : verificationCerts) { + addVerificationCert(certificate); + } + return this; + } + + /** + * Add a detached signature for the signature verification process. + * + * @param detachedSignature detached signature + * @return options + */ + public ConsumerOptions addVerificationOfDetachedSignature(PGPSignature detachedSignature) { + detachedSignatures.add(detachedSignature); + return this; + } + + /** + * Set a callback that's used when a certificate (public key) is missing for signature verification. + * + * @param callback callback + * @return options + */ + public ConsumerOptions setMissingCertificateCallback(MissingPublicKeyCallback callback) { + this.missingCertificateCallback = callback; + return this; + } + + + /** + * Attempt decryption using a session key. + * + * Note: PGPainless does not yet support decryption with session keys. + * TODO: Implement + * + * @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-2.1">RFC4880 on Session Keys</a> + * + * @param sessionKey session key + * @return options + */ + public ConsumerOptions setSessionKey(@Nonnull byte[] sessionKey) { + this.sessionKey = sessionKey; + return this; + } + + /** + * Add a key for message decryption. + * The key is expected to be unencrypted. + * + * @param key unencrypted key + * @return options + */ + public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key) { + return addDecryptionKey(key, SecretKeyRingProtector.unprotectedKeys()); + } + + /** + * Add a key for message decryption. If the key is encrypted, the {@link SecretKeyRingProtector} is used to decrypt it + * when needed. + * + * @param key key + * @param keyRingProtector protector for the secret key + * @return options + */ + public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key, @Nonnull SecretKeyRingProtector keyRingProtector) { + decryptionKeys.put(key, keyRingProtector); + return this; + } + + /** + * Add a passphrase for message decryption. + * + * @param passphrase passphrase + * @return options + */ + public ConsumerOptions addDecryptionPassphrase(@Nonnull Passphrase passphrase) { + decryptionPassphrases.add(passphrase); + return this; + } + + public Set<PGPSecretKeyRing> getDecryptionKeys() { + return Collections.unmodifiableSet(decryptionKeys.keySet()); + } + + public Set<Passphrase> getDecryptionPassphrases() { + return Collections.unmodifiableSet(decryptionPassphrases); + } + + public Set<PGPPublicKeyRing> getCertificates() { + return Collections.unmodifiableSet(certificates); + } + + public MissingPublicKeyCallback getMissingCertificateCallback() { + return missingCertificateCallback; + } + + public SecretKeyRingProtector getSecretKeyProtector(PGPSecretKeyRing decryptionKeyRing) { + return decryptionKeys.get(decryptionKeyRing); + } + + public Set<PGPSignature> getDetachedSignatures() { + return Collections.unmodifiableSet(detachedSignatures); + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java index 8bafe6ff..25e76057 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java @@ -63,6 +63,15 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { class DecryptWithImpl implements DecryptWith { + @Override + public DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException { + if (consumerOptions == null) { + throw new IllegalArgumentException("Consumer options cannot be null."); + } + + return DecryptionStreamFactory.create(inputStream, consumerOptions); + } + @Override public Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings) { DecryptionBuilder.this.decryptionKeys = secretKeyRings; @@ -219,8 +228,27 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { @Override public DecryptionStream build() throws IOException, PGPException { - return DecryptionStreamFactory.create(inputStream, decryptionKeys, decryptionKeyDecryptor, - decryptionPassphrase, detachedSignatures, verificationKeys, missingPublicKeyCallback); + ConsumerOptions options = new ConsumerOptions(); + + for (PGPSecretKeyRing decryptionKey : (decryptionKeys != null ? decryptionKeys : Collections.<PGPSecretKeyRing>emptyList())) { + options.addDecryptionKey(decryptionKey, decryptionKeyDecryptor); + } + + for (PGPPublicKeyRing certificate : (verificationKeys != null ? verificationKeys : Collections.<PGPPublicKeyRing>emptyList())) { + options.addVerificationCert(certificate); + } + + for (PGPSignature detachedSignature : (detachedSignatures != null ? detachedSignatures : Collections.<PGPSignature>emptyList())) { + options.addVerificationOfDetachedSignature(detachedSignature); + } + + options.setMissingCertificateCallback(missingPublicKeyCallback); + + if (decryptionPassphrase != null) { + options.addDecryptionPassphrase(decryptionPassphrase); + } + + return DecryptionStreamFactory.create(inputStream, options); } } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java index c890ff0f..45717753 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -46,6 +46,8 @@ public interface DecryptionBuilderInterface { interface DecryptWith { + DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException; + /** * Decrypt the encrypted data using the secret keys found in the provided {@link PGPSecretKeyRingCollection}. * Here it is assumed that the secret keys are not password protected. diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java index c9c42034..686876a9 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -54,6 +54,7 @@ import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.CompressionAlgorithm; +import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.StreamEncoding; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.exception.MessageNotIntegrityProtectedException; @@ -62,6 +63,7 @@ import org.pgpainless.exception.UnacceptableAlgorithmException; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.SubkeyIdentifier; +import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.signature.DetachedSignature; @@ -75,11 +77,7 @@ public final class DecryptionStreamFactory { private static final Level LEVEL = Level.FINE; private static final int MAX_RECURSION_DEPTH = 16; - private final PGPSecretKeyRingCollection decryptionKeys; - private final SecretKeyRingProtector decryptionKeyDecryptor; - private final Passphrase decryptionPassphrase; - private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>(); - private final MissingPublicKeyCallback missingPublicKeyCallback; + private final ConsumerOptions options; private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder(); private static final PGPContentVerifierBuilderProvider verifierBuilderProvider = ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(); @@ -87,47 +85,30 @@ public final class DecryptionStreamFactory { private final Map<OpenPgpV4Fingerprint, OnePassSignature> verifiableOnePassSignatures = new HashMap<>(); private final List<IntegrityProtectedInputStream> integrityProtectedStreams = new ArrayList<>(); - private DecryptionStreamFactory(@Nullable PGPSecretKeyRingCollection decryptionKeys, - @Nullable SecretKeyRingProtector decryptor, - @Nullable Passphrase decryptionPassphrase, - @Nullable Set<PGPPublicKeyRing> verificationKeys, - @Nullable MissingPublicKeyCallback missingPublicKeyCallback) { - this.decryptionKeys = decryptionKeys; - this.decryptionKeyDecryptor = decryptor; - this.decryptionPassphrase = decryptionPassphrase; - this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList()); - this.missingPublicKeyCallback = missingPublicKeyCallback; + public DecryptionStreamFactory(ConsumerOptions options) { + this.options = options; } public static DecryptionStream create(@Nonnull InputStream inputStream, - @Nullable PGPSecretKeyRingCollection decryptionKeys, - @Nullable SecretKeyRingProtector decryptor, - @Nullable Passphrase decryptionPassphrase, - @Nullable List<PGPSignature> detachedSignatures, - @Nullable Set<PGPPublicKeyRing> verificationKeys, - @Nullable MissingPublicKeyCallback missingPublicKeyCallback) - throws IOException, PGPException { - InputStream pgpInputStream; - DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, - decryptionPassphrase, verificationKeys, missingPublicKeyCallback); + @Nonnull ConsumerOptions options) throws PGPException, IOException { + InputStream pgpInputStream = inputStream; + DecryptionStreamFactory factory = new DecryptionStreamFactory(options); - if (detachedSignatures != null) { - pgpInputStream = inputStream; - for (PGPSignature signature : detachedSignatures) { - PGPPublicKeyRing signingKeyRing = factory.findSignatureVerificationKeyRing(signature.getKeyID()); - if (signingKeyRing == null) { - continue; - } - PGPPublicKey signingKey = signingKeyRing.getPublicKey(signature.getKeyID()); - signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey); - factory.resultBuilder.addDetachedSignature( - new DetachedSignature(signature, signingKeyRing, new SubkeyIdentifier(signingKeyRing, signature.getKeyID()))); + for (PGPSignature signature : options.getDetachedSignatures()) { + PGPPublicKeyRing signingKeyRing = factory.findSignatureVerificationKeyRing(signature.getKeyID()); + if (signingKeyRing == null) { + continue; } - } else { - PGPObjectFactory objectFactory = new PGPObjectFactory( - PGPUtil.getDecoderStream(inputStream), keyFingerprintCalculator); - pgpInputStream = factory.processPGPPackets(objectFactory, 1); + PGPPublicKey signingKey = signingKeyRing.getPublicKey(signature.getKeyID()); + signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey); + factory.resultBuilder.addDetachedSignature( + new DetachedSignature(signature, signingKeyRing, new SubkeyIdentifier(signingKeyRing, signature.getKeyID()))); } + + PGPObjectFactory objectFactory = new PGPObjectFactory( + PGPUtil.getDecoderStream(inputStream), keyFingerprintCalculator); + pgpInputStream = factory.processPGPPackets(objectFactory, 1); + return new DecryptionStream(pgpInputStream, factory.resultBuilder, factory.integrityProtectedStreams); } @@ -210,50 +191,68 @@ public final class DecryptionStreamFactory { while (encryptedDataIterator.hasNext()) { PGPEncryptedData encryptedData = encryptedDataIterator.next(); + // TODO: Can we just skip non-integrity-protected packages? if (!encryptedData.isIntegrityProtected()) { throw new MessageNotIntegrityProtectedException(); } + // Data is passphrase encrypted if (encryptedData instanceof PGPPBEEncryptedData) { PGPPBEEncryptedData pbeEncryptedData = (PGPPBEEncryptedData) encryptedData; - if (decryptionPassphrase != null) { + for (Passphrase passphrase : options.getDecryptionPassphrases()) { PBEDataDecryptorFactory passphraseDecryptor = ImplementationFactory.getInstance() - .getPBEDataDecryptorFactory(decryptionPassphrase); - SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.fromId( - pbeEncryptedData.getSymmetricAlgorithm(passphraseDecryptor)); - throwIfAlgorithmIsRejected(symmetricKeyAlgorithm); - resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm); - + .getPBEDataDecryptorFactory(passphrase); try { - return pbeEncryptedData.getDataStream(passphraseDecryptor); + InputStream decryptedDataStream = pbeEncryptedData.getDataStream(passphraseDecryptor); + + SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.fromId( + pbeEncryptedData.getSymmetricAlgorithm(passphraseDecryptor)); + throwIfAlgorithmIsRejected(symmetricKeyAlgorithm); + resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm); + + return decryptedDataStream; } catch (PGPException e) { LOGGER.log(LEVEL, "Probable passphrase mismatch, skip PBE encrypted data block", e); } } } + // data is public key encrypted else if (encryptedData instanceof PGPPublicKeyEncryptedData) { + if (options.getDecryptionKeys().isEmpty()) { + + } PGPPublicKeyEncryptedData publicKeyEncryptedData = (PGPPublicKeyEncryptedData) encryptedData; long keyId = publicKeyEncryptedData.getKeyID(); - if (decryptionKeys != null) { + if (!options.getDecryptionKeys().isEmpty()) { // Known key id if (keyId != 0) { LOGGER.log(LEVEL, "PGPEncryptedData is encrypted for key " + Long.toHexString(keyId)); resultBuilder.addRecipientKeyId(keyId); - PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId); - if (secretKey != null) { + PGPSecretKeyRing decryptionKeyRing = findDecryptionKeyRing(keyId); + if (decryptionKeyRing != null) { + PGPSecretKey secretKey = decryptionKeyRing.getSecretKey(keyId); LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId)); // Watch out! This assignment is possibly done multiple times. encryptedSessionKey = publicKeyEncryptedData; - decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, decryptionKeyDecryptor); + decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, options.getSecretKeyProtector(decryptionKeyRing)); resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey)); } - } else { - // Hidden recipient + } + + // Hidden recipient + else { LOGGER.log(LEVEL, "Hidden recipient detected. Try to decrypt with all available secret keys."); - outerloop: for (PGPSecretKeyRing ring : decryptionKeys) { - for (PGPSecretKey key : ring) { - PGPPrivateKey privateKey = key.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(key.getKeyID())); + outerloop: for (PGPSecretKeyRing ring : options.getDecryptionKeys()) { + KeyRingInfo info = new KeyRingInfo(ring); + List<PGPPublicKey> encryptionSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS); + for (PGPPublicKey pubkey : encryptionSubkeys) { + PGPSecretKey key = ring.getSecretKey(pubkey.getKeyID()); + if (key == null) { + continue; + } + + PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(key, options.getSecretKeyProtector(ring).getDecryptor(key.getKeyID())); PublicKeyDataDecryptorFactory decryptorFactory = ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey); try { publicKeyEncryptedData.getSymmetricAlgorithm(decryptorFactory); // will only succeed if we have the right secret key @@ -271,7 +270,11 @@ public final class DecryptionStreamFactory { } } } + return decryptWith(encryptedSessionKey, decryptionKey); + } + private InputStream decryptWith(PGPPublicKeyEncryptedData encryptedSessionKey, PGPPrivateKey decryptionKey) + throws PGPException { if (decryptionKey == null) { throw new MissingDecryptionMethodException("Decryption failed - No suitable decryption key or passphrase found"); } @@ -339,9 +342,18 @@ public final class DecryptionStreamFactory { verifiableOnePassSignatures.put(fingerprint, onePassSignature); } + private PGPSecretKeyRing findDecryptionKeyRing(long keyId) { + for (PGPSecretKeyRing key : options.getDecryptionKeys()) { + if (key.getSecretKey(keyId) != null) { + return key; + } + } + return null; + } + private PGPPublicKeyRing findSignatureVerificationKeyRing(long keyId) { PGPPublicKeyRing verificationKeyRing = null; - for (PGPPublicKeyRing publicKeyRing : verificationKeys) { + for (PGPPublicKeyRing publicKeyRing : options.getCertificates()) { PGPPublicKey verificationKey = publicKeyRing.getPublicKey(keyId); if (verificationKey != null) { LOGGER.log(LEVEL, "Found public key " + Long.toHexString(keyId) + " for signature verification"); @@ -350,8 +362,8 @@ public final class DecryptionStreamFactory { } } - if (verificationKeyRing == null && missingPublicKeyCallback != null) { - verificationKeyRing = missingPublicKeyCallback.onMissingPublicKeyEncountered(keyId); + if (verificationKeyRing == null && options.getMissingCertificateCallback() != null) { + verificationKeyRing = options.getMissingCertificateCallback().onMissingPublicKeyEncountered(keyId); } return verificationKeyRing; diff --git a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java index 1dc95319..9c7e6f8b 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java @@ -109,7 +109,7 @@ public class SymmetricEncryptionTest { @ParameterizedTest @MethodSource("org.pgpainless.util.TestUtil#provideImplementationFactories") - public void testMissmatchPassphraseFails(ImplementationFactory implementationFactory) throws IOException, PGPException { + public void testMismatchPassphraseFails(ImplementationFactory implementationFactory) throws IOException, PGPException { ImplementationFactory.setFactoryImplementation(implementationFactory); byte[] bytes = new byte[5000]; From 8f425cd31d582dfb0317a1805ff59acd30c1ed17 Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Tue, 15 Jun 2021 17:35:58 +0200 Subject: [PATCH 2/8] Fix parsing of non-OpenPGP messages when handling detached signatures --- .../DecryptionStreamFactory.java | 22 +++++++++++---- .../MissingLiteralDataException.java | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/exception/MissingLiteralDataException.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java index 686876a9..685417fa 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -15,6 +15,7 @@ */ package org.pgpainless.decryption_verification; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -59,6 +60,7 @@ import org.pgpainless.algorithm.StreamEncoding; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.exception.MessageNotIntegrityProtectedException; import org.pgpainless.exception.MissingDecryptionMethodException; +import org.pgpainless.exception.MissingLiteralDataException; import org.pgpainless.exception.UnacceptableAlgorithmException; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; @@ -91,7 +93,8 @@ public final class DecryptionStreamFactory { public static DecryptionStream create(@Nonnull InputStream inputStream, @Nonnull ConsumerOptions options) throws PGPException, IOException { - InputStream pgpInputStream = inputStream; + BufferedInputStream bufferedIn = new BufferedInputStream(inputStream); + bufferedIn.mark(200); DecryptionStreamFactory factory = new DecryptionStreamFactory(options); for (PGPSignature signature : options.getDetachedSignatures()) { @@ -106,10 +109,19 @@ public final class DecryptionStreamFactory { } PGPObjectFactory objectFactory = new PGPObjectFactory( - PGPUtil.getDecoderStream(inputStream), keyFingerprintCalculator); - pgpInputStream = factory.processPGPPackets(objectFactory, 1); + PGPUtil.getDecoderStream(bufferedIn), keyFingerprintCalculator); - return new DecryptionStream(pgpInputStream, factory.resultBuilder, factory.integrityProtectedStreams); + try { + // Parse OpenPGP message + inputStream = factory.processPGPPackets(objectFactory, 1); + } catch (MissingLiteralDataException e) { + // Not an OpenPGP message. Reset the buffered stream to parse the message as arbitrary binary data + // to allow for detached signature verification. + bufferedIn.reset(); + inputStream = bufferedIn; + } + + return new DecryptionStream(inputStream, factory.resultBuilder, factory.integrityProtectedStreams); } private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory, int depth) throws IOException, PGPException { @@ -132,7 +144,7 @@ public final class DecryptionStreamFactory { } } - throw new PGPException("No Literal Data Packet found"); + throw new MissingLiteralDataException("No Literal Data Packet found"); } private InputStream processPGPEncryptedDataList(PGPEncryptedDataList pgpEncryptedDataList, int depth) diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/MissingLiteralDataException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/MissingLiteralDataException.java new file mode 100644 index 00000000..aca46a41 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/exception/MissingLiteralDataException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.exception; + +import org.bouncycastle.openpgp.PGPException; + +/** + * Exception that gets thrown if a {@link org.bouncycastle.bcpg.LiteralDataPacket} is expected, but not found. + */ +public class MissingLiteralDataException extends PGPException { + + public MissingLiteralDataException(String message) { + super(message); + } +} From 715d055b412969f0a6e648ff39d30b99c0cbd4c3 Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Tue, 15 Jun 2021 17:56:36 +0200 Subject: [PATCH 3/8] Add documentation and deprecate old methods --- .../ConsumerOptions.java | 60 ++++++++++--- .../DecryptionBuilderInterface.java | 88 ++++++++++++++++++- .../DecryptionStreamFactory.java | 9 +- 3 files changed, 135 insertions(+), 22 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java index e9f6c916..f0ae17f1 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java @@ -1,23 +1,32 @@ +/* + * Copyright 2021 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.pgpainless.decryption_verification; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.util.Passphrase; @@ -31,8 +40,8 @@ public class ConsumerOptions { private Date verifyNotAfter; // Set of verification keys - private Set<PGPPublicKeyRing> certificates = new HashSet<>(); - private Set<PGPSignature> detachedSignatures = new HashSet<>(); + private final Set<PGPPublicKeyRing> certificates = new HashSet<>(); + private final Set<PGPSignature> detachedSignatures = new HashSet<>(); private MissingPublicKeyCallback missingCertificateCallback = null; // Session key for decryption without passphrase/key @@ -53,6 +62,10 @@ public class ConsumerOptions { return this; } + public Date getVerifyNotBefore() { + return verifyNotBefore; + } + /** * Consider signatures made after the given timestamp invalid. * @@ -64,6 +77,10 @@ public class ConsumerOptions { return this; } + public Date getVerifyNotAfter() { + return verifyNotAfter; + } + /** * Add a certificate (public key ring) for signature verification. * @@ -127,6 +144,21 @@ public class ConsumerOptions { return this; } + /** + * Return the session key. + * + * @return session key or null + */ + public @Nullable byte[] getSessionKey() { + if (sessionKey == null) { + return null; + } + + byte[] sk = new byte[sessionKey.length]; + System.arraycopy(sessionKey, 0, sk, 0, sessionKey.length); + return sk; + } + /** * Add a key for message decryption. * The key is expected to be unencrypted. @@ -162,27 +194,27 @@ public class ConsumerOptions { return this; } - public Set<PGPSecretKeyRing> getDecryptionKeys() { + public @Nonnull Set<PGPSecretKeyRing> getDecryptionKeys() { return Collections.unmodifiableSet(decryptionKeys.keySet()); } - public Set<Passphrase> getDecryptionPassphrases() { + public @Nonnull Set<Passphrase> getDecryptionPassphrases() { return Collections.unmodifiableSet(decryptionPassphrases); } - public Set<PGPPublicKeyRing> getCertificates() { + public @Nonnull Set<PGPPublicKeyRing> getCertificates() { return Collections.unmodifiableSet(certificates); } - public MissingPublicKeyCallback getMissingCertificateCallback() { + public @Nullable MissingPublicKeyCallback getMissingCertificateCallback() { return missingCertificateCallback; } - public SecretKeyRingProtector getSecretKeyProtector(PGPSecretKeyRing decryptionKeyRing) { + public @Nullable SecretKeyRingProtector getSecretKeyProtector(PGPSecretKeyRing decryptionKeyRing) { return decryptionKeys.get(decryptionKeyRing); } - public Set<PGPSignature> getDetachedSignatures() { + public @Nonnull Set<PGPSignature> getDetachedSignatures() { return Collections.unmodifiableSet(detachedSignatures); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java index 45717753..0b9a89e5 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -46,6 +46,14 @@ public interface DecryptionBuilderInterface { interface DecryptWith { + /** + * Add options for decryption / signature verification, such as keys, passphrases etc. + * + * @param consumerOptions consumer options + * @return decryption stream + * @throws PGPException in case of an OpenPGP related error + * @throws IOException in case of an IO error + */ DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException; /** @@ -56,7 +64,11 @@ public interface DecryptionBuilderInterface { * * @param secretKeyRings secret keys * @return api handle + * + * @deprecated use {@link ConsumerOptions#addDecryptionKey(PGPSecretKeyRing, SecretKeyRingProtector)} + * ({@link #withOptions(ConsumerOptions)}) instead. */ + @Deprecated default Verify decryptWith(@Nonnull PGPSecretKeyRingCollection secretKeyRings) { return decryptWith(new UnprotectedKeysProtector(), secretKeyRings); } @@ -68,7 +80,11 @@ public interface DecryptionBuilderInterface { * @param decryptor for unlocking locked secret keys * @param secretKeyRings secret keys * @return api handle + * + * @deprecated use {@link ConsumerOptions#addDecryptionKey(PGPSecretKeyRing, SecretKeyRingProtector)} + * ({@link #withOptions(ConsumerOptions)}) instead. */ + @Deprecated Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings); /** @@ -78,8 +94,13 @@ public interface DecryptionBuilderInterface { * @param decryptor for unlocking locked secret key * @param secretKeyRing secret key * @return api handle + * + * @deprecated use {@link ConsumerOptions#addDecryptionKey(PGPSecretKeyRing, SecretKeyRingProtector)} + * ({@link #withOptions(ConsumerOptions)}) instead. */ - Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing secretKeyRing) throws PGPException, IOException; + @Deprecated + Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing secretKeyRing) + throws PGPException, IOException; /** * Decrypt the encrypted data using a passphrase. @@ -87,7 +108,11 @@ public interface DecryptionBuilderInterface { * * @param passphrase passphrase * @return api handle + * + * @deprecated use {@link ConsumerOptions#addDecryptionPassphrase(Passphrase)} + * ({@link #withOptions(ConsumerOptions)}) instead. */ + @Deprecated Verify decryptWith(@Nonnull Passphrase passphrase); /** @@ -95,32 +120,41 @@ public interface DecryptionBuilderInterface { * Useful for signature verification of signed-only data. * * @return api handle + * + * @deprecated use {@link #withOptions(ConsumerOptions)} instead and set no decryption keys. */ + @Deprecated Verify doNotDecrypt(); } + @Deprecated interface Verify extends VerifyWith { @Override + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings); @Override + @Deprecated default HandleMissingPublicKeys verifyWith(@Nonnull OpenPgpV4Fingerprint trustedFingerprint, @Nonnull PGPPublicKeyRingCollection publicKeyRings) { return verifyWith(Collections.singleton(trustedFingerprint), publicKeyRings); } @Override + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull Set<OpenPgpV4Fingerprint> trustedFingerprints, @Nonnull PGPPublicKeyRingCollection publicKeyRings); @Override + @Deprecated default HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRing publicKeyRing) { return verifyWith(Collections.singleton(publicKeyRing)); } @Override + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull Set<PGPPublicKeyRing> publicKeyRings); /** @@ -130,7 +164,11 @@ public interface DecryptionBuilderInterface { * @return api handle * @throws IOException if some IO error occurs * @throws PGPException if the detached signatures are malformed + * + * @deprecated use {@link ConsumerOptions#addVerificationOfDetachedSignature(PGPSignature)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated default VerifyWith verifyDetachedSignature(@Nonnull byte[] bytes) throws IOException, PGPException { return verifyDetachedSignature(new ByteArrayInputStream(bytes)); } @@ -142,7 +180,11 @@ public interface DecryptionBuilderInterface { * @return api handle * @throws IOException in case something is wrong with the input stream * @throws PGPException if the detached signatures are malformed + * + * @deprecated use {@link ConsumerOptions#addVerificationOfDetachedSignature(PGPSignature)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated VerifyWith verifyDetachedSignature(@Nonnull InputStream inputStream) throws IOException, PGPException; /** @@ -150,7 +192,11 @@ public interface DecryptionBuilderInterface { * * @param signature detached signature * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationOfDetachedSignature(PGPSignature)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated default VerifyWith verifyDetachedSignature(@Nonnull PGPSignature signature) { return verifyDetachedSignatures(Collections.singletonList(signature)); } @@ -160,17 +206,25 @@ public interface DecryptionBuilderInterface { * * @param signatures detached signatures * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationOfDetachedSignature(PGPSignature)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated VerifyWith verifyDetachedSignatures(@Nonnull List<PGPSignature> signatures); /** * Instruct the {@link DecryptionStream} to not verify any signatures. * * @return api handle + * + * @deprecated use {@link DecryptWith#withOptions(ConsumerOptions)} instead and don't set verification keys. */ + @Deprecated Build doNotVerify(); } + @Deprecated interface VerifyWith { /** @@ -178,7 +232,11 @@ public interface DecryptionBuilderInterface { * * @param publicKeyRings public keys * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationCerts(PGPPublicKeyRingCollection)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings); /** @@ -188,7 +246,10 @@ public interface DecryptionBuilderInterface { * @param trustedFingerprint {@link OpenPgpV4Fingerprint} of the public key that shall be used to verify the signatures. * @param publicKeyRings public keys * @return api handle + * @deprecated use {@link ConsumerOptions#addVerificationCert(PGPPublicKeyRing)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated default HandleMissingPublicKeys verifyWith(@Nonnull OpenPgpV4Fingerprint trustedFingerprint, @Nonnull PGPPublicKeyRingCollection publicKeyRings) { return verifyWith(Collections.singleton(trustedFingerprint), publicKeyRings); @@ -201,7 +262,11 @@ public interface DecryptionBuilderInterface { * @param trustedFingerprints set of trusted {@link OpenPgpV4Fingerprint OpenPgpV4Fingerprints}. * @param publicKeyRings public keys * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationCert(PGPPublicKeyRing)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull Set<OpenPgpV4Fingerprint> trustedFingerprints, @Nonnull PGPPublicKeyRingCollection publicKeyRings); @@ -210,7 +275,11 @@ public interface DecryptionBuilderInterface { * * @param publicKeyRing public key * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationCert(PGPPublicKeyRing)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated default HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRing publicKeyRing) { return verifyWith(Collections.singleton(publicKeyRing)); } @@ -220,11 +289,16 @@ public interface DecryptionBuilderInterface { * * @param publicKeyRings public keys * @return api handle + * + * @deprecated use {@link ConsumerOptions#addVerificationCert(PGPPublicKeyRing)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated HandleMissingPublicKeys verifyWith(@Nonnull Set<PGPPublicKeyRing> publicKeyRings); } + @Deprecated interface HandleMissingPublicKeys { /** @@ -232,17 +306,26 @@ public interface DecryptionBuilderInterface { * * @param callback callback * @return api handle + * + * @deprecated use {@link ConsumerOptions#setMissingCertificateCallback(MissingPublicKeyCallback)} + * ({@link DecryptWith#withOptions(ConsumerOptions)}) instead. */ + @Deprecated Build handleMissingPublicKeysWith(@Nonnull MissingPublicKeyCallback callback); /** * Instruct the {@link DecryptionStream} to ignore any missing public keys. * * @return api handle + * + * @deprecated simply do not set a {@link MissingPublicKeyCallback} and use + * {@link DecryptWith#withOptions(ConsumerOptions)} instead. */ + @Deprecated Build ignoreMissingPublicKeys(); } + @Deprecated interface Build { /** @@ -252,7 +335,10 @@ public interface DecryptionBuilderInterface { * @throws IOException in case of an I/O error * @throws PGPException if something is malformed * @throws org.pgpainless.exception.UnacceptableAlgorithmException if the message uses weak/unacceptable algorithms + * + * @deprecated use {@link DecryptWith#withOptions(ConsumerOptions)} instead. */ + @Deprecated DecryptionStream build() throws IOException, PGPException; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java index 685417fa..f863ccf9 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -19,17 +19,13 @@ import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPEncryptedData; @@ -46,7 +42,6 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; @@ -66,7 +61,6 @@ import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.info.KeyRingInfo; -import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.signature.DetachedSignature; import org.pgpainless.signature.OnePassSignature; @@ -92,7 +86,8 @@ public final class DecryptionStreamFactory { } public static DecryptionStream create(@Nonnull InputStream inputStream, - @Nonnull ConsumerOptions options) throws PGPException, IOException { + @Nonnull ConsumerOptions options) + throws PGPException, IOException { BufferedInputStream bufferedIn = new BufferedInputStream(inputStream); bufferedIn.mark(200); DecryptionStreamFactory factory = new DecryptionStreamFactory(options); From 88891e1337104796a5288de576cd4f124f04707f Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Wed, 16 Jun 2021 15:38:02 +0200 Subject: [PATCH 4/8] Remove usage of deprecated decryption/verification API in tests --- .../ConsumerOptions.java | 68 ++++++++++ .../pgpainless/signature/SignatureUtils.java | 52 +++++-- .../DecryptAndVerifyMessageTest.java | 14 +- .../DecryptHiddenRecipientMessage.java | 12 +- .../ModificationDetectionTests.java | 24 ++-- .../RecursionDepthTest.java | 8 +- ...eakSymmetricAlgorithmDuringDecryption.java | 38 +++--- .../VerifyWithMissingPublicKeyCallback.java | 11 +- .../EncryptDecryptTest.java | 53 ++++---- .../encryption_signing/FileInfoTest.java | 9 +- .../encryption_signing/LengthTest.java | 127 ------------------ ...ymmetricAlgorithmDuringEncryptionTest.java | 3 +- .../encryption_signing/SigningTest.java | 36 +++-- .../BindingSignatureSubpacketsTest.java | 67 ++++----- .../signature/IgnoreMarkerPackets.java | 28 ++-- .../signature/KeyRevocationTest.java | 5 +- .../SignatureChainValidatorTest.java | 15 ++- .../signature/SignatureStructureTest.java | 3 +- ...ultiPassphraseSymmetricEncryptionTest.java | 22 +-- .../SymmetricEncryptionTest.java | 32 ++--- ...ncryptCommsStorageFlagsDifferentiated.java | 13 +- .../weird_keys/TestTwoSubkeysEncryption.java | 2 +- .../org/pgpainless/sop/commands/Decrypt.java | 38 +++--- .../org/pgpainless/sop/commands/Verify.java | 52 +++---- 24 files changed, 342 insertions(+), 390 deletions(-) delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/encryption_signing/LengthTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java index f0ae17f1..86bc8176 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java @@ -15,19 +15,31 @@ */ package org.pgpainless.decryption_verification; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.bouncycastle.bcpg.MarkerPacket; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPUtil; +import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.util.Passphrase; @@ -105,6 +117,48 @@ public class ConsumerOptions { return this; } + public ConsumerOptions addVerificationOfDetachedSignatures(InputStream signatureInputStream) throws IOException, PGPException { + List<PGPSignature> signatures = new ArrayList<>(); + InputStream pgpIn = PGPUtil.getDecoderStream(signatureInputStream); + PGPObjectFactory objectFactory = new PGPObjectFactory( + pgpIn, ImplementationFactory.getInstance().getKeyFingerprintCalculator()); + + Object nextObject = objectFactory.nextObject(); + while (nextObject != null) { + if (nextObject instanceof MarkerPacket) { + nextObject = objectFactory.nextObject(); + continue; + } + if (nextObject instanceof PGPCompressedData) { + PGPCompressedData compressedData = (PGPCompressedData) nextObject; + objectFactory = new PGPObjectFactory(compressedData.getDataStream(), + ImplementationFactory.getInstance().getKeyFingerprintCalculator()); + nextObject = objectFactory.nextObject(); + continue; + } + if (nextObject instanceof PGPSignatureList) { + PGPSignatureList signatureList = (PGPSignatureList) nextObject; + for (PGPSignature s : signatureList) { + signatures.add(s); + } + } + if (nextObject instanceof PGPSignature) { + signatures.add((PGPSignature) nextObject); + } + nextObject = objectFactory.nextObject(); + } + pgpIn.close(); + + return addVerificationOfDetachedSignatures(signatures); + } + + public ConsumerOptions addVerificationOfDetachedSignatures(List<PGPSignature> detachedSignatures) { + for (PGPSignature signature : detachedSignatures) { + addVerificationOfDetachedSignature(signature); + } + return this; + } + /** * Add a detached signature for the signature verification process. * @@ -183,6 +237,20 @@ public class ConsumerOptions { return this; } + /** + * Add the keys in the provided key collection for message decryption. + * + * @param keys key collection + * @param keyRingProtector protector for encrypted secret keys + * @return options + */ + public ConsumerOptions addDecryptionKeys(@Nonnull PGPSecretKeyRingCollection keys, @Nonnull SecretKeyRingProtector keyRingProtector) { + for (PGPSecretKeyRing key : keys) { + addDecryptionKey(key, keyRingProtector); + } + return this; + } + /** * Add a passphrase for message decryption. * diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureUtils.java b/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureUtils.java index b8117fad..c1d418e1 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureUtils.java @@ -15,22 +15,27 @@ */ package org.pgpainless.signature; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.bouncycastle.bcpg.MarkerPacket; import org.bouncycastle.bcpg.sig.KeyExpirationTime; import org.bouncycastle.bcpg.sig.RevocationReason; import org.bouncycastle.bcpg.sig.SignatureExpirationTime; -import org.bouncycastle.openpgp.PGPMarker; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.util.encoders.Hex; import org.pgpainless.PGPainless; @@ -41,7 +46,6 @@ import org.pgpainless.key.util.OpenPgpKeyAttributeUtil; import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.policy.Policy; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -import org.pgpainless.util.BCUtil; /** * Utility methods related to signatures. @@ -183,18 +187,44 @@ public class SignatureUtils { * @return signature list * @throws IOException if the signatures cannot be read */ - public static PGPSignatureList readSignatures(String encodedSignatures) throws IOException { - InputStream inputStream = BCUtil.getPgpDecoderInputStream(encodedSignatures.getBytes(Charset.forName("UTF8"))); - PGPObjectFactory objectFactory = new PGPObjectFactory(inputStream, ImplementationFactory.getInstance().getKeyFingerprintCalculator()); - Object next = objectFactory.nextObject(); - while (next != null) { - if (next instanceof PGPMarker) { - next = objectFactory.nextObject(); + public static List<PGPSignature> readSignatures(String encodedSignatures) throws IOException, PGPException { + InputStream inputStream = new ByteArrayInputStream(encodedSignatures.getBytes(Charset.forName("UTF8"))); + return readSignatures(inputStream); + } + + public static List<PGPSignature> readSignatures(InputStream inputStream) throws IOException, PGPException { + List<PGPSignature> signatures = new ArrayList<>(); + InputStream pgpIn = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory objectFactory = new PGPObjectFactory( + pgpIn, ImplementationFactory.getInstance().getKeyFingerprintCalculator()); + + Object nextObject = objectFactory.nextObject(); + while (nextObject != null) { + if (nextObject instanceof MarkerPacket) { + nextObject = objectFactory.nextObject(); continue; } - return (PGPSignatureList) next; + if (nextObject instanceof PGPCompressedData) { + PGPCompressedData compressedData = (PGPCompressedData) nextObject; + objectFactory = new PGPObjectFactory(compressedData.getDataStream(), + ImplementationFactory.getInstance().getKeyFingerprintCalculator()); + nextObject = objectFactory.nextObject(); + continue; + } + if (nextObject instanceof PGPSignatureList) { + PGPSignatureList signatureList = (PGPSignatureList) nextObject; + for (PGPSignature s : signatureList) { + signatures.add(s); + } + } + if (nextObject instanceof PGPSignature) { + signatures.add((PGPSignature) nextObject); + } + nextObject = objectFactory.nextObject(); } - return null; + pgpIn.close(); + + return signatures; } public static String getSignatureDigestPrefix(PGPSignature signature) { diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java index aa33d45e..c47f095f 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptAndVerifyMessageTest.java @@ -23,12 +23,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Collections; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; @@ -38,7 +35,7 @@ import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.TestKeys; -import org.pgpainless.key.protection.UnprotectedKeysProtector; +import org.pgpainless.key.util.KeyRingUtils; public class DecryptAndVerifyMessageTest { @@ -60,12 +57,13 @@ public class DecryptAndVerifyMessageTest { ImplementationFactory.setFactoryImplementation(implementationFactory); String encryptedMessage = TestKeys.MSG_SIGN_CRYPT_JULIET_JULIET; + ConsumerOptions options = new ConsumerOptions() + .addDecryptionKey(juliet) + .addVerificationCert(KeyRingUtils.publicKeyRingFrom(juliet)); + DecryptionStream decryptor = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes())) - .decryptWith(new UnprotectedKeysProtector(), new PGPSecretKeyRingCollection(Collections.singleton(juliet))) - .verifyWith(Collections.singleton(new PGPPublicKeyRing(Collections.singletonList(juliet.getPublicKey())))) - .ignoreMissingPublicKeys() - .build(); + .withOptions(options); ByteArrayOutputStream toPlain = new ByteArrayOutputStream(); Streams.pipeAll(decryptor, toPlain); diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java index 4b388cba..7cc76f33 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java @@ -21,13 +21,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Collections; import java.util.Set; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -35,7 +33,6 @@ import org.pgpainless.PGPainless; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; -import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.util.selection.key.impl.EncryptionKeySelectionStrategy; @@ -144,11 +141,12 @@ public class DecryptHiddenRecipientMessage { "=1knQ\n" + "-----END PGP MESSAGE-----\n"; ByteArrayInputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); + ConsumerOptions options = new ConsumerOptions() + .addDecryptionKey(secretKeys); - DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(messageIn) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), new PGPSecretKeyRingCollection(Collections.singletonList(secretKeys))) - .doNotVerify() - .build(); + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(messageIn) + .withOptions(options); ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.pipeAll(decryptionStream, out); diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/ModificationDetectionTests.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/ModificationDetectionTests.java index a9b92750..0504aa23 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/ModificationDetectionTests.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/ModificationDetectionTests.java @@ -154,9 +154,9 @@ public class ModificationDetectionTests { InputStream in = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(in) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeyRings) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKeys(secretKeyRings, SecretKeyRingProtector.unprotectedKeys()) + ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); assertThrows(EOFException.class, () -> { @@ -187,9 +187,9 @@ public class ModificationDetectionTests { ByteArrayInputStream in = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(in) - .decryptWith(getDecryptionKey()) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKeys(getDecryptionKey(), SecretKeyRingProtector.unprotectedKeys()) + ); ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.pipeAll(decryptionStream, out); @@ -218,9 +218,9 @@ public class ModificationDetectionTests { ByteArrayInputStream in = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(in) - .decryptWith(getDecryptionKey()) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKeys(getDecryptionKey(), SecretKeyRingProtector.unprotectedKeys()) + ); ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.pipeAll(decryptionStream, out); @@ -387,9 +387,9 @@ public class ModificationDetectionTests { assertThrows(MessageNotIntegrityProtectedException.class, () -> PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertext.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unlockAllKeysWith(passphrase, secretKeyRing), new PGPSecretKeyRingCollection(Collections.singleton(secretKeyRing))) - .doNotVerify() - .build()); + .withOptions(new ConsumerOptions().addDecryptionKey(secretKeyRing, + SecretKeyRingProtector.unlockAllKeysWith(passphrase, secretKeyRing))) + ); } private PGPSecretKeyRingCollection getDecryptionKey() throws IOException, PGPException { diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RecursionDepthTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RecursionDepthTest.java index 74dfa22f..3ec55359 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RecursionDepthTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RecursionDepthTest.java @@ -21,17 +21,14 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Collections; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.key.protection.SecretKeyRingProtector; public class RecursionDepthTest { @@ -127,7 +124,6 @@ public class RecursionDepthTest { "=miES\n" + "-----END PGP PRIVATE KEY BLOCK-----\n"; PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key); - PGPSecretKeyRingCollection secretKeys = new PGPSecretKeyRingCollection(Collections.singletonList(secretKey)); // message contains compressed data that contains compressed data that contains... 64 times. String msg = "-----BEGIN PGP ARMORED FILE-----\n" + @@ -161,9 +157,7 @@ public class RecursionDepthTest { assertThrows(PGPException.class, () -> { DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions().addDecryptionKey(secretKey)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Streams.pipeAll(decryptionStream, outputStream); diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RejectWeakSymmetricAlgorithmDuringDecryption.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RejectWeakSymmetricAlgorithmDuringDecryption.java index 6db5eda0..46c43143 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RejectWeakSymmetricAlgorithmDuringDecryption.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/RejectWeakSymmetricAlgorithmDuringDecryption.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.bouncycastle.openpgp.PGPException; @@ -27,7 +28,6 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; import org.pgpainless.exception.UnacceptableAlgorithmException; -import org.pgpainless.key.protection.SecretKeyRingProtector; /** * Test PGPainless' default symmetric key algorithm policy for decryption of messages. @@ -146,11 +146,13 @@ public class RejectWeakSymmetricAlgorithmDuringDecryption { "=w0KS\n" + "-----END PGP MESSAGE-----\n"; + InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); + assertThrows(UnacceptableAlgorithmException.class, () -> - PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .doNotVerify() - .build()); + PGPainless.decryptAndOrVerify() + .onInputStream(messageIn) + .withOptions(new ConsumerOptions().addDecryptionKey(secretKeys)) + ); } @Test @@ -171,11 +173,14 @@ public class RejectWeakSymmetricAlgorithmDuringDecryption { "WLlG7ee7fRqQPTSP+OLh4Cm8zDIaCNowj0Ua4KwcWZDYERzg\n" + "=j71X\n" + "-----END PGP ARMORED FILE-----\n"; + + InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); + assertThrows(UnacceptableAlgorithmException.class, () -> - PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .doNotVerify() - .build()); + PGPainless.decryptAndOrVerify() + .onInputStream(messageIn) + .withOptions(new ConsumerOptions().addDecryptionKey(secretKeys)) + ); } @Test @@ -197,11 +202,11 @@ public class RejectWeakSymmetricAlgorithmDuringDecryption { "=qNxx\n" + "-----END PGP ARMORED FILE-----\n"; + InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); assertThrows(UnacceptableAlgorithmException.class, () -> - PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .doNotVerify() - .build()); + PGPainless.decryptAndOrVerify().onInputStream(messageIn) + .withOptions(new ConsumerOptions().addDecryptionKey(secretKeys)) + ); } // Control: In contrast, AES256 is acceptable @@ -222,11 +227,10 @@ public class RejectWeakSymmetricAlgorithmDuringDecryption { "4yjtOxfmmp9Fac50SS5i9dzBdnVNllLs+ADQt+LksJnzTW1IINGnIw8=\n" + "=kLfl\n" + "-----END PGP ARMORED FILE-----\n"; + InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); - PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .doNotVerify() - .build(); + PGPainless.decryptAndOrVerify().onInputStream(messageIn) + .withOptions(new ConsumerOptions().addDecryptionKey(secretKeys)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java index 514ced4d..5efb51dc 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/VerifyWithMissingPublicKeyCallback.java @@ -72,18 +72,17 @@ public class VerifyWithMissingPublicKeyCallback { DecryptionStream verificationStream = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(signOut.toByteArray())) - .doNotDecrypt() - .verifyWith(unrelatedKeys) - .handleMissingPublicKeysWith( - new MissingPublicKeyCallback() { + .withOptions(new ConsumerOptions() + .addVerificationCert(unrelatedKeys) + .setMissingCertificateCallback(new MissingPublicKeyCallback() { @Nullable @Override public PGPPublicKeyRing onMissingPublicKeyEncountered(@Nonnull Long keyId) { assertEquals(signingKey.getKeyID(), keyId, "Signing key-ID mismatch."); return signingPubKeys; } - } - ).build(); + })); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); Streams.pipeAll(verificationStream, plainOut); verificationStream.close(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java index a660778d..24151e57 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java @@ -27,9 +27,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import java.util.Collections; import java.util.Set; -import java.util.logging.Logger; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPException; @@ -44,6 +42,7 @@ import org.pgpainless.PGPainless; import org.pgpainless.algorithm.DocumentSignatureType; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.implementation.ImplementationFactory; @@ -61,17 +60,16 @@ import org.pgpainless.util.ArmoredOutputStreamFactory; public class EncryptDecryptTest { - private static final Logger LOGGER = Logger.getLogger(EncryptDecryptTest.class.getName()); // Don't use StandardCharsets.UTF_8 because of Android API level. private static final Charset UTF8 = Charset.forName("UTF-8"); private static final String testMessage = "Ah, Juliet, if the measure of thy joy\n" + - "Be heaped like mine, and that thy skill be more\n" + - "To blazon it, then sweeten with thy breath\n" + - "This neighbor air, and let rich music’s tongue\n" + - "Unfold the imagined happiness that both\n" + - "Receive in either by this dear encounter."; + "Be heaped like mine, and that thy skill be more\n" + + "To blazon it, then sweeten with thy breath\n" + + "This neighbor air, and let rich music’s tongue\n" + + "Unfold the imagined happiness that both\n" + + "Receive in either by this dear encounter."; @ParameterizedTest @MethodSource("org.pgpainless.util.TestUtil#provideImplementationFactories") @@ -178,10 +176,10 @@ public class EncryptDecryptTest { ByteArrayInputStream envelopeIn = new ByteArrayInputStream(encryptedSecretMessage); DecryptionStream decryptor = PGPainless.decryptAndOrVerify() .onInputStream(envelopeIn) - .decryptWith(keyDecryptor, KeyRingUtils.keyRingsToKeyRingCollection(recipientSec)) - .verifyWith(KeyRingUtils.keyRingsToKeyRingCollection(senderPub)) - .ignoreMissingPublicKeys() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKey(recipientSec, keyDecryptor) + .addVerificationCert(senderPub) + ); ByteArrayOutputStream decryptedSecretMessage = new ByteArrayOutputStream(); @@ -227,12 +225,13 @@ public class EncryptDecryptTest { // CHECKSTYLE:ON inputStream = new ByteArrayInputStream(testMessage.getBytes()); - DecryptionStream verifier = PGPainless.decryptAndOrVerify().onInputStream(inputStream) - .doNotDecrypt() - .verifyDetachedSignature(new ByteArrayInputStream(armorSig.getBytes())) - .verifyWith(Collections.singleton(KeyRingUtils.publicKeyRingFrom(signingKeys))) - .ignoreMissingPublicKeys() - .build(); + DecryptionStream verifier = PGPainless.decryptAndOrVerify() + .onInputStream(inputStream) + .withOptions(new ConsumerOptions() + .addVerificationOfDetachedSignatures(new ByteArrayInputStream(armorSig.getBytes())) + .addVerificationCert(KeyRingUtils.publicKeyRingFrom(signingKeys)) + ); + dummyOut = new ByteArrayOutputStream(); Streams.pipeAll(verifier, dummyOut); verifier.close(); @@ -257,16 +256,12 @@ public class EncryptDecryptTest { Streams.pipeAll(inputStream, signer); signer.close(); - // CHECKSTYLE:OFF - System.out.println(signOut.toString()); - // CHECKSTYLE:ON - inputStream = new ByteArrayInputStream(signOut.toByteArray()); - DecryptionStream verifier = PGPainless.decryptAndOrVerify().onInputStream(inputStream) - .doNotDecrypt() - .verifyWith(Collections.singleton(KeyRingUtils.publicKeyRingFrom(signingKeys))) - .ignoreMissingPublicKeys() - .build(); + DecryptionStream verifier = PGPainless.decryptAndOrVerify() + .onInputStream(inputStream) + .withOptions(new ConsumerOptions() + .addVerificationCert(KeyRingUtils.publicKeyRingFrom(signingKeys)) + ); signOut = new ByteArrayOutputStream(); Streams.pipeAll(verifier, signOut); verifier.close(); @@ -335,7 +330,7 @@ public class EncryptDecryptTest { PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); assertThrows(IllegalArgumentException.class, () -> - PGPainless.encryptAndOrSign().onOutputStream(outputStream) - .toRecipient(publicKeys)); + PGPainless.encryptAndOrSign().onOutputStream(outputStream) + .toRecipient(publicKeys)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/FileInfoTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/FileInfoTest.java index 97fdac81..fbf2dc62 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/FileInfoTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/FileInfoTest.java @@ -23,20 +23,18 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import java.util.Collections; import java.util.Date; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.StreamEncoding; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; -import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; public class FileInfoTest { @@ -85,9 +83,8 @@ public class FileInfoTest { DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(cryptIn) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), new PGPSecretKeyRingCollection(Collections.singleton(secretKeys))) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKey(secretKeys)); Streams.pipeAll(decryptionStream, plainOut); decryptionStream.close(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/LengthTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/LengthTest.java deleted file mode 100644 index 63862e61..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/LengthTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.encryption_signing; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.Random; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.util.io.Streams; -import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.DocumentSignatureType; -import org.pgpainless.key.TestKeys; -import org.pgpainless.key.generation.type.rsa.RsaLength; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.key.protection.UnprotectedKeysProtector; -import org.pgpainless.key.util.KeyRingUtils; - -/** - * Class used to determine the length of cipher-text depending on used algorithms. - */ -public class LengthTest { - - private static final Logger LOGGER = Logger.getLogger(LengthTest.class.getName()); - - // @Test - public void ecEc() - throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, - IOException { - LOGGER.log(Level.FINER, "\nEC -> EC"); - PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("simplejid@server.tld"); - PGPSecretKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("otherjid@other.srv"); - encryptDecryptForSecretKeyRings(sender, recipient); - } - - - // @Test - public void RsaRsa() - throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, - IOException { - LOGGER.log(Level.FINER, "\nRSA-2048 -> RSA-2048"); - PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("simplejid@server.tld", RsaLength._2048); - PGPSecretKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("otherjid@other.srv", RsaLength._2048); - encryptDecryptForSecretKeyRings(sender, recipient); - } - - // @Test - public void RsaRsa4096() - throws PGPException, - IOException { - LOGGER.log(Level.FINER, "\nRSA-4096 -> RSA-4096"); - PGPSecretKeyRing sender = PGPainless.readKeyRing().secretKeyRing(TestKeys.JULIET_SEC); - PGPSecretKeyRing recipient = PGPainless.readKeyRing().secretKeyRing(TestKeys.ROMEO_SEC); - encryptDecryptForSecretKeyRings(sender, recipient); - } - - // @Test - public void rsaEc() throws PGPException, IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, - NoSuchProviderException { - LOGGER.log(Level.FINER, "\nRSA-2048 -> EC"); - PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("simplejid@server.tld", RsaLength._2048); - PGPSecretKeyRing recipient = PGPainless.generateKeyRing().simpleEcKeyRing("otherjid@other.srv"); - encryptDecryptForSecretKeyRings(sender, recipient); - } - - // @Test - public void ecRsa() - throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, - IOException { - LOGGER.log(Level.FINER, "\nEC -> RSA-2048"); - PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleEcKeyRing("simplejid@server.tld"); - @SuppressWarnings("deprecation") - PGPSecretKeyRing recipient = PGPainless.generateKeyRing().simpleRsaKeyRing("otherjid@other.srv", RsaLength._2048); - encryptDecryptForSecretKeyRings(sender, recipient); - } - - private void encryptDecryptForSecretKeyRings(PGPSecretKeyRing senderSec, PGPSecretKeyRing recipientSec) - throws PGPException, - IOException { - PGPPublicKeyRing recipientPub = KeyRingUtils.publicKeyRingFrom(recipientSec); - PGPPublicKeyRing senderPub = KeyRingUtils.publicKeyRingFrom(senderSec); - - SecretKeyRingProtector keyDecryptor = new UnprotectedKeysProtector(); - - for (int i = 1; i <= 100; i++) { - byte[] secretMessage = new byte[i * 20]; - new Random().nextBytes(secretMessage); - - ByteArrayOutputStream envelope = new ByteArrayOutputStream(); - - OutputStream encryptor = PGPainless.encryptAndOrSign() - .onOutputStream(envelope) - .toRecipient(recipientPub) - .and() - .signInlineWith(keyDecryptor, senderSec, "simplejid@server.tld", DocumentSignatureType.BINARY_DOCUMENT) - .noArmor(); - - Streams.pipeAll(new ByteArrayInputStream(secretMessage), encryptor); - encryptor.close(); - byte[] encryptedSecretMessage = envelope.toByteArray(); - - LOGGER.log(Level.FINER,"\n" + encryptedSecretMessage.length); - } - } -} diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/RespectPreferredSymmetricAlgorithmDuringEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/RespectPreferredSymmetricAlgorithmDuringEncryptionTest.java index b63e8332..7b18b2be 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/RespectPreferredSymmetricAlgorithmDuringEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/RespectPreferredSymmetricAlgorithmDuringEncryptionTest.java @@ -89,8 +89,7 @@ public class RespectPreferredSymmetricAlgorithmDuringEncryptionTest { ByteArrayOutputStream out = new ByteArrayOutputStream(); EncryptionStream encryptionStream = PGPainless.encryptAndOrSign().onOutputStream(out) .withOptions( - ProducerOptions.encrypt( - new EncryptionOptions() + ProducerOptions.encrypt(new EncryptionOptions() .addRecipient(publicKeys) )); diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java index 49223c71..f04b2c11 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java @@ -26,8 +26,6 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; @@ -41,12 +39,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.DocumentSignatureType; -import org.pgpainless.algorithm.EncryptionPurpose; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.exception.KeyValidationException; import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.TestKeys; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; @@ -71,15 +68,16 @@ public class SigningTest { PGPPublicKeyRingCollection keys = new PGPPublicKeyRingCollection(Arrays.asList(julietKeys, romeoKeys)); ByteArrayOutputStream out = new ByteArrayOutputStream(); - EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionPurpose.STORAGE) + EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() .onOutputStream(out) - .toRecipients(keys) - .and() - .toRecipient(KeyRingUtils.publicKeyRingFrom(cryptieKeys)) - .and() - .signInlineWith(SecretKeyRingProtector.unlockSingleKeyWith(TestKeys.CRYPTIE_PASSPHRASE, cryptieSigningKey), - cryptieKeys, TestKeys.CRYPTIE_UID, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT) - .asciiArmor(); + .withOptions(ProducerOptions.signAndEncrypt( + EncryptionOptions.encryptDataAtRest() + .addRecipients(keys) + .addRecipient(KeyRingUtils.publicKeyRingFrom(cryptieKeys)), + new SigningOptions() + .addInlineSignature(SecretKeyRingProtector.unlockSingleKeyWith(TestKeys.CRYPTIE_PASSPHRASE, cryptieSigningKey), + cryptieKeys, TestKeys.CRYPTIE_UID, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT) + ).setAsciiArmor(true)); byte[] messageBytes = "This message is signed and encrypted to Romeo and Juliet.".getBytes(StandardCharsets.UTF_8); ByteArrayInputStream message = new ByteArrayInputStream(messageBytes); @@ -94,16 +92,14 @@ public class SigningTest { PGPSecretKeyRing julietSecret = TestKeys.getJulietSecretKeyRing(); PGPSecretKeyRingCollection secretKeys = new PGPSecretKeyRingCollection(Arrays.asList(romeoSecret, julietSecret)); - Set<OpenPgpV4Fingerprint> trustedFingerprints = new HashSet<>(); - trustedFingerprints.add(new OpenPgpV4Fingerprint(cryptieKeys)); - trustedFingerprints.add(new OpenPgpV4Fingerprint(julietKeys)); PGPPublicKeyRingCollection verificationKeys = new PGPPublicKeyRingCollection(Arrays.asList(KeyRingUtils.publicKeyRingFrom(cryptieKeys), romeoKeys)); - DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(cryptIn) - .decryptWith(secretKeys) - .verifyWith(trustedFingerprints, verificationKeys) - .ignoreMissingPublicKeys() - .build(); + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(cryptIn) + .withOptions(new ConsumerOptions() + .addDecryptionKeys(secretKeys, SecretKeyRingProtector.unprotectedKeys()) + .addVerificationCerts(verificationKeys) + ); ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java index 924b2c53..bb3db213 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/BindingSignatureSubpacketsTest.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.junit.jupiter.api.Test; @@ -59,7 +60,7 @@ public class BindingSignatureSubpacketsTest { private Policy policy = PGPainless.getPolicy(); @Test - public void baseCase() throws IOException { + public void baseCase() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -118,7 +119,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingIssuerFpOnly() throws IOException { + public void subkeyBindingIssuerFpOnly() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -177,7 +178,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingIssuerV6IssuerFp() throws IOException { + public void subkeyBindingIssuerV6IssuerFp() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -236,7 +237,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingIssuerFakeIssuer() throws IOException { + public void subkeyBindingIssuerFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -295,7 +296,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingFakeIssuerIssuer() throws IOException { + public void subkeyBindingFakeIssuerIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -354,7 +355,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingFakeIssuer() throws IOException { + public void subkeyBindingFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -413,7 +414,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingNoIssuer() throws IOException { + public void subkeyBindingNoIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -471,7 +472,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void unknownSubpacketHashed() throws IOException { + public void unknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -530,7 +531,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingUnknownCriticalSubpacket() throws IOException { + public void subkeyBindingUnknownCriticalSubpacket() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -589,7 +590,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingUnknownSubpacketUnhashed() throws IOException { + public void subkeyBindingUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -648,7 +649,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException { + public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -707,7 +708,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingUnknownNotationHashed() throws IOException { + public void subkeyBindingUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -767,7 +768,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingCriticalUnknownNotationHashed() throws IOException { + public void subkeyBindingCriticalUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -827,7 +828,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingUnknownNotationUnhashed() throws IOException { + public void subkeyBindingUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -887,7 +888,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException { + public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -947,7 +948,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingBackSigFakeBackSig() throws IOException { + public void subkeyBindingBackSigFakeBackSig() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1017,7 +1018,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void subkeyBindingFakeBackSigBackSig() throws IOException { + public void subkeyBindingFakeBackSigBackSig() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1087,7 +1088,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingIssuerFpOnly() throws IOException { + public void primaryBindingIssuerFpOnly() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1146,7 +1147,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingIssuerV6IssuerFp() throws IOException { + public void primaryBindingIssuerV6IssuerFp() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1205,7 +1206,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingIssuerFakeIssuer() throws IOException { + public void primaryBindingIssuerFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1264,7 +1265,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingFakeIssuerIssuer() throws IOException { + public void primaryBindingFakeIssuerIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1323,7 +1324,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingFakeIssuer() throws IOException { + public void primaryBindingFakeIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1382,7 +1383,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingNoIssuer() throws IOException { + public void primaryBindingNoIssuer() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1440,7 +1441,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingUnknownSubpacketHashed() throws IOException { + public void primaryBindingUnknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1499,7 +1500,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException { + public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1558,7 +1559,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingUnknownSubpacketUnhashed() throws IOException { + public void primaryBindingUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1617,7 +1618,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException { + public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1676,7 +1677,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingUnknownNotationHashed() throws IOException { + public void primaryBindingUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1736,7 +1737,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingCriticalUnknownNotationHashed() throws IOException { + public void primaryBindingCriticalUnknownNotationHashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1796,7 +1797,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingUnknownNotationUnhashed() throws IOException { + public void primaryBindingUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1856,7 +1857,7 @@ public class BindingSignatureSubpacketsTest { } @Test - public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException { + public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException { String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + @@ -1915,7 +1916,7 @@ public class BindingSignatureSubpacketsTest { expectSignatureValidationSucceeds(key, "Critical unknown notation is acceptable in unhashed area of primary key binding sig."); } - private void expectSignatureValidationSucceeds(String key, String message) throws IOException { + private void expectSignatureValidationSucceeds(String key, String message) throws IOException, PGPException { PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); @@ -1929,7 +1930,7 @@ public class BindingSignatureSubpacketsTest { } } - private void expectSignatureValidationFails(String key, String message) throws IOException { + private void expectSignatureValidationFails(String key, String message) throws IOException, PGPException { PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key); PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java b/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java index 4806f1fe..5c6846e2 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/IgnoreMarkerPackets.java @@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.bouncycastle.openpgp.PGPException; @@ -32,10 +33,10 @@ import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.key.OpenPgpV4Fingerprint; -import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; /** @@ -151,12 +152,13 @@ public class IgnoreMarkerPackets { String data = "Marker + Detached signature"; PGPSignature signature = SignatureUtils.readSignatures(sig).get(0); - DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))) - .doNotDecrypt() - .verifyDetachedSignature(signature) - .verifyWith(publicKeys) - .ignoreMissingPublicKeys() - .build(); + InputStream messageIn = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(messageIn) + .withOptions(new ConsumerOptions() + .addVerificationCert(publicKeys) + .addVerificationOfDetachedSignature(signature) + ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -199,11 +201,13 @@ public class IgnoreMarkerPackets { PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); String data = "Marker + Encrypted Message"; - DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8))) - .decryptWith(SecretKeyRingProtector.unprotectedKeys(), secretKeys) - .verifyWith(publicKeys) - .ignoreMissingPublicKeys() - .build(); + InputStream messageIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8)); + DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(messageIn) + .withOptions(new ConsumerOptions() + .addDecryptionKey(secretKeys) + .addVerificationCert(publicKeys) + ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java index a3bc1f1f..a70c5921 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/KeyRevocationTest.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Date; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.junit.jupiter.api.Test; @@ -33,7 +34,7 @@ public class KeyRevocationTest { private static final String data = "Hello, World"; @Test - public void subkeySignsPrimaryKeyRevokedNoReason() throws IOException, SignatureValidationException { + public void subkeySignsPrimaryKeyRevokedNoReason() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -178,7 +179,7 @@ public class KeyRevocationTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__subkey_signs__primary_key_is_not_revoked__base_case_">Sequoia Test-Suite</a> */ @Test - public void subkeySignsPrimaryKeyNotRevoked() throws IOException, SignatureValidationException { + public void subkeySignsPrimaryKeyNotRevoked() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureChainValidatorTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureChainValidatorTest.java index a527e154..849f202a 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureChainValidatorTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureChainValidatorTest.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Date; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.junit.jupiter.api.Test; @@ -39,7 +40,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__primary_key_signs_and_is_revoked__revoked__unknown">Sequoia Test Suite</a> */ @Test - public void testPrimaryKeySignsAndIsHardRevokedUnknown() throws IOException { + public void testPrimaryKeySignsAndIsHardRevokedUnknown() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -188,7 +189,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__subkey_signs__primary_key_is_revoked__revoked__unknown">Sequoia Test Suite</a> */ @Test - public void testSubkeySignsPrimaryKeyIsHardRevokedUnknown() throws IOException { + public void testSubkeySignsPrimaryKeyIsHardRevokedUnknown() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -338,7 +339,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__subkey_signs__subkey_is_revoked__revoked__unknown">Sequoia Test Suite</a> */ @Test - public void testSubkeySignsAndIsHardRevokedUnknown() throws IOException { + public void testSubkeySignsAndIsHardRevokedUnknown() throws IOException, PGPException { String keyWithHardRev = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -488,7 +489,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__primary_key_signs_and_is_revoked__revoked__superseded">Sequoia Test Suite</a> */ @Test - public void testPrimaryKeySignsAndIsSoftRevokedSuperseded() throws IOException { + public void testPrimaryKeySignsAndIsSoftRevokedSuperseded() throws IOException, PGPException { String keyWithSoftRev = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -643,7 +644,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__subkey_signs__primary_key_is_revoked__revoked__superseded">Sequoia Test Suite</a> */ @Test - public void testSubkeySignsPrimaryKeyIsSoftRevokedSuperseded() throws IOException { + public void testSubkeySignsPrimaryKeyIsSoftRevokedSuperseded() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -794,7 +795,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Key_revocation_test__primary_key_signs_and_is_revoked__revoked__key_retired">Sequoia Test Suite</a> */ @Test - public void testPrimaryKeySignsAndIsSoftRevokedRetired() throws IOException { + public void testPrimaryKeySignsAndIsSoftRevokedRetired() throws IOException, PGPException { String key = "-----BEGIN PGP ARMORED FILE-----\n" + "Comment: ASCII Armor added by openpgp-interoperability-test-suite\n" + "\n" + @@ -945,7 +946,7 @@ public class SignatureChainValidatorTest { * @see <a href="https://tests.sequoia-pgp.org/#Temporary_validity">Sequoia Test Suite</a> */ @Test - public void testTemporaryValidity() throws IOException { + public void testTemporaryValidity() throws IOException, PGPException { String keyA = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330\n" + "Comment: Bob Babbage <bob@openpgp.example>\n" + diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureStructureTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureStructureTest.java index 610a3e72..e25cd682 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureStructureTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureStructureTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.List; import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.BeforeAll; @@ -38,7 +39,7 @@ public class SignatureStructureTest { private static PGPSignature signature; @BeforeAll - public static void parseSignature() throws IOException { + public static void parseSignature() throws IOException, PGPException { // see https://tests.sequoia-pgp.org/#Detached_signature_with_Subpackets (base case) signature = SignatureUtils.readSignatures("-----BEGIN PGP SIGNATURE-----\n" + "\n" + diff --git a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/MultiPassphraseSymmetricEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/MultiPassphraseSymmetricEncryptionTest.java index 50c3662a..8eb19130 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/MultiPassphraseSymmetricEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/MultiPassphraseSymmetricEncryptionTest.java @@ -26,8 +26,11 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionStream; +import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.util.Passphrase; @@ -44,12 +47,11 @@ public class MultiPassphraseSymmetricEncryptionTest { ByteArrayOutputStream ciphertextOut = new ByteArrayOutputStream(); EncryptionStream encryptor = PGPainless.encryptAndOrSign() .onOutputStream(ciphertextOut) - .forPassphrase(Passphrase.fromPassword("p1")) - .and() - .forPassphrase(Passphrase.fromPassword("p2")) - .and() - .doNotSign() - .noArmor(); + .withOptions(ProducerOptions.encrypt( + EncryptionOptions.encryptCommunications() + .addPassphrase(Passphrase.fromPassword("p1")) + .addPassphrase(Passphrase.fromPassword("p2")) + ).setAsciiArmor(false)); Streams.pipeAll(plaintextIn, encryptor); encryptor.close(); @@ -58,10 +60,10 @@ public class MultiPassphraseSymmetricEncryptionTest { // decrypting the p1 package with p2 first will not work. Test if it is handled correctly. for (Passphrase passphrase : new Passphrase[] {Passphrase.fromPassword("p2"), Passphrase.fromPassword("p1")}) { - DecryptionStream decryptor = PGPainless.decryptAndOrVerify().onInputStream(new ByteArrayInputStream(ciphertext)) - .decryptWith(passphrase) - .doNotVerify() - .build(); + DecryptionStream decryptor = PGPainless.decryptAndOrVerify() + .onInputStream(new ByteArrayInputStream(ciphertext)) + .withOptions(new ConsumerOptions() + .addDecryptionPassphrase(passphrase)); ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java index 9c7e6f8b..b4bc4e72 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java @@ -31,8 +31,8 @@ import org.bouncycastle.util.io.Streams; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; -import org.pgpainless.encryption_signing.EncryptionBuilderInterface; import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.ProducerOptions; @@ -60,14 +60,13 @@ public class SymmetricEncryptionTest { Passphrase encryptionPassphrase = Passphrase.fromPassword("greenBeans"); ByteArrayOutputStream ciphertextOut = new ByteArrayOutputStream(); - EncryptionBuilderInterface.Armor armor = PGPainless.encryptAndOrSign().onOutputStream(ciphertextOut) - .forPassphrase(encryptionPassphrase) - .and() - .toRecipient(encryptionKey) - .and() - .doNotSign(); - EncryptionStream encryptor = armor - .noArmor(); + EncryptionStream encryptor = PGPainless.encryptAndOrSign() + .onOutputStream(ciphertextOut) + .withOptions(ProducerOptions.encrypt( + EncryptionOptions.encryptCommunications() + .addPassphrase(encryptionPassphrase) + .addRecipient(encryptionKey) + )); Streams.pipeAll(plaintextIn, encryptor); encryptor.close(); @@ -77,9 +76,8 @@ public class SymmetricEncryptionTest { // Test symmetric decryption DecryptionStream decryptor = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertext)) - .decryptWith(encryptionPassphrase) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionPassphrase(encryptionPassphrase)); ByteArrayOutputStream decrypted = new ByteArrayOutputStream(); @@ -95,9 +93,8 @@ public class SymmetricEncryptionTest { new SolitaryPassphraseProvider(Passphrase.fromPassword(TestKeys.CRYPTIE_PASSWORD))); decryptor = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertext)) - .decryptWith(protector, decryptionKeys) - .doNotVerify() - .build(); + .withOptions(new ConsumerOptions() + .addDecryptionKeys(decryptionKeys, protector)); decrypted = new ByteArrayOutputStream(); @@ -126,8 +123,7 @@ public class SymmetricEncryptionTest { assertThrows(MissingDecryptionMethodException.class, () -> PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertextOut.toByteArray())) - .decryptWith(Passphrase.fromPassword("meldir")) - .doNotVerify() - .build()); + .withOptions(new ConsumerOptions() + .addDecryptionPassphrase(Passphrase.fromPassword("meldir")))); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java index 2728933e..772995cb 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java +++ b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java @@ -17,7 +17,6 @@ package org.pgpainless.weird_keys; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -27,9 +26,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.encryption_signing.EncryptionBuilderInterface; +import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.rsa.RsaLength; @@ -49,13 +47,10 @@ public class TestEncryptCommsStorageFlagsDifferentiated { .withPrimaryUserId("cannot@encrypt.comms") .withoutPassphrase() .build(); + PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - EncryptionBuilderInterface.ToRecipients builder = PGPainless.encryptAndOrSign(EncryptionPurpose.COMMUNICATIONS) - .onOutputStream(out); - - // since the key does not carry the flag ENCRYPT_COMMS, it cannot be used by the stream. - assertThrows(IllegalArgumentException.class, () -> builder.toRecipient(publicKeys)); + assertThrows(IllegalArgumentException.class, () -> EncryptionOptions.encryptCommunications() + .addRecipient(publicKeys)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestTwoSubkeysEncryption.java b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestTwoSubkeysEncryption.java index 766afdee..d1f8f181 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestTwoSubkeysEncryption.java +++ b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestTwoSubkeysEncryption.java @@ -59,7 +59,7 @@ public class TestTwoSubkeysEncryption { PGPSecretKeyRing twoSuitableSubkeysKeyRing = WeirdKeys.getTwoCryptSubkeysKey(); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(twoSuitableSubkeysKeyRing); ByteArrayOutputStream out = new ByteArrayOutputStream(); - EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionPurpose.STORAGE) + EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() .onOutputStream(out) .withOptions( ProducerOptions.encrypt(new EncryptionOptions(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS) diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java index 4d14e671..de3f57e9 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java @@ -24,17 +24,15 @@ import java.io.IOException; import java.io.PrintStream; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.HashSet; import java.util.List; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.util.io.Streams; import org.pgpainless.PGPainless; -import org.pgpainless.decryption_verification.DecryptionBuilderInterface; +import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.key.OpenPgpV4Fingerprint; @@ -108,34 +106,34 @@ public class Decrypt implements Runnable { System.exit(1); } - PGPSecretKeyRingCollection secretKeys; - List<PGPPublicKeyRing> verifyWith = null; + ConsumerOptions options = new ConsumerOptions(); + List<PGPPublicKeyRing> verifyWith = null; try { + List<PGPSecretKeyRing> secretKeyRings = loadKeysFromFiles(keys); - secretKeys = new PGPSecretKeyRingCollection(secretKeyRings); + for (PGPSecretKeyRing secretKey : secretKeyRings) { + options.addDecryptionKey(secretKey); + } + if (certs != null) { verifyWith = SopKeyUtil.loadCertificatesFromFile(certs); + for (PGPPublicKeyRing cert : verifyWith) { + options.addVerificationCert(cert); + } } + } catch (IOException | PGPException e) { err_ln(e.getMessage()); System.exit(1); return; } - - DecryptionBuilderInterface.Verify builder = PGPainless.decryptAndOrVerify() - .onInputStream(System.in) - .decryptWith(secretKeys); - DecryptionStream decryptionStream = null; + DecryptionStream decryptionStream; try { - if (verifyWith != null) { - decryptionStream = builder.verifyWith(new HashSet<>(verifyWith)) - .ignoreMissingPublicKeys().build(); - } else { - decryptionStream = builder.doNotVerify() - .build(); - } + decryptionStream = PGPainless.decryptAndOrVerify() + .onInputStream(System.in) + .withOptions(options); } catch (IOException | PGPException e) { err_ln("Error constructing decryption stream: " + e.getMessage()); System.exit(1); @@ -169,14 +167,14 @@ public class Decrypt implements Runnable { PGPSignature signature = metadata.getVerifiedSignatures().get(fingerprint); sb.append(df.format(signature.getCreationTime())).append(' ') .append(fingerprint).append(' ') - .append(new OpenPgpV4Fingerprint(verifier)).append('\n'); + .append(verifier != null ? new OpenPgpV4Fingerprint(verifier) : "null").append('\n'); } try { verifyOut.createNewFile(); PrintStream verifyPrinter = new PrintStream(new FileOutputStream(verifyOut)); // CHECKSTYLE:OFF - verifyPrinter.println(sb.toString()); + verifyPrinter.println(sb); // CHECKSTYLE:ON verifyPrinter.close(); } catch (IOException e) { diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java index 0456fb47..52581709 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java @@ -15,20 +15,11 @@ */ package org.pgpainless.sop.commands; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.util.io.Streams; -import org.pgpainless.PGPainless; -import org.pgpainless.decryption_verification.DecryptionStream; -import org.pgpainless.decryption_verification.OpenPgpMetadata; -import org.pgpainless.key.OpenPgpV4Fingerprint; -import picocli.CommandLine; +import static org.pgpainless.sop.Print.err_ln; +import static org.pgpainless.sop.Print.print_ln; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.text.DateFormat; @@ -36,12 +27,20 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.TimeZone; -import static org.pgpainless.sop.Print.err_ln; -import static org.pgpainless.sop.Print.print_ln; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.io.Streams; +import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.ConsumerOptions; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.OpenPgpMetadata; +import org.pgpainless.key.OpenPgpV4Fingerprint; +import picocli.CommandLine; @CommandLine.Command(name = "verify", description = "Verify a detached signature over the data from standard input", @@ -89,32 +88,35 @@ public class Verify implements Runnable { Date notBeforeDate = parseNotBefore(); Date notAfterDate = parseNotAfter(); + ConsumerOptions options = new ConsumerOptions(); + try (FileInputStream sigIn = new FileInputStream(signature)) { + options.addVerificationOfDetachedSignatures(sigIn); + } catch (IOException | PGPException e) { + err_ln("Cannot read detached signature: " + e.getMessage()); + System.exit(1); + } + Map<PGPPublicKeyRing, File> publicKeys = readCertificatesFromFiles(); if (publicKeys.isEmpty()) { err_ln("No certificates supplied."); System.exit(19); } + for (PGPPublicKeyRing cert : publicKeys.keySet()) { + options.addVerificationCert(cert); + } + OpenPgpMetadata metadata; - try (FileInputStream sigIn = new FileInputStream(signature)) { + try { DecryptionStream verifier = PGPainless.decryptAndOrVerify() .onInputStream(System.in) - .doNotDecrypt() - .verifyDetachedSignature(sigIn) - .verifyWith(new HashSet<>(publicKeys.keySet())) - .ignoreMissingPublicKeys() - .build(); + .withOptions(options); OutputStream out = new NullOutputStream(); Streams.pipeAll(verifier, out); verifier.close(); metadata = verifier.getResult(); - } catch (FileNotFoundException e) { - err_ln("Signature file not found:"); - err_ln(e.getMessage()); - System.exit(1); - return; } catch (IOException | PGPException e) { err_ln("Signature validation failed."); err_ln(e.getMessage()); From df22c2a102b4b7c234fa339b015ce3c007288d0c Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Wed, 16 Jun 2021 15:49:43 +0200 Subject: [PATCH 5/8] Remove duplicate code and throw NotYetImplementedExceptions where sensible --- .../ConsumerOptions.java | 58 +++++-------------- .../exception/NotYetImplementedException.java | 23 ++++++++ 2 files changed, 39 insertions(+), 42 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/exception/NotYetImplementedException.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java index 86bc8176..5cbf2ce7 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java @@ -17,7 +17,6 @@ package org.pgpainless.decryption_verification; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -28,19 +27,15 @@ import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bouncycastle.bcpg.MarkerPacket; -import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureList; -import org.bouncycastle.openpgp.PGPUtil; -import org.pgpainless.implementation.ImplementationFactory; +import org.pgpainless.exception.NotYetImplementedException; import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.signature.SignatureUtils; import org.pgpainless.util.Passphrase; /** @@ -66,12 +61,16 @@ public class ConsumerOptions { /** * Consider signatures made before the given timestamp invalid. * + * Note: This method does not have any effect yet. + * TODO: Add support for custom signature validity date ranges + * * @param timestamp timestamp * @return options */ public ConsumerOptions verifyNotBefore(Date timestamp) { this.verifyNotBefore = timestamp; - return this; + throw new NotYetImplementedException(); + // return this; } public Date getVerifyNotBefore() { @@ -81,12 +80,16 @@ public class ConsumerOptions { /** * Consider signatures made after the given timestamp invalid. * + * Note: This method does not have any effect yet. + * TODO: Add support for custom signature validity date ranges + * * @param timestamp timestamp * @return options */ public ConsumerOptions verifyNotAfter(Date timestamp) { this.verifyNotAfter = timestamp; - return this; + throw new NotYetImplementedException(); + // return this; } public Date getVerifyNotAfter() { @@ -118,37 +121,7 @@ public class ConsumerOptions { } public ConsumerOptions addVerificationOfDetachedSignatures(InputStream signatureInputStream) throws IOException, PGPException { - List<PGPSignature> signatures = new ArrayList<>(); - InputStream pgpIn = PGPUtil.getDecoderStream(signatureInputStream); - PGPObjectFactory objectFactory = new PGPObjectFactory( - pgpIn, ImplementationFactory.getInstance().getKeyFingerprintCalculator()); - - Object nextObject = objectFactory.nextObject(); - while (nextObject != null) { - if (nextObject instanceof MarkerPacket) { - nextObject = objectFactory.nextObject(); - continue; - } - if (nextObject instanceof PGPCompressedData) { - PGPCompressedData compressedData = (PGPCompressedData) nextObject; - objectFactory = new PGPObjectFactory(compressedData.getDataStream(), - ImplementationFactory.getInstance().getKeyFingerprintCalculator()); - nextObject = objectFactory.nextObject(); - continue; - } - if (nextObject instanceof PGPSignatureList) { - PGPSignatureList signatureList = (PGPSignatureList) nextObject; - for (PGPSignature s : signatureList) { - signatures.add(s); - } - } - if (nextObject instanceof PGPSignature) { - signatures.add((PGPSignature) nextObject); - } - nextObject = objectFactory.nextObject(); - } - pgpIn.close(); - + List<PGPSignature> signatures = SignatureUtils.readSignatures(signatureInputStream); return addVerificationOfDetachedSignatures(signatures); } @@ -186,7 +159,7 @@ public class ConsumerOptions { * Attempt decryption using a session key. * * Note: PGPainless does not yet support decryption with session keys. - * TODO: Implement + * TODO: Add support for decryption using session key. * * @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-2.1">RFC4880 on Session Keys</a> * @@ -195,7 +168,8 @@ public class ConsumerOptions { */ public ConsumerOptions setSessionKey(@Nonnull byte[] sessionKey) { this.sessionKey = sessionKey; - return this; + throw new NotYetImplementedException(); + // return this; } /** diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/NotYetImplementedException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/NotYetImplementedException.java new file mode 100644 index 00000000..12478f05 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/exception/NotYetImplementedException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2021 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.exception; + +/** + * Method that gets thrown if the user requests some functionality which is not yet implemented. + */ +public class NotYetImplementedException extends RuntimeException { + +} From e7f685e63b566988f0ca309c4d40bea96108014a Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Wed, 23 Jun 2021 19:20:47 +0200 Subject: [PATCH 6/8] Tests: Make use of KeyRingInfo to determine encryption subkeys --- .../DecryptHiddenRecipientMessage.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java index 7cc76f33..e86659c7 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/DecryptHiddenRecipientMessage.java @@ -21,7 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Set; +import java.util.List; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; @@ -30,11 +30,10 @@ import org.bouncycastle.util.io.Streams; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; -import org.pgpainless.key.util.KeyRingUtils; -import org.pgpainless.util.selection.key.impl.EncryptionKeySelectionStrategy; +import org.pgpainless.key.info.KeyRingInfo; public class DecryptHiddenRecipientMessage { @@ -155,10 +154,8 @@ public class DecryptHiddenRecipientMessage { OpenPgpMetadata metadata = decryptionStream.getResult(); assertEquals(0, metadata.getRecipientKeyIds().size()); - // Hacky way of getting the encryption subkey of the key ring - // TODO: Create convenient method for this - Set<PGPPublicKey> encryptionKeys = new EncryptionKeySelectionStrategy(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .selectKeysFromKeyRing(KeyRingUtils.publicKeyRingFrom(secretKeys)); + KeyRingInfo info = new KeyRingInfo(secretKeys); + List<PGPPublicKey> encryptionKeys = info.getEncryptionSubkeys(EncryptionPurpose.STORAGE_AND_COMMUNICATIONS); assertEquals(1, encryptionKeys.size()); assertEquals(new OpenPgpV4Fingerprint(encryptionKeys.iterator().next()), metadata.getDecryptionFingerprint()); From 3c37072774a30fb51103c9288ecd452f10815a78 Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Wed, 23 Jun 2021 19:33:13 +0200 Subject: [PATCH 7/8] Remove unused Selection classes --- .../org/pgpainless/key/util/KeyRingUtils.java | 36 ---- .../util/selection/key/impl/And.java | 66 ------- .../impl/EncryptionKeySelectionStrategy.java | 54 ------ .../impl/HasAllKeyFlagSelectionStrategy.java | 71 -------- .../impl/HasAnyKeyFlagSelectionStrategy.java | 74 -------- .../key/impl/KeyBelongsToKeyRing.java | 67 ------- .../util/selection/key/impl/NoRevocation.java | 51 ------ .../util/selection/key/impl/Or.java | 66 ------- .../impl/SignatureKeySelectionStrategy.java | 55 ------ .../util/selection/key/impl/package-info.java | 19 -- .../encryption_signing/SigningTest.java | 7 +- .../java/org/pgpainless/util/BCUtilTest.java | 31 ---- .../key/AndOrSelectionStrategyTest.java | 62 ------- .../key/KeyBelongsToKeyRingTest.java | 54 ------ .../KeyFlagBasedSelectionStrategyTest.java | 169 ------------------ 15 files changed, 3 insertions(+), 879 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/And.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/EncryptionKeySelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAllKeyFlagSelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAnyKeyFlagSelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/KeyBelongsToKeyRing.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/NoRevocation.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/Or.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/SignatureKeySelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/package-info.java delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/util/selection/key/AndOrSelectionStrategyTest.java delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyBelongsToKeyRingTest.java delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyFlagBasedSelectionStrategyTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java index 7b75e084..c7872589 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java @@ -21,7 +21,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; - import javax.annotation.Nonnull; import org.bouncycastle.openpgp.PGPException; @@ -35,10 +34,6 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.impl.And; -import org.pgpainless.util.selection.key.impl.KeyBelongsToKeyRing; -import org.pgpainless.util.selection.key.impl.NoRevocation; public class KeyRingUtils { @@ -162,37 +157,6 @@ public class KeyRingUtils { return new PGPSecretKeyRingCollection(Arrays.asList(rings)); } - /** - * Remove all keys from the key ring, are either not having a subkey signature from the master key - * (identified by {@code masterKeyId}), or are revoked ("normal" key revocation, as well as subkey revocation). - * - * @param ring key ring - * @param masterKey master key - * @return "cleaned" key ring - */ - public static PGPSecretKeyRing removeUnassociatedKeysFromKeyRing(@Nonnull PGPSecretKeyRing ring, - @Nonnull PGPPublicKey masterKey) { - if (!masterKey.isMasterKey()) { - throw new IllegalArgumentException("Given key is not a master key."); - } - // Only select keys which are signed by the master key and not revoked. - PublicKeySelectionStrategy selector = new And.PubKeySelectionStrategy( - new KeyBelongsToKeyRing.PubkeySelectionStrategy(masterKey), - new NoRevocation.PubKeySelectionStrategy()); - - PGPSecretKeyRing cleaned = ring; - - Iterator<PGPSecretKey> secretKeys = ring.getSecretKeys(); - while (secretKeys.hasNext()) { - PGPSecretKey secretKey = secretKeys.next(); - if (!selector.accept(secretKey.getPublicKey())) { - cleaned = PGPSecretKeyRing.removeSecretKey(cleaned, secretKey); - } - } - - return cleaned; - } - public static boolean keyRingContainsKeyWithId(@Nonnull PGPPublicKeyRing ring, long keyId) { return ring.getPublicKey(keyId) != null; diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/And.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/And.java deleted file mode 100644 index f2f7194e..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/And.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -public class And { - - public static class PubKeySelectionStrategy extends PublicKeySelectionStrategy { - - private final Set<PublicKeySelectionStrategy> strategies = new HashSet<>(); - - public PubKeySelectionStrategy(@Nonnull PublicKeySelectionStrategy... strategies) { - this.strategies.addAll(Arrays.asList(strategies)); - } - - @Override - public boolean accept(PGPPublicKey key) { - boolean accept = true; - for (PublicKeySelectionStrategy strategy : strategies) { - accept &= strategy.accept(key); - } - return accept; - } - } - - public static class SecKeySelectionStrategy extends SecretKeySelectionStrategy { - - private final Set<SecretKeySelectionStrategy> strategies = new HashSet<>(); - - public SecKeySelectionStrategy(@Nonnull SecretKeySelectionStrategy... strategies) { - this.strategies.addAll(Arrays.asList(strategies)); - } - - @Override - public boolean accept(PGPSecretKey key) { - boolean accept = true; - for (SecretKeySelectionStrategy strategy : strategies) { - accept &= strategy.accept(key); - } - return accept; - } - } - -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/EncryptionKeySelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/EncryptionKeySelectionStrategy.java deleted file mode 100644 index 71c5e26e..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/EncryptionKeySelectionStrategy.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.algorithm.PublicKeyAlgorithm; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; - -/** - * Key Selection Strategy that only accepts {@link PGPPublicKey}s which are capable of encryption. - */ -public class EncryptionKeySelectionStrategy extends PublicKeySelectionStrategy { - - public static final Logger LOGGER = Logger.getLogger(EncryptionKeySelectionStrategy.class.getName()); - - private final HasAnyKeyFlagSelectionStrategy.PublicKey keyFlagSelector; - - public EncryptionKeySelectionStrategy(KeyFlag... flags) { - this.keyFlagSelector = new HasAnyKeyFlagSelectionStrategy.PublicKey(flags); - } - - @Override - public boolean accept(@Nonnull PGPPublicKey key) { - if (!key.isEncryptionKey()) { - LOGGER.log(Level.FINE, "Rejecting key " + Long.toHexString(key.getKeyID()) + " as its algorithm (" + - PublicKeyAlgorithm.fromId(key.getAlgorithm()) + ") is not suitable of encryption."); - return false; - } - if (!keyFlagSelector.accept(key)) { - LOGGER.log(Level.FINE, "Rejecting key " + Long.toHexString(key.getKeyID()) + " as it does not the appropriate encryption key flags."); - return false; - } - - return true; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAllKeyFlagSelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAllKeyFlagSelectionStrategy.java deleted file mode 100644 index 791687ec..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAllKeyFlagSelectionStrategy.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2021 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.Iterator; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -/** - * Selection Strategy that accepts a key if it carries all of the specified key flags. - */ -public class HasAllKeyFlagSelectionStrategy { - - public static class PublicKey extends PublicKeySelectionStrategy { - - private final int keyFlagMask; - - public PublicKey(KeyFlag... flags) { - this(KeyFlag.toBitmask(flags)); - } - - public PublicKey(int mask) { - this.keyFlagMask = mask; - } - - @Override - public boolean accept(PGPPublicKey key) { - Iterator<PGPSignature> signatures = key.getSignatures(); - int flags = signatures.next().getHashedSubPackets().getKeyFlags(); - return (keyFlagMask & flags) == keyFlagMask; - } - } - - public static class SecretKey extends SecretKeySelectionStrategy { - - private final int keyFlagMask; - - public SecretKey(KeyFlag... flags) { - this(KeyFlag.toBitmask(flags)); - } - - public SecretKey(int mask) { - this.keyFlagMask = mask; - } - - @Override - public boolean accept(PGPSecretKey key) { - Iterator<PGPSignature> signatures = key.getPublicKey().getSignatures(); - int flags = signatures.next().getHashedSubPackets().getKeyFlags(); - return (keyFlagMask & flags) == keyFlagMask; - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAnyKeyFlagSelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAnyKeyFlagSelectionStrategy.java deleted file mode 100644 index 70b7267c..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/HasAnyKeyFlagSelectionStrategy.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.Iterator; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -/** - * Selection Strategies that accept a key if it carries at least one of the given key flags. - */ -public class HasAnyKeyFlagSelectionStrategy { - - public static class PublicKey extends PublicKeySelectionStrategy { - - private final int keyFlagMask; - - public PublicKey(KeyFlag... flags) { - this(KeyFlag.toBitmask(flags)); - } - - public PublicKey(int mask) { - this.keyFlagMask = mask; - } - - @Override - public boolean accept(PGPPublicKey key) { - Iterator<PGPSignature> signatures = key.getSignatures(); - int flags = 0; - while (signatures.hasNext()) { - flags = signatures.next().getHashedSubPackets().getKeyFlags(); - } - return (keyFlagMask & flags) != 0; - } - } - - public static class SecretKey extends SecretKeySelectionStrategy { - - private final int keyFlagMask; - - public SecretKey(KeyFlag... flags) { - this(KeyFlag.toBitmask(flags)); - } - - public SecretKey(int mask) { - this.keyFlagMask = mask; - } - - @Override - public boolean accept(PGPSecretKey key) { - Iterator<PGPSignature> signatures = key.getPublicKey().getSignatures(); - int flags = signatures.next().getHashedSubPackets().getKeyFlags(); - return (keyFlagMask & flags) != 0; - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/KeyBelongsToKeyRing.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/KeyBelongsToKeyRing.java deleted file mode 100644 index 5e2b4af2..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/KeyBelongsToKeyRing.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import javax.annotation.Nonnull; -import java.util.Arrays; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSignature; -import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; - -public class KeyBelongsToKeyRing { - - private static final Logger LOGGER = Logger.getLogger(KeyBelongsToKeyRing.class.getName()); - - public static class PubkeySelectionStrategy extends PublicKeySelectionStrategy { - - private final PGPPublicKey masterKey; - - public PubkeySelectionStrategy(PGPPublicKey masterKey) { - this.masterKey = masterKey; - } - - @Override - public boolean accept(@Nonnull PGPPublicKey key) { - // Same key -> accept - if (Arrays.equals(masterKey.getFingerprint(), key.getFingerprint())) { - return true; - } - - Iterator<PGPSignature> signatures = key.getSignaturesForKeyID(masterKey.getKeyID()); - while (signatures.hasNext()) { - PGPSignature signature = signatures.next(); - if (signature.getSignatureType() == PGPSignature.SUBKEY_BINDING) { - try { - signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), masterKey); - return signature.verifyCertification(masterKey, key); - } catch (PGPException e) { - LOGGER.log(Level.WARNING, "Could not verify subkey signature of key " + - Long.toHexString(masterKey.getKeyID()) + " on key " + Long.toHexString(key.getKeyID())); - - return false; - } - } - } - return false; - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/NoRevocation.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/NoRevocation.java deleted file mode 100644 index c5c7899f..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/NoRevocation.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -/** - * Key Selection Strategies that do accept only keys, which have no revocation. - */ -public class NoRevocation { - - /** - * Key Selection Strategy which only accepts {@link PGPPublicKey}s which have no revocation. - */ - public static class PubKeySelectionStrategy extends PublicKeySelectionStrategy { - - @Override - public boolean accept(@Nonnull PGPPublicKey key) { - return !key.hasRevocation(); - } - } - - /** - * Key Selection Strategy which only accepts {@link PGPSecretKey}s which have no revocation. - */ - public static class SecKeySelectionStrategy extends SecretKeySelectionStrategy { - - @Override - public boolean accept(@Nonnull PGPSecretKey key) { - return !key.getPublicKey().hasRevocation(); - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/Or.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/Or.java deleted file mode 100644 index 880df4f1..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/Or.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -public class Or { - - public static class PubKeySelectionStrategy extends PublicKeySelectionStrategy { - - private final Set<PublicKeySelectionStrategy> strategies = new HashSet<>(); - - public PubKeySelectionStrategy(@Nonnull PublicKeySelectionStrategy... strategies) { - this.strategies.addAll(Arrays.asList(strategies)); - } - - @Override - public boolean accept(PGPPublicKey key) { - boolean accept = false; - for (PublicKeySelectionStrategy strategy : strategies) { - accept |= strategy.accept(key); - } - return accept; - } - } - - public static class SecKeySelectionStrategy extends SecretKeySelectionStrategy { - - private final Set<SecretKeySelectionStrategy> strategies = new HashSet<>(); - - public SecKeySelectionStrategy(@Nonnull SecretKeySelectionStrategy... strategies) { - this.strategies.addAll(Arrays.asList(strategies)); - } - - @Override - public boolean accept(PGPSecretKey key) { - boolean accept = false; - for (SecretKeySelectionStrategy strategy : strategies) { - accept |= strategy.accept(key); - } - return accept; - } - } - -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/SignatureKeySelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/SignatureKeySelectionStrategy.java deleted file mode 100644 index 9b5e310b..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/SignatureKeySelectionStrategy.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key.impl; - -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPSecretKey; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.algorithm.PublicKeyAlgorithm; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -/** - * Key Selection Strategy that only accepts {@link PGPSecretKey}s which are capable of signing. - */ -public class SignatureKeySelectionStrategy extends SecretKeySelectionStrategy { - - private static final Logger LOGGER = Logger.getLogger(SignatureKeySelectionStrategy.class.getName()); - - HasAnyKeyFlagSelectionStrategy.SecretKey flagSelector = - new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.SIGN_DATA); - - @Override - public boolean accept(@Nonnull PGPSecretKey key) { - boolean hasSignDataKeyFlag = flagSelector.accept(key); - - if (!key.isSigningKey()) { - LOGGER.log(Level.FINE, "Rejecting key " + Long.toHexString(key.getKeyID()) + " as its algorithm (" + - PublicKeyAlgorithm.fromId(key.getPublicKey().getAlgorithm()) + ") is not capable of signing."); - return false; - } - - if (!hasSignDataKeyFlag) { - LOGGER.log(Level.FINE, "Rejecting key " + Long.toHexString(key.getKeyID()) + - " as it does not carry the key flag SIGN_DATA."); - return false; - } - return true; - } - -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/package-info.java deleted file mode 100644 index 6729a311..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/impl/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Implementations of Key Selection Strategies. - */ -package org.pgpainless.util.selection.key.impl; diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java index f04b2c11..ab03f45a 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java @@ -45,10 +45,10 @@ import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.exception.KeyValidationException; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.TestKeys; +import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.util.Passphrase; -import org.pgpainless.util.selection.key.impl.SignatureKeySelectionStrategy; public class SigningTest { @@ -61,9 +61,8 @@ public class SigningTest { PGPPublicKeyRing romeoKeys = TestKeys.getRomeoPublicKeyRing(); PGPSecretKeyRing cryptieKeys = TestKeys.getCryptieSecretKeyRing(); - PGPSecretKey cryptieSigningKey = new SignatureKeySelectionStrategy() - .selectKeysFromKeyRing(cryptieKeys) - .iterator().next(); + KeyRingInfo cryptieInfo = new KeyRingInfo(cryptieKeys); + PGPSecretKey cryptieSigningKey = cryptieKeys.getSecretKey(cryptieInfo.getSigningSubkeys().get(0).getKeyID()); PGPPublicKeyRingCollection keys = new PGPPublicKeyRingCollection(Arrays.asList(julietKeys, romeoKeys)); diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java index 69887b8d..857e6574 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java @@ -16,8 +16,6 @@ package org.pgpainless.util; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; @@ -30,7 +28,6 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.junit.jupiter.api.Test; @@ -105,32 +102,4 @@ public class BCUtilTest { LOGGER.log(Level.FINER, "PubCol: " + pubColSize); } - - @Test - public void removeUnsignedKeysTest() - throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - @SuppressWarnings("deprecation") - PGPSecretKeyRing alice = PGPainless.generateKeyRing().simpleRsaKeyRing("alice@wonderland.lit", RsaLength._1024); - PGPSecretKeyRing mallory = PGPainless.generateKeyRing().simpleEcKeyRing("mallory@mall.ory"); - - PGPSecretKey subKey = null; - Iterator<PGPSecretKey> sit = mallory.getSecretKeys(); - while (sit.hasNext()) { - PGPSecretKey s = sit.next(); - if (!s.isMasterKey()) { - subKey = s; - break; - } - } - - assertNotNull(subKey); - - PGPSecretKeyRing alice_mallory = PGPSecretKeyRing.insertSecretKey(alice, subKey); - - // Check, if alice_mallory contains mallory's key - assertNotNull(alice_mallory.getSecretKey(subKey.getKeyID())); - - PGPSecretKeyRing cleaned = KeyRingUtils.removeUnassociatedKeysFromKeyRing(alice_mallory, alice.getPublicKey()); - assertNull(cleaned.getSecretKey(subKey.getKeyID())); - } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/AndOrSelectionStrategyTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/AndOrSelectionStrategyTest.java deleted file mode 100644 index 32e85006..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/AndOrSelectionStrategyTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2021 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.util.Iterator; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.junit.jupiter.api.Test; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.key.TestKeys; -import org.pgpainless.util.selection.key.impl.EncryptionKeySelectionStrategy; -import org.pgpainless.util.selection.key.impl.HasAnyKeyFlagSelectionStrategy; -import org.pgpainless.util.selection.key.impl.Or; - -public class AndOrSelectionStrategyTest { - - @Test - public void testOr() throws IOException, PGPException { - PGPSecretKeyRing ring = TestKeys.getEmilSecretKeyRing(); - Iterator<PGPSecretKey> secretKeys = ring.getSecretKeys(); - Or.SecKeySelectionStrategy secStrategy = new Or.SecKeySelectionStrategy( - new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_COMMS), - new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_STORAGE) - ); - PGPSecretKey certSecKey = secretKeys.next(); - PGPSecretKey cryptSecKey = secretKeys.next(); - - assertFalse(secStrategy.accept(certSecKey)); - assertTrue(secStrategy.accept(cryptSecKey)); - - Iterator<PGPPublicKey> publicKeys = ring.getPublicKeys(); - Or.PubKeySelectionStrategy pubStrategy = new Or.PubKeySelectionStrategy( - new EncryptionKeySelectionStrategy(KeyFlag.ENCRYPT_COMMS), - new EncryptionKeySelectionStrategy(KeyFlag.ENCRYPT_STORAGE) - ); - PGPPublicKey certPubKey = publicKeys.next(); - PGPPublicKey cryptPubKey = publicKeys.next(); - - assertFalse(pubStrategy.accept(certPubKey)); - assertTrue(pubStrategy.accept(cryptPubKey)); - } -} diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyBelongsToKeyRingTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyBelongsToKeyRingTest.java deleted file mode 100644 index 1fd17535..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyBelongsToKeyRingTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.junit.jupiter.api.Test; -import org.pgpainless.PGPainless; -import org.pgpainless.key.TestKeys; -import org.pgpainless.util.selection.key.impl.KeyBelongsToKeyRing; - -public class KeyBelongsToKeyRingTest { - - @Test - public void testStrategyOnlyAcceptsKeysThatBelongToKeyRing() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { - PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("test@test.test"); - Iterator<PGPPublicKey> iterator = secretKeys.getPublicKeys(); - PGPPublicKey primaryKey = iterator.next(); - PGPPublicKey subKey = iterator.next(); - - KeyBelongsToKeyRing.PubkeySelectionStrategy strategy = new KeyBelongsToKeyRing.PubkeySelectionStrategy(primaryKey); - assertTrue(strategy.accept(primaryKey)); - assertTrue(strategy.accept(subKey)); - - PGPSecretKeyRing unrelatedKeys = TestKeys.getEmilSecretKeyRing(); - Iterator<PGPPublicKey> unrelated = unrelatedKeys.getPublicKeys(); - while (unrelated.hasNext()) { - PGPPublicKey unrelatedKey = unrelated.next(); - assertFalse(strategy.accept(unrelatedKey)); - } - } -} diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyFlagBasedSelectionStrategyTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyFlagBasedSelectionStrategyTest.java deleted file mode 100644 index 27422354..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/util/selection/key/KeyFlagBasedSelectionStrategyTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2021 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.junit.jupiter.api.Test; -import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.key.generation.KeySpec; -import org.pgpainless.key.generation.type.KeyType; -import org.pgpainless.key.generation.type.ecc.EllipticCurve; -import org.pgpainless.key.generation.type.eddsa.EdDSACurve; -import org.pgpainless.key.generation.type.xdh.XDHSpec; -import org.pgpainless.util.selection.key.impl.HasAllKeyFlagSelectionStrategy; -import org.pgpainless.util.selection.key.impl.HasAnyKeyFlagSelectionStrategy; -import org.pgpainless.key.util.KeyRingUtils; - -public class KeyFlagBasedSelectionStrategyTest { - - @Test - public void testKeyFlagSelectors() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._P256)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.AUTHENTICATION) - .withDefaultAlgorithms()) - .withPrimaryUserId("test@test.test") - .withoutPassphrase().build(); - - Iterator<PGPSecretKey> iterator = secretKeys.iterator(); - // CERTIFY_OTHER and AUTHENTICATION - PGPSecretKey s_primaryKey = iterator.next(); - // SIGN_DATA - PGPSecretKey s_signingKey = iterator.next(); - // ENCRYPT_COMMS - PGPSecretKey s_encryptionKey = iterator.next(); - - HasAllKeyFlagSelectionStrategy.SecretKey s_certifyOther = - new HasAllKeyFlagSelectionStrategy.SecretKey(KeyFlag.CERTIFY_OTHER); - HasAllKeyFlagSelectionStrategy.SecretKey s_encryptComms = - new HasAllKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_COMMS); - HasAllKeyFlagSelectionStrategy.SecretKey s_encryptCommsEncryptStorage = - new HasAllKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE); - HasAnyKeyFlagSelectionStrategy.SecretKey s_anyEncryptCommsEncryptStorage = - new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE); - - assertTrue(s_certifyOther.accept(s_primaryKey)); - assertFalse(s_certifyOther.accept(s_encryptionKey)); - assertFalse(s_certifyOther.accept(s_signingKey)); - - assertTrue(s_encryptComms.accept(s_encryptionKey)); - assertFalse(s_encryptComms.accept(s_primaryKey)); - assertFalse(s_encryptComms.accept(s_signingKey)); - - assertFalse(s_encryptCommsEncryptStorage.accept(s_encryptionKey), - "Must not accept the key, as it only carries ENCRYPT_COMMS, but not ENCRYPT_STORAGE"); - assertFalse(s_encryptCommsEncryptStorage.accept(s_primaryKey)); - assertFalse(s_encryptCommsEncryptStorage.accept(s_signingKey)); - - assertTrue(s_anyEncryptCommsEncryptStorage.accept(s_encryptionKey)); - assertFalse(s_anyEncryptCommsEncryptStorage.accept(s_primaryKey)); - assertFalse(s_anyEncryptCommsEncryptStorage.accept(s_signingKey)); - - PGPPublicKey p_primaryKey = s_primaryKey.getPublicKey(); - PGPPublicKey p_encryptionKey = s_encryptionKey.getPublicKey(); - PGPPublicKey p_signingKey = s_signingKey.getPublicKey(); - - HasAllKeyFlagSelectionStrategy.PublicKey p_certifyOther = - new HasAllKeyFlagSelectionStrategy.PublicKey(KeyFlag.CERTIFY_OTHER); - HasAllKeyFlagSelectionStrategy.PublicKey p_encryptComms = - new HasAllKeyFlagSelectionStrategy.PublicKey(KeyFlag.ENCRYPT_COMMS); - HasAllKeyFlagSelectionStrategy.PublicKey p_encryptCommsEncryptStorage = - new HasAllKeyFlagSelectionStrategy.PublicKey(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE); - HasAnyKeyFlagSelectionStrategy.PublicKey p_anyEncryptCommsEncryptStorage = - new HasAnyKeyFlagSelectionStrategy.PublicKey(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE); - - assertTrue(p_certifyOther.accept(p_primaryKey)); - assertFalse(p_certifyOther.accept(p_encryptionKey)); - assertFalse(p_certifyOther.accept(p_signingKey)); - - assertTrue(p_encryptComms.accept(p_encryptionKey)); - assertFalse(p_encryptComms.accept(p_primaryKey)); - assertFalse(p_encryptComms.accept(p_signingKey)); - - assertFalse(p_encryptCommsEncryptStorage.accept(p_encryptionKey), - "Must not accept the key, as it only carries ENCRYPT_COMMS, but not ENCRYPT_STORAGE"); - assertFalse(p_encryptCommsEncryptStorage.accept(p_primaryKey)); - assertFalse(p_encryptCommsEncryptStorage.accept(p_signingKey)); - - assertTrue(p_anyEncryptCommsEncryptStorage.accept(p_encryptionKey)); - assertFalse(p_anyEncryptCommsEncryptStorage.accept(p_primaryKey)); - assertFalse(p_anyEncryptCommsEncryptStorage.accept(p_signingKey)); - } - - @Test - public void testSelectKeysFromKeyRing() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._P256)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.AUTHENTICATION) - .withDefaultAlgorithms()) - .withPrimaryUserId("test@test.test") - .withoutPassphrase().build(); - - HasAnyKeyFlagSelectionStrategy.SecretKey secSelection = - new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.SIGN_DATA); - - Set<PGPSecretKey> secSigningKeys = secSelection.selectKeysFromKeyRing(secretKeys); - assertEquals(2, secSigningKeys.size()); - - PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); - - HasAnyKeyFlagSelectionStrategy.PublicKey pubSelection = - new HasAnyKeyFlagSelectionStrategy.PublicKey(KeyFlag.SIGN_DATA); - - Set<PGPPublicKey> pubSigningKeys = pubSelection.selectKeysFromKeyRing(publicKeys); - assertEquals(2, pubSigningKeys.size()); - - List<Long> ids = new ArrayList<>(); - for (PGPSecretKey secretKey : secSigningKeys) { - ids.add(secretKey.getKeyID()); - } - - for (PGPPublicKey publicKey : pubSigningKeys) { - assertTrue(ids.contains(publicKey.getKeyID())); - } - - secSelection = new HasAnyKeyFlagSelectionStrategy.SecretKey(KeyFlag.ENCRYPT_STORAGE); - assertTrue(secSelection.selectKeysFromKeyRing(secretKeys).isEmpty()); - } -} From 259f629b3cf4b2656e06dcbe937efd33e984dd0e Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Wed, 23 Jun 2021 19:39:10 +0200 Subject: [PATCH 8/8] Further deletion of unused selection classes --- .../selection/key/KeySelectionStrategy.java | 32 ------- .../key/PublicKeySelectionStrategy.java | 41 --------- .../key/SecretKeySelectionStrategy.java | 42 --------- .../util/selection/key/package-info.java | 19 ---- .../util/selection/keyring/impl/Email.java | 32 ------- .../selection/keyring/impl/PartialUserId.java | 67 -------------- .../EmailKeyRingSelectionStrategyTest.java | 91 ------------------- 7 files changed, 324 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/KeySelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/PublicKeySelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/SecretKeySelectionStrategy.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/key/package-info.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/Email.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/PartialUserId.java delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/util/selection/keyring/EmailKeyRingSelectionStrategyTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/KeySelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/KeySelectionStrategy.java deleted file mode 100644 index 68884e92..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/KeySelectionStrategy.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import java.util.Set; -import javax.annotation.Nonnull; - - -/** - * Interface that describes a selection strategy for OpenPGP keys. - * @param <K> Type of the Key - * @param <R> Type of the PGPKeyRing - */ -public interface KeySelectionStrategy<K, R> { - - boolean accept(K key); - - Set<K> selectKeysFromKeyRing(@Nonnull R ring); -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/PublicKeySelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/PublicKeySelectionStrategy.java deleted file mode 100644 index 5e0f22bb..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/PublicKeySelectionStrategy.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; - -/** - * Key Selection Strategy which accepts {@link PGPPublicKey}s that are accepted by the abstract method - * {@link #accept(Object)}. - */ -public abstract class PublicKeySelectionStrategy implements KeySelectionStrategy<PGPPublicKey, PGPPublicKeyRing> { - - @Override - public Set<PGPPublicKey> selectKeysFromKeyRing(@Nonnull PGPPublicKeyRing ring) { - Set<PGPPublicKey> keys = new HashSet<>(); - for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) { - PGPPublicKey key = i.next(); - if (accept(key)) keys.add(key); - } - return keys; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/SecretKeySelectionStrategy.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/SecretKeySelectionStrategy.java deleted file mode 100644 index 49e40a0d..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/SecretKeySelectionStrategy.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.key; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; - -/** - * Key Selection Strategy which accepts {@link PGPSecretKey}s that are accepted by the abstract method - * {@link #accept(Object)}. - * - */ -public abstract class SecretKeySelectionStrategy implements KeySelectionStrategy<PGPSecretKey, PGPSecretKeyRing> { - - @Override - public Set<PGPSecretKey> selectKeysFromKeyRing(@Nonnull PGPSecretKeyRing ring) { - Set<PGPSecretKey> keys = new HashSet<>(); - for (Iterator<PGPSecretKey> i = ring.getSecretKeys(); i.hasNext(); ) { - PGPSecretKey key = i.next(); - if (accept(key)) keys.add(key); - } - return keys; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/package-info.java deleted file mode 100644 index 971fa41c..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/key/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Different Key Selection Strategies. - */ -package org.pgpainless.util.selection.key; diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/Email.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/Email.java deleted file mode 100644 index 059907df..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/Email.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.keyring.impl; - -public class Email { - - public static class PubRingSelectionStrategy extends PartialUserId.PubRingSelectionStrategy { - - public PubRingSelectionStrategy(String email) { - super(email.matches("^<.+>$") ? email : '<' + email + '>'); - } - } - - public static class SecRingSelectionStrategy extends PartialUserId.SecRingSelectionStrategy { - public SecRingSelectionStrategy(String email) { - super(email.matches("^<.+>$") ? email : '<' + email + '>'); - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/PartialUserId.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/PartialUserId.java deleted file mode 100644 index 816aca3a..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/keyring/impl/PartialUserId.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.keyring.impl; - -import javax.annotation.Nonnull; -import java.util.Iterator; - -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.pgpainless.util.selection.key.PublicKeySelectionStrategy; -import org.pgpainless.util.selection.key.SecretKeySelectionStrategy; - -public class PartialUserId { - - public static class PubRingSelectionStrategy extends PublicKeySelectionStrategy { - - protected final String identifier; - - public PubRingSelectionStrategy(String identifier) { - this.identifier = identifier; - } - - @Override - public boolean accept(@Nonnull PGPPublicKey key) { - for (Iterator<String> userIds = key.getUserIDs(); userIds.hasNext(); ) { - String userId = userIds.next(); - if (userId.contains(identifier)) { - return true; - } - } - return false; - } - } - - public static class SecRingSelectionStrategy extends SecretKeySelectionStrategy { - - protected final String partialUserId; - - public SecRingSelectionStrategy(String partialUserId) { - this.partialUserId = partialUserId; - } - - @Override - public boolean accept(@Nonnull PGPSecretKey key) { - for (Iterator<String> userIds = key.getUserIDs(); userIds.hasNext(); ) { - String userId = userIds.next(); - if (userId.contains(partialUserId)) { - return true; - } - } - return false; - } - } -} diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/selection/keyring/EmailKeyRingSelectionStrategyTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/selection/keyring/EmailKeyRingSelectionStrategyTest.java deleted file mode 100644 index 655af7b8..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/util/selection/keyring/EmailKeyRingSelectionStrategyTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2020 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.util.selection.keyring; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.junit.jupiter.api.Test; -import org.pgpainless.key.TestKeys; -import org.pgpainless.util.selection.keyring.impl.Email; - -public class EmailKeyRingSelectionStrategyTest { - - @Test - public void testMatchingEmailUIDAcceptedOnPubKey() throws IOException { - String uid = "<emil@email.user>"; - PGPPublicKey key = TestKeys.getEmilPublicKeyRing().getPublicKey(); - - Email.PubRingSelectionStrategy pubKeySelectionStrategy = new Email.PubRingSelectionStrategy(uid); - - assertTrue(pubKeySelectionStrategy.accept(key)); - } - - @Test - public void testAddressIsFormattedToMatchOnPubKey() throws IOException { - String uid = "emil@email.user"; - PGPPublicKey key = TestKeys.getEmilPublicKeyRing().getPublicKey(); - - Email.PubRingSelectionStrategy pubKeySelectionStrategy = new Email.PubRingSelectionStrategy(uid); - - assertTrue(pubKeySelectionStrategy.accept(key)); - } - - @Test - public void testPubKeyWithDifferentUIDIsRejected() throws IOException { - String wrongUid = "emilia@email.user"; - PGPPublicKey key = TestKeys.getEmilPublicKeyRing().getPublicKey(); - - Email.PubRingSelectionStrategy pubKeySelectionStrategy = new Email.PubRingSelectionStrategy(wrongUid); - - assertFalse(pubKeySelectionStrategy.accept(key)); - } - - @Test - public void testMatchingEmailUIDAcceptedOnSecKey() throws IOException, PGPException { - String uid = "<emil@email.user>"; - PGPSecretKey key = TestKeys.getEmilSecretKeyRing().getSecretKey(); - - Email.SecRingSelectionStrategy secKeySelectionStrategy = new Email.SecRingSelectionStrategy(uid); - - assertTrue(secKeySelectionStrategy.accept(key)); - } - - @Test - public void testAddressIsFormattedToMatchOnSecKey() throws IOException, PGPException { - String uid = "emil@email.user"; - PGPSecretKey key = TestKeys.getEmilSecretKeyRing().getSecretKey(); - - Email.SecRingSelectionStrategy secKeySelectionStrategy = new Email.SecRingSelectionStrategy(uid); - - assertTrue(secKeySelectionStrategy.accept(key)); - } - - @Test - public void testSecKeyWithDifferentUIDIsRejected() throws IOException, PGPException { - String wrongUid = "emilia@email.user"; - PGPSecretKey key = TestKeys.getEmilSecretKeyRing().getSecretKey(); - - Email.SecRingSelectionStrategy secKeySelectionStrategy = new Email.SecRingSelectionStrategy(wrongUid); - - assertFalse(secKeySelectionStrategy.accept(key)); - } -}