1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-27 06:42:05 +01:00

Work on KeyRingInfo

This commit is contained in:
Paul Schaub 2021-05-24 16:10:26 +02:00
parent 6cb9091b2a
commit 909f0e7be3
4 changed files with 224 additions and 72 deletions

View file

@ -15,10 +15,12 @@
*/ */
package org.pgpainless.encryption_signing; package org.pgpainless.encryption_signing;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -32,15 +34,46 @@ import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.util.Passphrase; import org.pgpainless.util.Passphrase;
/**
* Options for the encryption process.
* This class can be used to set encryption parameters, like encryption keys and passphrases, algorithms etc.
*
* A typical use might look like follows:
* <pre>
* {@code
* EncryptionOptions opt = new EncryptionOptions();
* opt.addRecipient(aliceKey, "Alice <alice@wonderland.lit>");
* opt.addPassphrase(Passphrase.fromPassword("AdditionalDecryptionPassphrase123"));
* }
* </pre>
*
* To use a custom symmetric encryption algorithm, use {@link #overrideEncryptionAlgorithm(SymmetricKeyAlgorithm)}.
* This will cause PGPainless to use the provided algorithm for message encryption, instead of negotiating an algorithm
* by inspecting the provided recipient keys.
*
* By default, PGPainless will only encrypt to a single encryption capable subkey per recipient key.
* This behavior can be changed, eg. by calling
* <pre>
* {@code
* opt.addRecipient(aliceKey, EncryptionOptions.encryptToAllCapableSubkeys());
* }
* </pre>
* when adding the recipient key.
*/
public class EncryptionOptions { public class EncryptionOptions {
private final EncryptionStream.Purpose purpose; private final EncryptionStream.Purpose purpose;
private final Set<PGPKeyEncryptionMethodGenerator> encryptionMethods = new LinkedHashSet<>(); private final Set<PGPKeyEncryptionMethodGenerator> encryptionMethods = new LinkedHashSet<>();
private final Set<SubkeyIdentifier> encryptionKeys = new LinkedHashSet<>(); private final Set<SubkeyIdentifier> encryptionKeys = new LinkedHashSet<>();
private final Map<SubkeyIdentifier, KeyRingInfo> keyRingInfo = new HashMap<>(); private final Map<SubkeyIdentifier, KeyRingInfo> keyRingInfo = new HashMap<>();
private final EncryptionKeySelector encryptionKeySelector = encryptToFirstSubkey();
private SymmetricKeyAlgorithm encryptionAlgorithmOverride = null; private SymmetricKeyAlgorithm encryptionAlgorithmOverride = null;
/**
* Encrypt to keys both carrying the key flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}
* or {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
*/
public EncryptionOptions() { public EncryptionOptions() {
this(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS); this(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS);
} }
@ -49,10 +82,22 @@ public class EncryptionOptions {
this.purpose = purpose; this.purpose = purpose;
} }
/**
* Factory method to create an {@link EncryptionOptions} object which will encrypt for keys
* which carry the flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}.
*
* @return encryption options
*/
public static EncryptionOptions encryptCommunications() { public static EncryptionOptions encryptCommunications() {
return new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS); return new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS);
} }
/**
* Factory method to create an {@link EncryptionOptions} object which will encrypt for keys
* which carry the flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
*
* @return encryption options
*/
public static EncryptionOptions encryptDataAtRest() { public static EncryptionOptions encryptDataAtRest() {
return new EncryptionOptions(EncryptionStream.Purpose.STORAGE); return new EncryptionOptions(EncryptionStream.Purpose.STORAGE);
} }
@ -64,30 +109,65 @@ public class EncryptionOptions {
* @param key key ring * @param key key ring
* @param userId user id * @param userId user id
*/ */
public void addRecipient(PGPPublicKeyRing key, String userId) { public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId) {
return addRecipient(key, userId, encryptionKeySelector);
}
/**
* Add a recipient by providing a key and recipient user-id, as well as a strategy for selecting one or multiple
* encryption capable subkeys from the key.
*
* @param key key
* @param userId user-id
* @param encryptionKeySelectionStrategy strategy to select one or more encryption subkeys to encrypt to
*/
public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId, EncryptionKeySelector encryptionKeySelectionStrategy) {
KeyRingInfo info = new KeyRingInfo(key, new Date()); KeyRingInfo info = new KeyRingInfo(key, new Date());
PGPPublicKey encryptionSubkey = info.getEncryptionSubkey(userId, purpose); List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
if (encryptionSubkey == null) { .selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose));
throw new AssertionError("Key has no encryption subkey."); if (encryptionSubkeys.isEmpty()) {
throw new AssertionError("Key has no suitable encryption subkeys.");
} }
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
addRecipientKey(key, encryptionSubkey); addRecipientKey(key, encryptionSubkey);
} }
return this;
}
/** /**
* Add a recipient by providing a key. * Add a recipient by providing a key.
* *
* @param key key ring * @param key key ring
*/ */
public void addRecipient(PGPPublicKeyRing key) { public EncryptionOptions addRecipient(PGPPublicKeyRing key) {
KeyRingInfo info = new KeyRingInfo(key, new Date()); return addRecipient(key, encryptionKeySelector);
PGPPublicKey encryptionSubkey = info.getEncryptionSubkey(purpose);
if (encryptionSubkey == null) {
throw new IllegalArgumentException("Key has no encryption subkey.");
} }
/**
* Add a recipient by providing a key and an encryption key selection strategy.
*
* @param key key ring
* @param encryptionKeySelectionStrategy strategy used to select one or multiple encryption subkeys.
*/
public EncryptionOptions addRecipient(PGPPublicKeyRing key, EncryptionKeySelector encryptionKeySelectionStrategy) {
KeyRingInfo info = new KeyRingInfo(key, new Date());
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose));
if (encryptionSubkeys.isEmpty()) {
throw new IllegalArgumentException("Key has no suitable encryption subkeys.");
}
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
addRecipientKey(key, encryptionSubkey); addRecipientKey(key, encryptionSubkey);
} }
return this;
}
private void addRecipientKey(PGPPublicKeyRing keyRing, PGPPublicKey key) { private void addRecipientKey(PGPPublicKeyRing keyRing, PGPPublicKey key) {
encryptionKeys.add(new SubkeyIdentifier(keyRing, key.getKeyID())); encryptionKeys.add(new SubkeyIdentifier(keyRing, key.getKeyID()));
PGPKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory PGPKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
@ -100,13 +180,13 @@ public class EncryptionOptions {
* *
* @param passphrase passphrase * @param passphrase passphrase
*/ */
public void addPassphrase(Passphrase passphrase) { public EncryptionOptions addPassphrase(Passphrase passphrase) {
if (passphrase.isEmpty()) { if (passphrase.isEmpty()) {
throw new IllegalArgumentException("Passphrase must not be empty."); throw new IllegalArgumentException("Passphrase must not be empty.");
} }
PBEKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory PBEKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
.getInstance().getPBEKeyEncryptionMethodGenerator(passphrase); .getInstance().getPBEKeyEncryptionMethodGenerator(passphrase);
addEncryptionMethod(encryptionMethod); return addEncryptionMethod(encryptionMethod);
} }
/** /**
@ -119,8 +199,9 @@ public class EncryptionOptions {
* *
* @param encryptionMethod encryption method * @param encryptionMethod encryption method
*/ */
public void addEncryptionMethod(PGPKeyEncryptionMethodGenerator encryptionMethod) { public EncryptionOptions addEncryptionMethod(PGPKeyEncryptionMethodGenerator encryptionMethod) {
encryptionMethods.add(encryptionMethod); encryptionMethods.add(encryptionMethod);
return this;
} }
public Set<PGPKeyEncryptionMethodGenerator> getEncryptionMethods() { public Set<PGPKeyEncryptionMethodGenerator> getEncryptionMethods() {
@ -141,4 +222,38 @@ public class EncryptionOptions {
} }
this.encryptionAlgorithmOverride = encryptionAlgorithm; this.encryptionAlgorithmOverride = encryptionAlgorithm;
} }
public interface EncryptionKeySelector {
List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys);
}
/**
* Only encrypt to the first valid encryption capable subkey we stumble upon.
*
* @return encryption key selector
*/
public static EncryptionKeySelector encryptToFirstSubkey() {
return new EncryptionKeySelector() {
@Override
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
return encryptionCapableKeys.isEmpty() ? Collections.emptyList() : Collections.singletonList(encryptionCapableKeys.get(0));
}
};
}
/**
* Encrypt to any valid, encryption capable subkey on the key ring.
*
* @return encryption key selector
*/
public static EncryptionKeySelector encryptToAllCapableSubkeys() {
return new EncryptionKeySelector() {
@Override
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
return encryptionCapableKeys;
}
};
}
// TODO: Create encryptToBestSubkey() method
} }

View file

@ -82,19 +82,22 @@ public final class SigningOptions {
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date()); KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
if (userId != null) { if (userId != null) {
if (!keyRingInfo.isUserIdValid(userId)) { if (!keyRingInfo.isUserIdValid(userId)) {
throw new KeyValidationException(userId, keyRingInfo.getCurrentUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId)); throw new KeyValidationException(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
} }
} }
PGPPublicKey signingPubKey = keyRingInfo.getSigningSubkey(); List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
if (signingPubKey == null) { if (signingPubKeys.isEmpty()) {
throw new AssertionError("Key has no valid signing key."); throw new AssertionError("Key has no valid signing key.");
} }
for (PGPPublicKey signingPubKey : signingPubKeys) {
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID()); PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID())); PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID()); List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, false); addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, false);
} }
}
public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor, public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey, PGPSecretKeyRing secretKey,
@ -111,19 +114,22 @@ public final class SigningOptions {
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date()); KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
if (userId != null) { if (userId != null) {
if (!keyRingInfo.isUserIdValid(userId)) { if (!keyRingInfo.isUserIdValid(userId)) {
throw new KeyValidationException(userId, keyRingInfo.getCurrentUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId)); throw new KeyValidationException(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
} }
} }
PGPPublicKey signingPubKey = keyRingInfo.getSigningSubkey(); List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
if (signingPubKey == null) { if (signingPubKeys.isEmpty()) {
throw new AssertionError("Key has no valid signing key."); throw new AssertionError("Key has no valid signing key.");
} }
for (PGPPublicKey signingPubKey : signingPubKeys) {
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID()); PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID())); PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID()); List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, true); addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, true);
} }
}
private void addSigningMethod(PGPSecretKeyRing secretKey, private void addSigningMethod(PGPSecretKeyRing secretKey,
PGPPrivateKey signingSubkey, PGPPrivateKey signingSubkey,

View file

@ -29,7 +29,6 @@ import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.PrimaryUserID; import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.openpgp.PGPKeyRing; import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
@ -116,7 +115,7 @@ public class KeyRingInfo {
* @return public key or null * @return public key or null
*/ */
public PGPPublicKey getPublicKey(long keyId) { public PGPPublicKey getPublicKey(long keyId) {
return keys.getPublicKey(keyId); return getPublicKey(keys, keyId);
} }
/** /**
@ -143,12 +142,36 @@ public class KeyRingInfo {
} }
if (publicKey == getPublicKey()) { if (publicKey == getPublicKey()) {
if (signatures.primaryKeyRevocation != null) {
if (SignatureUtils.isHardRevocation(signatures.primaryKeyRevocation)) {
return false;
}
}
return signatures.primaryKeyRevocation == null; return signatures.primaryKeyRevocation == null;
} }
PGPSignature binding = signatures.subkeyBindings.get(keyId); PGPSignature binding = signatures.subkeyBindings.get(keyId);
PGPSignature revocation = signatures.subkeyRevocations.get(keyId); PGPSignature revocation = signatures.subkeyRevocations.get(keyId);
return binding != null && revocation == null;
// No valid binding
if (binding == null || SignatureUtils.isSignatureExpired(binding)) {
return false;
}
// Revocation
if (revocation != null) {
if (SignatureUtils.isHardRevocation(revocation)) {
// Subkey is hard revoked
return false;
} else {
if (!SignatureUtils.isSignatureExpired(revocation) && revocation.getCreationTime().after(binding.getCreationTime())) {
// Key is soft-revoked, not yet re-bound
return false;
}
}
}
return true;
} }
/** /**
@ -322,11 +345,13 @@ public class KeyRingInfo {
} }
/** /**
* Return the current direct-key self signature. * Return the latest direct-key self signature.
* *
* @return * Note: This signature might be expired (check with {@link SignatureUtils#isSignatureExpired(PGPSignature)}).
*
* @return latest direct key self-signature
*/ */
public PGPSignature getCurrentDirectKeySelfSignature() { public PGPSignature getLatestDirectKeySelfSignature() {
return signatures.primaryKeySelfSignature; return signatures.primaryKeySelfSignature;
} }
@ -334,7 +359,7 @@ public class KeyRingInfo {
return signatures.primaryKeyRevocation; return signatures.primaryKeyRevocation;
} }
public PGPSignature getCurrentUserIdCertification(String userId) { public PGPSignature getLatestUserIdCertification(String userId) {
return signatures.userIdCertifications.get(userId); return signatures.userIdCertifications.get(userId);
} }
@ -353,27 +378,28 @@ public class KeyRingInfo {
public List<KeyFlag> getKeyFlagsOf(long keyId) { public List<KeyFlag> getKeyFlagsOf(long keyId) {
if (getPublicKey().getKeyID() == keyId) { if (getPublicKey().getKeyID() == keyId) {
PGPSignature directKeySignature = getCurrentDirectKeySelfSignature(); PGPSignature directKeySignature = getLatestDirectKeySelfSignature();
if (directKeySignature != null) { if (directKeySignature != null) {
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(directKeySignature); List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(directKeySignature);
if (flags != null) { if (keyFlags != null) {
return KeyFlag.fromBitmask(flags.getFlags()); return keyFlags;
} }
} }
String primaryUserId = getPrimaryUserId(); String primaryUserId = getPrimaryUserId();
if (primaryUserId != null) { if (primaryUserId != null) {
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(getCurrentUserIdCertification(primaryUserId)); PGPSignature userIdSignature = getLatestUserIdCertification(primaryUserId);
if (flags != null) { List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(userIdSignature);
return KeyFlag.fromBitmask(flags.getFlags()); if (keyFlags != null) {
return keyFlags;
} }
} }
} else { } else {
PGPSignature bindingSignature = getCurrentSubkeyBindingSignature(keyId); PGPSignature bindingSignature = getCurrentSubkeyBindingSignature(keyId);
if (bindingSignature != null) { if (bindingSignature != null) {
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(bindingSignature); List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(bindingSignature);
if (flags != null) { if (keyFlags != null) {
return KeyFlag.fromBitmask(flags.getFlags()); return keyFlags;
} }
} }
} }
@ -385,14 +411,14 @@ public class KeyRingInfo {
return Collections.emptyList(); return Collections.emptyList();
} }
PGPSignature userIdCertification = getCurrentUserIdCertification(userId); PGPSignature userIdCertification = getLatestUserIdCertification(userId);
if (userIdCertification == null) { if (userIdCertification == null) {
return Collections.emptyList(); throw new AssertionError("While user-id '" + userId + "' was reported as valid, there appears to be no certification for it.");
} }
KeyFlags keyFlags = SignatureSubpacketsUtil.getKeyFlags(userIdCertification); List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(userIdCertification);
if (keyFlags != null) { if (keyFlags != null) {
return KeyFlag.fromBitmask(keyFlags.getFlags()); return keyFlags;
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@ -428,7 +454,7 @@ public class KeyRingInfo {
private PGPSignature getMostRecentSignature() { private PGPSignature getMostRecentSignature() {
Set<PGPSignature> allSignatures = new HashSet<>(); Set<PGPSignature> allSignatures = new HashSet<>();
PGPSignature mostRecentSelfSignature = getCurrentDirectKeySelfSignature(); PGPSignature mostRecentSelfSignature = getLatestDirectKeySelfSignature();
PGPSignature revocationSelfSignature = getRevocationSelfSignature(); PGPSignature revocationSelfSignature = getRevocationSelfSignature();
if (mostRecentSelfSignature != null) allSignatures.add(mostRecentSelfSignature); if (mostRecentSelfSignature != null) allSignatures.add(mostRecentSelfSignature);
if (revocationSelfSignature != null) allSignatures.add(revocationSelfSignature); if (revocationSelfSignature != null) allSignatures.add(revocationSelfSignature);
@ -462,12 +488,12 @@ public class KeyRingInfo {
*/ */
public Date getPrimaryKeyExpirationDate() { public Date getPrimaryKeyExpirationDate() {
Date lastExpiration = null; Date lastExpiration = null;
if (getCurrentDirectKeySelfSignature() != null) { if (getLatestDirectKeySelfSignature() != null) {
lastExpiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), getCurrentDirectKeySelfSignature()); lastExpiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), getLatestDirectKeySelfSignature());
} }
for (String userId : getValidUserIds()) { for (String userId : getValidUserIds()) {
PGPSignature signature = getCurrentUserIdCertification(userId); PGPSignature signature = getLatestUserIdCertification(userId);
Date expiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), signature); Date expiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), signature);
if (expiration != null && (lastExpiration == null || expiration.after(lastExpiration))) { if (expiration != null && (lastExpiration == null || expiration.after(lastExpiration))) {
lastExpiration = expiration; lastExpiration = expiration;
@ -543,8 +569,9 @@ public class KeyRingInfo {
return false; return false;
} }
public PGPPublicKey getEncryptionSubkey(EncryptionStream.Purpose purpose) { public List<PGPPublicKey> getEncryptionSubkeys(EncryptionStream.Purpose purpose) {
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys(); Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
List<PGPPublicKey> encryptionKeys = new ArrayList<>();
while (subkeys.hasNext()) { while (subkeys.hasNext()) {
PGPPublicKey subKey = subkeys.next(); PGPPublicKey subKey = subkeys.next();
@ -560,36 +587,37 @@ public class KeyRingInfo {
switch (purpose) { switch (purpose) {
case COMMUNICATIONS: case COMMUNICATIONS:
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS)) { if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS)) {
return subKey; encryptionKeys.add(subKey);
} }
break; break;
case STORAGE: case STORAGE:
if (keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) { if (keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
return subKey; encryptionKeys.add(subKey);
} }
break; break;
case STORAGE_AND_COMMUNICATIONS: case STORAGE_AND_COMMUNICATIONS:
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) { if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
return subKey; encryptionKeys.add(subKey);
} }
break; break;
} }
} }
return null; return encryptionKeys;
} }
public PGPPublicKey getEncryptionSubkey(String userId, EncryptionStream.Purpose purpose) { public List<PGPPublicKey> getEncryptionSubkeys(String userId, EncryptionStream.Purpose purpose) {
if (userId != null) { if (userId != null) {
if (!isUserIdValid(userId)) { if (!isUserIdValid(userId)) {
throw new KeyValidationException(userId, getCurrentUserIdCertification(userId), getUserIdRevocation(userId)); throw new KeyValidationException(userId, getLatestUserIdCertification(userId), getUserIdRevocation(userId));
} }
} }
return getEncryptionSubkey(purpose); return getEncryptionSubkeys(purpose);
} }
public PGPPublicKey getSigningSubkey() { public List<PGPPublicKey> getSigningSubkeys() {
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys(); Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
List<PGPPublicKey> signingKeys = new ArrayList<>();
while (subkeys.hasNext()) { while (subkeys.hasNext()) {
PGPPublicKey subKey = subkeys.next(); PGPPublicKey subKey = subkeys.next();
@ -599,16 +627,16 @@ public class KeyRingInfo {
List<KeyFlag> keyFlags = getKeyFlagsOf(subKey.getKeyID()); List<KeyFlag> keyFlags = getKeyFlagsOf(subKey.getKeyID());
if (keyFlags.contains(KeyFlag.SIGN_DATA)) { if (keyFlags.contains(KeyFlag.SIGN_DATA)) {
return subKey; signingKeys.add(subKey);
} }
} }
return null; return signingKeys;
} }
public List<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) { public List<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) {
PGPSignature signature = getCurrentUserIdCertification(userId == null ? getPrimaryUserId() : userId); PGPSignature signature = getLatestUserIdCertification(userId == null ? getPrimaryUserId() : userId);
if (signature == null) { if (signature == null) {
signature = getCurrentDirectKeySelfSignature(); signature = getLatestDirectKeySelfSignature();
} }
if (signature == null) { if (signature == null) {
signature = getCurrentSubkeyBindingSignature(keyID); signature = getCurrentSubkeyBindingSignature(keyID);

View file

@ -26,11 +26,12 @@ import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.encryption_signing.EncryptionOptions;
import org.pgpainless.encryption_signing.EncryptionResult; import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.key.WeirdKeys; import org.pgpainless.key.WeirdKeys;
import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.key.util.KeyRingUtils;
@ -46,23 +47,25 @@ public class TestTwoSubkeysEncryption {
* {@link WeirdKeys#TWO_CRYPT_SUBKEYS} is a key that has two subkeys which both carry the key flags * {@link WeirdKeys#TWO_CRYPT_SUBKEYS} is a key that has two subkeys which both carry the key flags
* {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS} and {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}. * {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS} and {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
* *
* This test makes sure that both subkeys are used for encryption. * This test verifies that {@link EncryptionOptions#addRecipient(PGPPublicKeyRing, EncryptionOptions.EncryptionKeySelector)}
* works properly, if {@link EncryptionOptions#encryptToAllCapableSubkeys()} is provided as argument.
* *
* @throws IOException not expected * @throws IOException not expected
* @throws PGPException not expected * @throws PGPException not expected
*/ */
@Test @Test
@Disabled("We may not want to encrypt to all enc capable subkeys.")
public void testEncryptsToBothSubkeys() throws IOException, PGPException { public void testEncryptsToBothSubkeys() throws IOException, PGPException {
PGPSecretKeyRing twoSuitableSubkeysKeyRing = WeirdKeys.getTwoCryptSubkeysKey(); PGPSecretKeyRing twoSuitableSubkeysKeyRing = WeirdKeys.getTwoCryptSubkeysKey();
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(twoSuitableSubkeysKeyRing); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(twoSuitableSubkeysKeyRing);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionStream.Purpose.STORAGE) EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionStream.Purpose.STORAGE)
.onOutputStream(out) .onOutputStream(out)
.toRecipient(publicKeys) .withOptions(
.and() ProducerOptions.encrypt(new EncryptionOptions(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS)
.doNotSign() .addRecipient(publicKeys, EncryptionOptions.encryptToAllCapableSubkeys())
.noArmor(); )
.setAsciiArmor(false)
);
Streams.pipeAll(getPlainIn(), encryptionStream); Streams.pipeAll(getPlainIn(), encryptionStream);
encryptionStream.close(); encryptionStream.close();