From ce5f8990ef1cbbc36b05477ebe325d7f4040b2e9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 19 Feb 2021 19:51:44 +0100 Subject: [PATCH] Add HashAlgorithmPolicy and SymmetricKeyAlgorithmPolicy --- .../main/java/org/pgpainless/PGPainless.java | 1 + .../src/main/java/org/pgpainless/Policy.java | 56 ------ .../key/generation/KeyRingBuilder.java | 8 +- .../pgpainless/key/util/SignatureUtils.java | 22 ++- .../java/org/pgpainless/policy/Policy.java | 161 ++++++++++++++++++ .../org/pgpainless/policy/package-info.java | 19 +++ 6 files changed, 207 insertions(+), 60 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/Policy.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/policy/package-info.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 40b08891..8a1270e1 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -32,6 +32,7 @@ import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor; import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface; import org.pgpainless.key.parsing.KeyRingReader; +import org.pgpainless.policy.Policy; import org.pgpainless.symmetric_encryption.SymmetricEncryptorDecryptor; import org.pgpainless.util.Passphrase; diff --git a/pgpainless-core/src/main/java/org/pgpainless/Policy.java b/pgpainless-core/src/main/java/org/pgpainless/Policy.java deleted file mode 100644 index 3880f3ee..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/Policy.java +++ /dev/null @@ -1,56 +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; - -import org.pgpainless.algorithm.HashAlgorithm; -import org.pgpainless.algorithm.SymmetricKeyAlgorithm; - -public final class Policy { - - private static Policy INSTANCE; - - private HashAlgorithm signatureHashAlgorithm = HashAlgorithm.SHA512; - private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256; - - private Policy() { - } - - public static Policy getInstance() { - if (INSTANCE == null) { - INSTANCE = new Policy(); - } - return INSTANCE; - } - - public void setDefaultSignatureHashAlgorithm(HashAlgorithm hashAlgorithm) { - if (hashAlgorithm == null) { - throw new IllegalArgumentException("HashAlgorithm cannot be null."); - } - this.signatureHashAlgorithm = hashAlgorithm; - } - - public HashAlgorithm getDefaultSignatureHashAlgorithm() { - return signatureHashAlgorithm; - } - - public void setDefaultKeyEncryptionAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) { - this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; - } - - public SymmetricKeyAlgorithm getDefaultSymmetricKeyAlgorithm() { - return symmetricKeyAlgorithm; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java index 601ee995..4b5a750a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java @@ -50,6 +50,7 @@ import org.pgpainless.PGPainless; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.SignatureType; +import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.eddsa.EdDSACurve; @@ -420,16 +421,19 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { } private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) { + HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm(); return ImplementationFactory.getInstance().getPGPContentSignerBuilder( certKey.getPublicKey().getAlgorithm(), - PGPainless.getPolicy().getDefaultSignatureHashAlgorithm().getAlgorithmId()); + hashAlgorithm.getAlgorithmId()); } private PBESecretKeyEncryptor buildSecretKeyEncryptor() { + SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyAlgorithmPolicy() + .getDefaultSymmetricKeyAlgorithm(); PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ? null : // unencrypted key pair, otherwise AES-256 encrypted ImplementationFactory.getInstance().getPBESecretKeyEncryptor( - PGPainless.getPolicy().getDefaultSymmetricKeyAlgorithm(), digestCalculator, passphrase); + keyEncryptionAlgorithm, digestCalculator, passphrase); return encryptor; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java index d9934bf9..e3463f6d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java @@ -58,7 +58,7 @@ public class SignatureUtils { private static HashAlgorithm negotiateHashAlgorithm(List preferredHashAlgorithms) { if (preferredHashAlgorithms.isEmpty()) { - return PGPainless.getPolicy().getDefaultSignatureHashAlgorithm(); + return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm(); } return preferredHashAlgorithms.get(0); } @@ -100,9 +100,10 @@ public class SignatureUtils { case CASUAL_CERTIFICATION: case POSITIVE_CERTIFICATION: case DIRECT_KEY: + return isSelfSignatureValid(signature, issuer); case KEY_REVOCATION: case CERTIFICATION_REVOCATION: - return isSelfSignatureValid(signature, issuer); + return isRevocationSignatureValid(signature, issuer); case SUBKEY_BINDING: case PRIMARYKEY_BINDING: case SUBKEY_REVOCATION: @@ -117,6 +118,23 @@ public class SignatureUtils { } public static boolean isSelfSignatureValid(PGPSignature signature, PGPPublicKey publicKey) throws PGPException { + if (!PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().isAcceptable(signature.getHashAlgorithm())) { + return false; + } + for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { + String userId = it.next(); + boolean valid = isSelfSignatureOnUserIdValid(signature, userId, publicKey); + if (valid) { + return true; + } + } + return false; + } + + public static boolean isRevocationSignatureValid(PGPSignature signature, PGPPublicKey publicKey) throws PGPException { + if (!PGPainless.getPolicy().getRevocationSignatureHashAlgorithmPolicy().isAcceptable(signature.getHashAlgorithm())) { + return false; + } for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { String userId = it.next(); boolean valid = isSelfSignatureOnUserIdValid(signature, userId, publicKey); diff --git a/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java b/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java new file mode 100644 index 00000000..e2147b2b --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java @@ -0,0 +1,161 @@ +/* + * 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.policy; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.pgpainless.algorithm.HashAlgorithm; +import org.pgpainless.algorithm.SymmetricKeyAlgorithm; + +public final class Policy { + + private static Policy INSTANCE; + + private HashAlgorithmPolicy signatureHashAlgorithmPolicy = + HashAlgorithmPolicy.defaultSignatureAlgorithmPolicy(); + private HashAlgorithmPolicy revocationSignatureHashAlgorithmPolicy = + HashAlgorithmPolicy.defaultRevocationSignatureHashAlgorithmPolicy(); + private SymmetricKeyAlgorithmPolicy symmetricKeyAlgorithmPolicy = + SymmetricKeyAlgorithmPolicy.defaultSymmetricKeyAlgorithmPolicy(); + + private Policy() { + } + + public static Policy getInstance() { + if (INSTANCE == null) { + INSTANCE = new Policy(); + } + return INSTANCE; + } + + public HashAlgorithmPolicy getSignatureHashAlgorithmPolicy() { + return signatureHashAlgorithmPolicy; + } + + public void setSignatureHashAlgorithmPolicy(HashAlgorithmPolicy policy) { + if (policy == null) { + throw new NullPointerException("Policy cannot be null."); + } + this.signatureHashAlgorithmPolicy = policy; + } + + public HashAlgorithmPolicy getRevocationSignatureHashAlgorithmPolicy() { + return revocationSignatureHashAlgorithmPolicy; + } + + public void setRevocationSignatureHashAlgorithmPolicy(HashAlgorithmPolicy policy) { + if (policy == null) { + throw new NullPointerException("Policy cannot be null."); + } + this.revocationSignatureHashAlgorithmPolicy = policy; + } + + public SymmetricKeyAlgorithmPolicy getSymmetricKeyAlgorithmPolicy() { + return symmetricKeyAlgorithmPolicy; + } + + public void setSymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithmPolicy policy) { + if (policy == null) { + throw new NullPointerException("Policy cannot be null."); + } + this.symmetricKeyAlgorithmPolicy = policy; + } + + public static final class SymmetricKeyAlgorithmPolicy { + + private final SymmetricKeyAlgorithm defaultSymmetricKeyAlgorithm; + private final List acceptableSymmetricKeyAlgorithms; + + public SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm defaultSymmetricKeyAlgorithm, List acceptableSymmetricKeyAlgorithms) { + this.defaultSymmetricKeyAlgorithm = defaultSymmetricKeyAlgorithm; + this.acceptableSymmetricKeyAlgorithms = Collections.unmodifiableList(acceptableSymmetricKeyAlgorithms); + } + + public SymmetricKeyAlgorithm getDefaultSymmetricKeyAlgorithm() { + return defaultSymmetricKeyAlgorithm; + } + + public boolean isAcceptable(SymmetricKeyAlgorithm algorithm) { + return acceptableSymmetricKeyAlgorithms.contains(algorithm); + } + + public boolean isAcceptable(int algorithmId) { + SymmetricKeyAlgorithm algorithm = SymmetricKeyAlgorithm.fromId(algorithmId); + return isAcceptable(algorithm); + } + + public static SymmetricKeyAlgorithmPolicy defaultSymmetricKeyAlgorithmPolicy() { + return new SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm.AES_256, Arrays.asList( + SymmetricKeyAlgorithm.IDEA, + SymmetricKeyAlgorithm.CAST5, + SymmetricKeyAlgorithm.BLOWFISH, + SymmetricKeyAlgorithm.AES_128, + SymmetricKeyAlgorithm.AES_192, + SymmetricKeyAlgorithm.AES_256, + SymmetricKeyAlgorithm.TWOFISH, + SymmetricKeyAlgorithm.CAMELLIA_128, + SymmetricKeyAlgorithm.CAMELLIA_192, + SymmetricKeyAlgorithm.CAMELLIA_256 + )); + } + } + + public static final class HashAlgorithmPolicy { + + private final HashAlgorithm defaultHashAlgorithm; + private final List acceptableHashAlgorithms; + + public HashAlgorithmPolicy(HashAlgorithm defaultHashAlgorithm, List acceptableHashAlgorithms) { + this.defaultHashAlgorithm = defaultHashAlgorithm; + this.acceptableHashAlgorithms = Collections.unmodifiableList(acceptableHashAlgorithms); + } + + public HashAlgorithm defaultHashAlgorithm() { + return defaultHashAlgorithm; + } + + public boolean isAcceptable(HashAlgorithm hashAlgorithm) { + return acceptableHashAlgorithms.contains(hashAlgorithm); + } + + public boolean isAcceptable(int algorithmId) { + HashAlgorithm algorithm = HashAlgorithm.fromId(algorithmId); + return isAcceptable(algorithm); + } + + public static HashAlgorithmPolicy defaultSignatureAlgorithmPolicy() { + return new HashAlgorithmPolicy(HashAlgorithm.SHA512, Arrays.asList( + HashAlgorithm.SHA224, + HashAlgorithm.SHA256, + HashAlgorithm.SHA384, + HashAlgorithm.SHA512 + )); + } + + public static HashAlgorithmPolicy defaultRevocationSignatureHashAlgorithmPolicy() { + return new HashAlgorithmPolicy(HashAlgorithm.SHA512, Arrays.asList( + HashAlgorithm.RIPEMD160, + HashAlgorithm.SHA1, + HashAlgorithm.SHA224, + HashAlgorithm.SHA256, + HashAlgorithm.SHA384, + HashAlgorithm.SHA512 + )); + } + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/policy/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/policy/package-info.java new file mode 100644 index 00000000..34d001de --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/policy/package-info.java @@ -0,0 +1,19 @@ +/* + * 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. + */ +/** + * Policy regarding used algorithms. + */ +package org.pgpainless.policy;