mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-27 13:28:00 +01:00
Introduce HashAlgorithmNegotiator
This commit is contained in:
parent
3f31b076dd
commit
a8998f27ad
4 changed files with 67 additions and 41 deletions
|
@ -0,0 +1,37 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.algorithm.negotiation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.policy.Policy;
|
||||
|
||||
public interface HashAlgorithmNegotiator {
|
||||
|
||||
HashAlgorithm negotiateHashAlgorithm(Set<HashAlgorithm> orderedHashAlgorithmPreferencesSet);
|
||||
|
||||
static HashAlgorithmNegotiator negotiateSignatureHashAlgorithm(Policy policy) {
|
||||
return negotiateByPolicy(policy.getSignatureHashAlgorithmPolicy());
|
||||
}
|
||||
|
||||
static HashAlgorithmNegotiator negotiateRevocationSignatureAlgorithm(Policy policy) {
|
||||
return negotiateByPolicy(policy.getRevocationSignatureHashAlgorithmPolicy());
|
||||
}
|
||||
|
||||
static HashAlgorithmNegotiator negotiateByPolicy(Policy.HashAlgorithmPolicy hashAlgorithmPolicy) {
|
||||
return new HashAlgorithmNegotiator() {
|
||||
@Override
|
||||
public HashAlgorithm negotiateHashAlgorithm(Set<HashAlgorithm> orderedPreferencesSet) {
|
||||
for (HashAlgorithm preference : orderedPreferencesSet) {
|
||||
if (hashAlgorithmPolicy.isAcceptable(preference)) {
|
||||
return preference;
|
||||
}
|
||||
}
|
||||
return hashAlgorithmPolicy.defaultHashAlgorithm();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
|||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.DocumentSignatureType;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
||||
import org.pgpainless.exception.KeyCannotSignException;
|
||||
import org.pgpainless.exception.KeyValidationError;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
|
@ -270,7 +271,7 @@ public final class SigningOptions {
|
|||
/**
|
||||
* Negotiate, which hash algorithm to use.
|
||||
*
|
||||
* This method gives highest priority to the algorithm override, which can be set via {@link #overrideHashAlgorithm(HashAlgorithm)}.
|
||||
* This method gives the 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.
|
||||
|
@ -284,18 +285,8 @@ public final class SigningOptions {
|
|||
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;
|
||||
return HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(policy)
|
||||
.negotiateHashAlgorithm(preferences);
|
||||
}
|
||||
|
||||
private PGPSignatureGenerator createSignatureGenerator(PGPPrivateKey privateKey,
|
||||
|
|
|
@ -8,7 +8,9 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
|
@ -23,7 +25,6 @@ public final class OpenPgpKeyAttributeUtil {
|
|||
|
||||
public static List<HashAlgorithm> getPreferredHashAlgorithms(PGPPublicKey publicKey) {
|
||||
List<HashAlgorithm> hashAlgorithms = new ArrayList<>();
|
||||
// TODO: I'd assume that we have to use publicKey.getKeySignatures() here, but that is empty...
|
||||
Iterator<?> keySignatures = publicKey.getSignatures();
|
||||
while (keySignatures.hasNext()) {
|
||||
PGPSignature signature = (PGPSignature) keySignatures.next();
|
||||
|
@ -44,8 +45,6 @@ public final class OpenPgpKeyAttributeUtil {
|
|||
hashAlgorithms.add(HashAlgorithm.fromId(h));
|
||||
}
|
||||
// Exit the loop after the first key signature with hash algorithms.
|
||||
// TODO: Find out, if it is possible that there are multiple key signatures which specify preferred
|
||||
// algorithms and how to deal with that.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -87,4 +86,21 @@ public final class OpenPgpKeyAttributeUtil {
|
|||
}
|
||||
return Collections.singletonList(hashAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to extract hash algorithm preferences from self signatures.
|
||||
* If no self-signature containing hash algorithm preferences is found,
|
||||
* try to derive a hash algorithm preference by inspecting the hash algorithm used by existing
|
||||
* self-signatures.
|
||||
*
|
||||
* @param publicKey key
|
||||
* @return hash algorithm preferences (might be empty!)
|
||||
*/
|
||||
public static Set<HashAlgorithm> getOrGuessPreferredHashAlgorithms(PGPPublicKey publicKey) {
|
||||
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey);
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
||||
}
|
||||
return new LinkedHashSet<>(preferredHashAlgorithms);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import java.io.InputStream;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.bcpg.sig.IssuerKeyID;
|
||||
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
|
||||
|
@ -30,6 +32,7 @@ import org.bouncycastle.util.encoders.Hex;
|
|||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.SignatureType;
|
||||
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
||||
|
@ -78,39 +81,18 @@ public final class SignatureUtils {
|
|||
* If no preferences can be derived, the key will fall back to the default hash algorithm as set in
|
||||
* the {@link org.pgpainless.policy.Policy}.
|
||||
*
|
||||
* TODO: Move negotiation to negotiator class
|
||||
*
|
||||
* @param publicKey public key
|
||||
* @return content signer builder
|
||||
*/
|
||||
private static PGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPPublicKey publicKey) {
|
||||
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey);
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
||||
}
|
||||
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms);
|
||||
Set<HashAlgorithm> hashAlgorithmSet = OpenPgpKeyAttributeUtil.getOrGuessPreferredHashAlgorithms(publicKey);
|
||||
|
||||
HashAlgorithm hashAlgorithm = HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy())
|
||||
.negotiateHashAlgorithm(hashAlgorithmSet);
|
||||
|
||||
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(publicKey.getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiate an acceptable hash algorithm from the provided list of options.
|
||||
* Acceptance of hash algorithms can be changed by setting a custom {@link Policy}.
|
||||
*
|
||||
* @param preferredHashAlgorithms list of preferred hash algorithms of a key
|
||||
* @return first acceptable algorithm, or policies default hash algorithm
|
||||
*/
|
||||
private static HashAlgorithm negotiateHashAlgorithm(List<HashAlgorithm> preferredHashAlgorithms) {
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
for (HashAlgorithm option : preferredHashAlgorithms) {
|
||||
if (policy.getSignatureHashAlgorithmPolicy().isAcceptable(option)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the key expiration date value from the given signature.
|
||||
* If the signature does not carry a {@link KeyExpirationTime} subpacket, return null.
|
||||
|
|
Loading…
Reference in a new issue