Add SymmetricKeyAlgorithmNegotiator

This commit is contained in:
Paul Schaub 2021-05-25 13:52:45 +02:00
parent 821a49576f
commit 412b0aa119
10 changed files with 426 additions and 194 deletions

View File

@ -0,0 +1,87 @@
/*
* 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.algorithm.negotiation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.policy.Policy;
/**
* Interface for symmetric key algorithm negotiation.
*/
public interface SymmetricKeyAlgorithmNegotiator {
/**
* Negotiate a symmetric encryption algorithm.
*
* @param policy algorithm policy
* @param override algorithm override (if not null, return this)
* @param keyPreferences list of preferences per key
* @return negotiated algorithm
*/
SymmetricKeyAlgorithm negotiate(Policy.SymmetricKeyAlgorithmPolicy policy, SymmetricKeyAlgorithm override, List<Set<SymmetricKeyAlgorithm>> keyPreferences);
static SymmetricKeyAlgorithmNegotiator byPopularity() {
return new SymmetricKeyAlgorithmNegotiator() {
@Override
public SymmetricKeyAlgorithm negotiate(Policy.SymmetricKeyAlgorithmPolicy policy, SymmetricKeyAlgorithm override, List<Set<SymmetricKeyAlgorithm>> preferences) {
if (override == SymmetricKeyAlgorithm.NULL) {
throw new IllegalArgumentException("Algorithm override cannot be NULL (plaintext).");
}
if (override != null) {
return override;
}
Map<SymmetricKeyAlgorithm, Integer> supportWeight = new LinkedHashMap<>();
for (Set<SymmetricKeyAlgorithm> keyPreferences : preferences) {
for (SymmetricKeyAlgorithm preferred : keyPreferences) {
if (supportWeight.containsKey(preferred)) {
supportWeight.put(preferred, supportWeight.get(preferred) + 1);
} else {
supportWeight.put(preferred, 1);
}
}
}
List<SymmetricKeyAlgorithm> scoreboard = new ArrayList<>(supportWeight.keySet());
// Sort scoreboard by descending popularity
Collections.sort(scoreboard, new Comparator<SymmetricKeyAlgorithm>() {
@Override
public int compare(SymmetricKeyAlgorithm t0, SymmetricKeyAlgorithm t1) {
return -supportWeight.get(t0).compareTo(supportWeight.get(t1));
}
});
for (SymmetricKeyAlgorithm mostWanted : scoreboard) {
if (policy.isAcceptable(mostWanted)) {
return mostWanted;
}
}
return policy.getDefaultSymmetricKeyAlgorithm();
}
};
}
}

View File

@ -0,0 +1,19 @@
/*
* 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.
*/
/**
* Classes related to algorithm negotiation.
*/
package org.pgpainless.algorithm.negotiation;

View File

@ -18,11 +18,8 @@ package org.pgpainless.encryption_signing;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPException;
@ -33,14 +30,12 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator;
import org.pgpainless.decryption_verification.OpenPgpMetadata;
import org.pgpainless.exception.KeyValidationException;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.info.KeyView;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.policy.Policy;
import org.pgpainless.util.Passphrase;
public class EncryptionBuilder implements EncryptionBuilderInterface {
@ -262,69 +257,22 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
/**
* Negotiate the {@link SymmetricKeyAlgorithm} used for message encryption.
* If the user chose to set an override ({@link EncryptionOptions#overrideEncryptionAlgorithm(SymmetricKeyAlgorithm)}, use that.
* Otherwise find an algorithm which is acceptable for all recipients.
* If no consensus can be reached, use {@link Policy.SymmetricKeyAlgorithmPolicy#getDefaultSymmetricKeyAlgorithm()}.
*
* @param encryptionOptions encryption options
* @return negotiated symmetric key algorithm
*/
public static SymmetricKeyAlgorithm negotiateSymmetricEncryptionAlgorithm(EncryptionOptions encryptionOptions) {
SymmetricKeyAlgorithm encryptionAlgorithmOverride = encryptionOptions.getEncryptionAlgorithmOverride();
if (encryptionAlgorithmOverride != null) {
return encryptionAlgorithmOverride;
}
Map<SymmetricKeyAlgorithm, Integer> supportWeight = new LinkedHashMap<>();
List<Set<SymmetricKeyAlgorithm>> preferences = new ArrayList<>();
for (SubkeyIdentifier key : encryptionOptions.getKeyViews().keySet()) {
KeyView keyView = encryptionOptions.getKeyViews().get(key);
for (SymmetricKeyAlgorithm preferred : keyView.getPreferredSymmetricKeyAlgorithms()) {
if (supportWeight.containsKey(preferred)) {
supportWeight.put(preferred, supportWeight.get(preferred) + 1);
} else {
supportWeight.put(preferred, 1);
}
}
preferences.add(encryptionOptions.getKeyViews().get(key).getPreferredSymmetricKeyAlgorithms());
}
List<SymmetricKeyAlgorithm> scoreboard = new ArrayList<>(supportWeight.keySet());
// Sort scoreboard by descending popularity
Collections.sort(scoreboard, new Comparator<SymmetricKeyAlgorithm>() {
@Override
public int compare(SymmetricKeyAlgorithm t0, SymmetricKeyAlgorithm t1) {
return -supportWeight.get(t0).compareTo(supportWeight.get(t1));
}
});
for (SymmetricKeyAlgorithm mostWanted : scoreboard) {
if (PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy().isAcceptable(mostWanted)) {
return mostWanted;
}
}
return PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy().getDefaultSymmetricKeyAlgorithm();
}
/**
* Negotiate the {@link HashAlgorithm} used for signatures.
*
* If we encrypt and sign, we look at the recipients keys to determine which algorithm to use.
* If we only sign, we look at the singing keys preferences instead.
*
* @param encryptionOptions encryption options (recipients keys)
* @param signingOptions signing options (signing keys)
* @return negotiated hash algorithm
*/
public static HashAlgorithm negotiateSignatureHashAlgorithm(EncryptionOptions encryptionOptions, SigningOptions signingOptions) {
HashAlgorithm hashAlgorithmOverride = signingOptions.getHashAlgorithmOverride();
if (hashAlgorithmOverride != null) {
return hashAlgorithmOverride;
}
// TODO: Negotiation
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
return SymmetricKeyAlgorithmNegotiator
.byPopularity()
.negotiate(
PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy(),
encryptionOptions.getEncryptionAlgorithmOverride(),
preferences);
}
public static CompressionAlgorithm negotiateCompressionAlgorithm(ProducerOptions producerOptions) {

View File

@ -32,7 +32,7 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.info.KeyView;
import org.pgpainless.key.info.KeyAccessor;
import org.pgpainless.util.Passphrase;
/**
@ -67,7 +67,7 @@ public class EncryptionOptions {
private final Set<PGPKeyEncryptionMethodGenerator> encryptionMethods = new LinkedHashSet<>();
private final Set<SubkeyIdentifier> encryptionKeys = new LinkedHashSet<>();
private final Map<SubkeyIdentifier, KeyRingInfo> keyRingInfo = new HashMap<>();
private final Map<SubkeyIdentifier, KeyView> keyViews = new HashMap<>();
private final Map<SubkeyIdentifier, KeyAccessor> keyViews = new HashMap<>();
private final EncryptionKeySelector encryptionKeySelector = encryptToFirstSubkey();
private SymmetricKeyAlgorithm encryptionAlgorithmOverride = null;
@ -135,7 +135,7 @@ public class EncryptionOptions {
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
SubkeyIdentifier keyId = new SubkeyIdentifier(key, encryptionSubkey.getKeyID());
keyRingInfo.put(keyId, info);
keyViews.put(keyId, new KeyView.ViaUserId(info, keyId, userId));
keyViews.put(keyId, new KeyAccessor.ViaUserId(info, keyId, userId));
addRecipientKey(key, encryptionSubkey);
}
@ -169,7 +169,7 @@ public class EncryptionOptions {
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
SubkeyIdentifier keyId = new SubkeyIdentifier(key, encryptionSubkey.getKeyID());
keyRingInfo.put(keyId, info);
keyViews.put(keyId, new KeyView.ViaKeyId(info, keyId));
keyViews.put(keyId, new KeyAccessor.ViaKeyId(info, keyId));
addRecipientKey(key, encryptionSubkey);
}
@ -224,7 +224,7 @@ public class EncryptionOptions {
return new HashSet<>(encryptionKeys);
}
public Map<SubkeyIdentifier, KeyView> getKeyViews() {
public Map<SubkeyIdentifier, KeyAccessor> getKeyViews() {
return new HashMap<>(keyViews);
}

View File

@ -140,7 +140,7 @@ public final class EncryptionStream extends OutputStream {
}
private void prepareCompression() throws IOException {
CompressionAlgorithm compressionAlgorithm = options.getCompressionAlgorithmOverride();
CompressionAlgorithm compressionAlgorithm = EncryptionBuilder.negotiateCompressionAlgorithm(options);
resultBuilder.setCompressionAlgorithm(compressionAlgorithm);
compressedDataGenerator = new PGPCompressedDataGenerator(
compressionAlgorithm.getAlgorithmId());

View File

@ -20,6 +20,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
@ -28,6 +29,7 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.exception.KeyValidationException;
@ -35,9 +37,13 @@ import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.policy.Policy;
public final class SigningOptions {
/**
* A method of signing.
*/
public static final class SigningMethod {
private final PGPSignatureGenerator signatureGenerator;
private final boolean detached;
@ -47,10 +53,25 @@ public final class SigningOptions {
this.detached = detached;
}
/**
* Inline-signature method.
* The resulting signature will be written into the message itself, together with a one-pass-signature packet.
*
* @param signatureGenerator signature generator
* @return inline signing method
*/
public static SigningMethod inlineSignature(PGPSignatureGenerator signatureGenerator) {
return new SigningMethod(signatureGenerator, false);
}
/**
* Detached signing method.
* The resulting signature will not be added to the message, and instead can be distributed separately
* to the signed message.
*
* @param signatureGenerator signature generator
* @return detached signing method
*/
public static SigningMethod detachedSignature(PGPSignatureGenerator signatureGenerator) {
return new SigningMethod(signatureGenerator, true);
}
@ -64,20 +85,47 @@ public final class SigningOptions {
}
}
private Map<SubkeyIdentifier, SigningMethod> signingMethods = new HashMap<>();
private final Map<SubkeyIdentifier, SigningMethod> signingMethods = new HashMap<>();
private HashAlgorithm hashAlgorithmOverride;
public void addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
DocumentSignatureType signatureType)
/**
* Add an inline-signature.
* Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use
* of one-pass-signature packets.
*
* @param secretKeyDecryptor decryptor to unlock the signing secret key
* @param secretKey signing key
* @param signatureType type of signature (binary, canonical text)
* @throws KeyValidationException if something is wrong with the key
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
* @return this
*/
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
DocumentSignatureType signatureType)
throws KeyValidationException, PGPException {
addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType);
return addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType);
}
public void addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
String userId,
DocumentSignatureType signatureType)
/**
* Add an inline-signature.
* Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use
* of one-pass-signature packets.
*
* This method uses the passed in user-id to select user-specific hash algorithms.
*
* @param secretKeyDecryptor decryptor to unlock the signing secret key
* @param secretKey signing key
* @param userId user-id of the signer
* @param signatureType signature type (binary, canonical text)
* @return this
* @throws KeyValidationException if the key is invalid
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
*/
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
String userId,
DocumentSignatureType signatureType)
throws KeyValidationException, PGPException {
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
if (userId != null) {
@ -94,22 +142,52 @@ public final class SigningOptions {
for (PGPPublicKey signingPubKey : signingPubKeys) {
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, false);
Set<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
addSigningMethod(secretKey, signingSubkey, hashAlgorithm, signatureType, false);
}
return this;
}
public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
DocumentSignatureType signatureType)
/**
* Create a detached signature.
* Detached signatures are not being added into the PGP message itself.
* Instead they can be distributed separately to the message.
* Detached signatures are useful if the data that is being signed shall not be modified (eg. when signing a file).
*
* @param secretKeyDecryptor decryptor to unlock the secret signing key
* @param secretKey signing key
* @param signatureType type of data that is signed (binary, canonical text)
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created
* @return this
*/
public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
DocumentSignatureType signatureType)
throws PGPException {
addDetachedSignature(secretKeyDecryptor, secretKey, null, signatureType);
return addDetachedSignature(secretKeyDecryptor, secretKey, null, signatureType);
}
public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
String userId,
DocumentSignatureType signatureType)
/**
* Create a detached signature.
* Detached signatures are not being added into the PGP message itself.
* Instead they can be distributed separately to the message.
* Detached signatures are useful if the data that is being signed shall not be modified (eg. when signing a file).
*
* This method uses the passed in user-id to select user-specific hash algorithms.
*
* @param secretKeyDecryptor decryptor to unlock the secret signing key
* @param secretKey signing key
* @param userId user-id
* @param signatureType type of data that is signed (binary, canonical text)
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created
* @return this
*/
public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
PGPSecretKeyRing secretKey,
String userId,
DocumentSignatureType signatureType)
throws PGPException {
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
if (userId != null) {
@ -126,9 +204,12 @@ public final class SigningOptions {
for (PGPPublicKey signingPubKey : signingPubKeys) {
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, true);
Set<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
addSigningMethod(secretKey, signingSubkey, hashAlgorithm, signatureType, true);
}
return this;
}
private void addSigningMethod(PGPSecretKeyRing secretKey,
@ -143,6 +224,37 @@ public final class SigningOptions {
signingMethods.put(signingKeyIdentifier, signingMethod);
}
/**
* Negotiate, which hash algorithm to use.
*
* This method gives highest priority to the algorithm override, which can be set via {@link #overrideHashAlgorithm(HashAlgorithm)}.
* After that, the signing keys hash algorithm preferences are iterated to find the first acceptable algorithm.
* Lastly, should no acceptable algorithm be found, the {@link Policy Policies} default signature hash algorithm is
* used as a fallback.
*
* @param preferences preferences
* @param policy policy
* @return selected hash algorithm
*/
private HashAlgorithm negotiateHashAlgorithm(Set<HashAlgorithm> preferences, Policy policy) {
if (hashAlgorithmOverride != null) {
return hashAlgorithmOverride;
}
HashAlgorithm algorithm = policy.getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
if (preferences.isEmpty()) {
return algorithm;
}
for (HashAlgorithm pref : preferences) {
if (policy.getSignatureHashAlgorithmPolicy().isAcceptable(pref)) {
return pref;
}
}
return algorithm;
}
private PGPSignatureGenerator createSignatureGenerator(PGPPrivateKey privateKey,
HashAlgorithm hashAlgorithm,
DocumentSignatureType signatureType)
@ -156,15 +268,37 @@ public final class SigningOptions {
return signatureGenerator;
}
/**
* Return a map of key-ids and signing methods.
* For internal use.
*
* @return signing methods
*/
public Map<SubkeyIdentifier, SigningMethod> getSigningMethods() {
return Collections.unmodifiableMap(signingMethods);
}
/**
* Override hash algorithm negotiation by dictating which hash algorithm needs to be used.
* If no override has been set, an accetable algorithm will be negotiated instead.
*
* Note: To override the hash algorithm for signing, call this method *before* calling
* {@link #addInlineSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)} or
* {@link #addDetachedSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)}.
*
* @param hashAlgorithmOverride override hash algorithm
* @return this
*/
public SigningOptions overrideHashAlgorithm(HashAlgorithm hashAlgorithmOverride) {
this.hashAlgorithmOverride = hashAlgorithmOverride;
return this;
}
/**
* Return the hash algorithm override (or null if no override is set).
*
* @return hash algorithm override
*/
public HashAlgorithm getHashAlgorithmOverride() {
return hashAlgorithmOverride;
}

View File

@ -0,0 +1,136 @@
/*
* 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.key.info;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
public abstract class KeyAccessor {
protected final KeyRingInfo info;
protected final SubkeyIdentifier key;
public KeyAccessor(KeyRingInfo info, SubkeyIdentifier key) {
this.info = info;
this.key = key;
}
/**
* Depending on the way we address the key (key-id or user-id), return the respective {@link PGPSignature}
* which contains the algorithm preferences we are going to use.
*
* If we address a key via its user-id, we want to rely on the algorithm preferences in the user-id certification,
* while we would instead rely on those in the direct-key signature if we'd address the key by key-id.
*
* @return signature
*/
public abstract @Nonnull PGPSignature getSignatureWithPreferences();
/**
* Return preferred symmetric key encryption algorithms.
*
* @return preferred symmetric algorithms
*/
public Set<SymmetricKeyAlgorithm> getPreferredSymmetricKeyAlgorithms() {
return SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(getSignatureWithPreferences());
}
/**
* Return preferred hash algorithms.
*
* @return preferred hash algorithms
*/
public Set<HashAlgorithm> getPreferredHashAlgorithms() {
return SignatureSubpacketsUtil.parsePreferredHashAlgorithms(getSignatureWithPreferences());
}
/**
* Return preferred compression algorithms.
*
* @return preferred compression algorithms
*/
public Set<CompressionAlgorithm> getPreferredCompressionAlgorithms() {
return SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(getSignatureWithPreferences());
}
/**
* Address the key via a user-id (eg "Alice &lt;alice@wonderland.lit&gt;).
* In this case we are sourcing preferred algorithms from the user-id certification first.
*/
public static class ViaUserId extends KeyAccessor {
private final String userId;
/**
* Access a key via user-id.
*
* @param info info about a key at a given date
* @param key id of the subkey
* @param userId user-id
*/
public ViaUserId(KeyRingInfo info, SubkeyIdentifier key, String userId) {
super(info, key);
this.userId = userId;
}
@Override
public @Nonnull PGPSignature getSignatureWithPreferences() {
PGPSignature signature = info.getLatestUserIdCertification(userId);
if (signature != null) {
return signature;
}
throw new IllegalStateException("No valid user-id certification signature found for '" + userId + "'.");
}
}
/**
* Address the key via key-id.
* In this case we are sourcing preferred algorithms from the keys direct-key signature first.
*/
public static class ViaKeyId extends KeyAccessor {
/**
* Address the key via key-id.
* @param info info about the key at a given date
* @param key key-id
*/
public ViaKeyId(KeyRingInfo info, SubkeyIdentifier key) {
super(info, key);
}
@Override
public @Nonnull PGPSignature getSignatureWithPreferences() {
PGPSignature signature = info.getLatestDirectKeySelfSignature();
if (signature != null) {
return signature;
}
signature = info.getLatestUserIdCertification(info.getPrimaryUserId());
if (signature == null) {
throw new IllegalStateException("No valid signature found.");
}
return signature;
}
}
}

View File

@ -43,6 +43,7 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.exception.KeyValidationException;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.SignaturePicker;
import org.pgpainless.signature.SignatureUtils;
@ -633,18 +634,10 @@ public class KeyRingInfo {
return signingKeys;
}
public List<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) {
PGPSignature signature = getLatestUserIdCertification(userId == null ? getPrimaryUserId() : userId);
if (signature == null) {
signature = getLatestDirectKeySelfSignature();
}
if (signature == null) {
signature = getCurrentSubkeyBindingSignature(keyID);
}
if (signature == null) {
throw new IllegalStateException("No valid signature.");
}
return SignatureSubpacketsUtil.parsePreferredHashAlgorithms(signature);
public Set<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) {
KeyAccessor keyAccessor = userId == null ? new KeyAccessor.ViaKeyId(this, new SubkeyIdentifier(keys, keyID))
: new KeyAccessor.ViaUserId(this, new SubkeyIdentifier(keys, keyID), userId);
return keyAccessor.getPreferredHashAlgorithms();
}
public static class Signatures {

View File

@ -1,87 +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.key.info;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
public abstract class KeyView {
protected final KeyRingInfo info;
protected final SubkeyIdentifier key;
public KeyView(KeyRingInfo info, SubkeyIdentifier key) {
this.info = info;
this.key = key;
}
public abstract PGPSignature getSignatureWithPreferences();
public List<SymmetricKeyAlgorithm> getPreferredSymmetricKeyAlgorithms() {
List<SymmetricKeyAlgorithm> algos = SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(getSignatureWithPreferences());
return new ArrayList<>(new LinkedHashSet<>(algos)); // remove duplicates
}
public List<HashAlgorithm> getPreferredHashAlgorithms() {
List<HashAlgorithm> algos = SignatureSubpacketsUtil.parsePreferredHashAlgorithms(getSignatureWithPreferences());
return new ArrayList<>(new LinkedHashSet<>(algos)); // remove duplicates
}
public List<CompressionAlgorithm> getPreferredCompressionAlgorithms() {
List<CompressionAlgorithm> algos = SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(getSignatureWithPreferences());
return new ArrayList<>(new LinkedHashSet<>(algos)); // remove duplicates
}
public static class ViaUserId extends KeyView {
private final String userId;
public ViaUserId(KeyRingInfo info, SubkeyIdentifier key, String userId) {
super(info, key);
this.userId = userId;
}
@Override
public PGPSignature getSignatureWithPreferences() {
return info.getLatestUserIdCertification(userId);
}
}
public static class ViaKeyId extends KeyView {
public ViaKeyId(KeyRingInfo info, SubkeyIdentifier key) {
super(info, key);
}
@Override
public PGPSignature getSignatureWithPreferences() {
PGPSignature signature = info.getLatestDirectKeySelfSignature();
if (signature != null) {
return signature;
}
return info.getLatestUserIdCertification(info.getPrimaryUserId());
}
}
}

View File

@ -18,7 +18,9 @@ package org.pgpainless.signature.subpackets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
@ -206,8 +208,8 @@ public class SignatureSubpacketsUtil {
return hashed(signature, SignatureSubpacket.preferredSymmetricAlgorithms);
}
public static List<SymmetricKeyAlgorithm> parsePreferredSymmetricKeyAlgorithms(PGPSignature signature) {
List<SymmetricKeyAlgorithm> algorithms = new ArrayList<>();
public static Set<SymmetricKeyAlgorithm> parsePreferredSymmetricKeyAlgorithms(PGPSignature signature) {
Set<SymmetricKeyAlgorithm> algorithms = new LinkedHashSet<>();
PreferredAlgorithms preferences = getPreferredSymmetricAlgorithms(signature);
if (preferences != null) {
for (int code : preferences.getPreferences()) {
@ -227,8 +229,8 @@ public class SignatureSubpacketsUtil {
return hashed(signature, SignatureSubpacket.preferredHashAlgorithms);
}
public static List<HashAlgorithm> parsePreferredHashAlgorithms(PGPSignature signature) {
List<HashAlgorithm> algorithms = new ArrayList<>();
public static Set<HashAlgorithm> parsePreferredHashAlgorithms(PGPSignature signature) {
Set<HashAlgorithm> algorithms = new LinkedHashSet<>();
PreferredAlgorithms preferences = getPreferredHashAlgorithms(signature);
if (preferences != null) {
for (int code : preferences.getPreferences()) {
@ -248,8 +250,8 @@ public class SignatureSubpacketsUtil {
return hashed(signature, SignatureSubpacket.preferredCompressionAlgorithms);
}
public static List<CompressionAlgorithm> parsePreferredCompressionAlgorithms(PGPSignature signature) {
List<CompressionAlgorithm> algorithms = new ArrayList<>();
public static Set<CompressionAlgorithm> parsePreferredCompressionAlgorithms(PGPSignature signature) {
Set<CompressionAlgorithm> algorithms = new LinkedHashSet<>();
PreferredAlgorithms preferences = getPreferredCompressionAlgorithms(signature);
if (preferences != null) {
for (int code : preferences.getPreferences()) {