1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-07 04:41:12 +01:00

Rework Policy to be immutable. Changes are now done by calling policy.copy().withXYZ().build()

This commit is contained in:
Paul Schaub 2025-03-16 22:01:59 +01:00
parent a1c19020c5
commit fe45ee12be
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
9 changed files with 192 additions and 94 deletions

View file

@ -59,10 +59,7 @@ public class EncryptDecryptTest {
@BeforeEach
public void setDefaultPolicy() {
PGPainless.getPolicy().setSymmetricKeyEncryptionAlgorithmPolicy(
Policy.SymmetricKeyAlgorithmPolicy.symmetricKeyEncryptionPolicy2022());
PGPainless.getPolicy().setSymmetricKeyDecryptionAlgorithmPolicy(
Policy.SymmetricKeyAlgorithmPolicy.symmetricKeyDecryptionPolicy2022());
PGPainless.getInstance().setAlgorithmPolicy(new Policy());
}
@TestTemplate

View file

@ -25,13 +25,13 @@ import org.pgpainless.util.NotationRegistry;
* can be rejected.
* Note, that PGPainless distinguishes between hash algorithms used in revocation and non-revocation signatures,
* and has different policies for those.
*
* <p>
* Furthermore, PGPainless has policies for symmetric encryption algorithms (both for encrypting and decrypting),
* for public key algorithms and key lengths, as well as compression algorithms.
*
* <p>
* The following examples show how these policies can be modified.
*
* PGPainless' policy is being accessed by calling {@link PGPainless#getPolicy()}.
* <p>
* PGPainless' policy is being accessed by calling {@link PGPainless#getAlgorithmPolicy()}.
* Custom sub-policies can be set by calling the setter methods of {@link Policy}.
*/
public class ManagePolicy {
@ -43,50 +43,29 @@ public class ManagePolicy {
@AfterEach
public void resetPolicy() {
// Policy for hash algorithms in non-revocation signatures
PGPainless.getPolicy().setCertificationSignatureHashAlgorithmPolicy(
Policy.HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy());
// Policy for hash algorithms in data signatures
PGPainless.getPolicy().setDataSignatureHashAlgorithmPolicy(
Policy.HashAlgorithmPolicy.static2022SignatureHashAlgorithmPolicy());
// Policy for hash algorithms in revocation signatures
PGPainless.getPolicy().setRevocationSignatureHashAlgorithmPolicy(
Policy.HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy());
// Policy for public key algorithms and bit lengths
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
// Policy for acceptable symmetric encryption algorithms when decrypting messages
PGPainless.getPolicy().setSymmetricKeyDecryptionAlgorithmPolicy(
Policy.SymmetricKeyAlgorithmPolicy.symmetricKeyDecryptionPolicy2022());
// Policy for acceptable symmetric encryption algorithms when encrypting messages
PGPainless.getPolicy().setSymmetricKeyEncryptionAlgorithmPolicy(
Policy.SymmetricKeyAlgorithmPolicy.symmetricKeyEncryptionPolicy2022());
// Policy for acceptable compression algorithms
PGPainless.getPolicy().setCompressionAlgorithmPolicy(
Policy.CompressionAlgorithmPolicy.anyCompressionAlgorithmPolicy());
// Known notations
PGPainless.getPolicy().getNotationRegistry().clear();
PGPainless.getInstance().setAlgorithmPolicy(new Policy());
}
/**
* {@link HashAlgorithm Hash Algorithms} may get outdated with time. {@link HashAlgorithm#SHA1} is a prominent
* example for an algorithm that is nowadays considered unsafe to use and which shall be avoided.
*
* <p>
* PGPainless comes with a {@link Policy} class that defines which algorithms are trustworthy and acceptable.
* It also allows the user to specify a custom policy tailored to their needs.
*
* <p>
* Per default, PGPainless will reject non-revocation signatures that use SHA-1 as hash algorithm.
* To inspect PGPainless' default signature hash algorithm policy, see
* {@link Policy.HashAlgorithmPolicy#static2022SignatureHashAlgorithmPolicy()}.
*
* <p>
* Since it may be a valid use-case to accept signatures made using SHA-1 as part of a less strict policy,
* this example demonstrates how to set a custom signature hash algorithm policy.
*/
@Test
public void setCustomSignatureHashPolicy() {
// Get PGPainless' policy singleton
Policy policy = PGPainless.getPolicy();
// Get PGPainless' policy
Policy oldPolicy = PGPainless.getInstance().getAlgorithmPolicy();
Policy.HashAlgorithmPolicy sigHashAlgoPolicy = policy.getDataSignatureHashAlgorithmPolicy();
Policy.HashAlgorithmPolicy sigHashAlgoPolicy = oldPolicy.getDataSignatureHashAlgorithmPolicy();
assertTrue(sigHashAlgoPolicy.isAcceptable(HashAlgorithm.SHA512));
// Per default, non-revocation signatures using SHA-1 are rejected
assertFalse(sigHashAlgoPolicy.isAcceptable(HashAlgorithm.SHA1));
@ -98,12 +77,17 @@ public class ManagePolicy {
// List of acceptable hash algorithms
Arrays.asList(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256, HashAlgorithm.SHA224, HashAlgorithm.SHA1));
// Set the hash algo policy as policy for non-revocation signatures
policy.setDataSignatureHashAlgorithmPolicy(customPolicy);
PGPainless.getInstance().setAlgorithmPolicy(
oldPolicy.copy().withDataSignatureHashAlgorithmPolicy(customPolicy).build()
);
sigHashAlgoPolicy = policy.getDataSignatureHashAlgorithmPolicy();
sigHashAlgoPolicy = PGPainless.getInstance().getAlgorithmPolicy().getDataSignatureHashAlgorithmPolicy();
assertTrue(sigHashAlgoPolicy.isAcceptable(HashAlgorithm.SHA512));
// SHA-1 is now acceptable as well
assertTrue(sigHashAlgoPolicy.isAcceptable(HashAlgorithm.SHA1));
// reset old policy
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
}
/**
@ -111,13 +95,13 @@ public class ManagePolicy {
* Per default, PGPainless will reject signatures made by keys of unacceptable algorithm or length.
* See {@link Policy.PublicKeyAlgorithmPolicy#bsi2021PublicKeyAlgorithmPolicy()}
* to inspect PGPainless' defaults.
*
* <p>
* This example demonstrates how to set a custom public key algorithm policy.
*/
@Test
public void setCustomPublicKeyAlgorithmPolicy() {
Policy policy = PGPainless.getPolicy();
Policy.PublicKeyAlgorithmPolicy pkAlgorithmPolicy = policy.getPublicKeyAlgorithmPolicy();
Policy oldPolicy = PGPainless.getInstance().getAlgorithmPolicy();
Policy.PublicKeyAlgorithmPolicy pkAlgorithmPolicy = oldPolicy.getPublicKeyAlgorithmPolicy();
assertTrue(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.RSA_GENERAL, 4096));
assertTrue(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.RSA_GENERAL, 2048));
assertFalse(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.RSA_GENERAL, 1024));
@ -131,27 +115,30 @@ public class ManagePolicy {
put(PublicKeyAlgorithm.RSA_GENERAL, 3000);
}}
);
policy.setPublicKeyAlgorithmPolicy(customPolicy);
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy.copy().withPublicKeyAlgorithmPolicy(customPolicy).build());
pkAlgorithmPolicy = policy.getPublicKeyAlgorithmPolicy();
pkAlgorithmPolicy = PGPainless.getInstance().getAlgorithmPolicy().getPublicKeyAlgorithmPolicy();
assertTrue(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.RSA_GENERAL, 4096));
// RSA 2048 is no longer acceptable
assertFalse(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.RSA_GENERAL, 2048));
// ECDSA is no longer acceptable, since it is no longer included in the policy at all
assertFalse(pkAlgorithmPolicy.isAcceptable(PublicKeyAlgorithm.ECDSA, 256));
// Reset policy
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
}
/**
* OpenPGP requires implementations to reject signatures which contain critical notation data subpackets
* which are not known to the implementation.
*
* <p>
* PGPainless allows the user to define which notations should be considered known notations.
* The following example demonstrates how to mark the notation value 'unknown@pgpainless.org' as known,
* such that signatures containing a critical notation with that name are no longer being invalidated because of it.
*/
@Test
public void manageKnownNotations() {
Policy policy = PGPainless.getPolicy();
Policy policy = PGPainless.getInstance().getAlgorithmPolicy();
NotationRegistry notationRegistry = policy.getNotationRegistry();
assertFalse(notationRegistry.isKnownNotation("unknown@pgpainless.org"));

View file

@ -22,9 +22,7 @@ public class GeneratingWeakKeyThrowsTest {
@Test
public void refuseToGenerateWeakPrimaryKeyTest() {
// ensure we have default public key algorithm policy set
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
PGPainless.getInstance().setAlgorithmPolicy(new Policy());
assertThrows(IllegalArgumentException.class, () ->
PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._1024),
@ -34,8 +32,7 @@ public class GeneratingWeakKeyThrowsTest {
@Test
public void refuseToAddWeakSubkeyDuringGenerationTest() {
// ensure we have default public key algorithm policy set
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
PGPainless.getInstance().setAlgorithmPolicy(new Policy());
KeyRingBuilder kb = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096),
@ -52,8 +49,10 @@ public class GeneratingWeakKeyThrowsTest {
Map<PublicKeyAlgorithm, Integer> bitStrengths = new HashMap<>();
bitStrengths.put(PublicKeyAlgorithm.RSA_GENERAL, 512);
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(
new Policy.PublicKeyAlgorithmPolicy(bitStrengths));
Policy oldPolicy = PGPainless.getPolicy();
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy.copy()
.withPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(bitStrengths))
.build());
PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096),
@ -64,7 +63,6 @@ public class GeneratingWeakKeyThrowsTest {
.build();
// reset public key algorithm policy
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
}
}

View file

@ -29,7 +29,11 @@ public class RefuseToAddWeakSubkeyTest {
@Test
public void testEditorRefusesToAddWeakSubkey() {
// ensure default policy is set
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
Policy oldPolicy = PGPainless.getPolicy();
Policy adjusted = oldPolicy.copy().withPublicKeyAlgorithmPolicy(
Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy()
).build();
PGPainless.getInstance().setAlgorithmPolicy(adjusted);
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice")
@ -39,6 +43,7 @@ public class RefuseToAddWeakSubkeyTest {
assertThrows(IllegalArgumentException.class, () ->
editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()));
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
}
@Test
@ -47,6 +52,8 @@ public class RefuseToAddWeakSubkeyTest {
.modernKeyRing("Alice")
.getPGPSecretKeyRing();
Policy oldPolicy = PGPainless.getPolicy();
// set weak policy
Map<PublicKeyAlgorithm, Integer> minimalBitStrengths = new EnumMap<>(PublicKeyAlgorithm.class);
// §5.4.1
@ -68,7 +75,9 @@ public class RefuseToAddWeakSubkeyTest {
minimalBitStrengths.put(PublicKeyAlgorithm.DIFFIE_HELLMAN, 2000);
// §7.2.2
minimalBitStrengths.put(PublicKeyAlgorithm.ECDH, 250);
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(minimalBitStrengths));
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy.copy()
.withPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(minimalBitStrengths))
.build());
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
KeySpec spec = KeySpec.getBuilder(KeyType.RSA(RsaLength._1024), KeyFlag.ENCRYPT_COMMS)
@ -81,6 +90,6 @@ public class RefuseToAddWeakSubkeyTest {
assertEquals(2, PGPainless.inspectKeyRing(secretKeys).getEncryptionSubkeys(EncryptionPurpose.ANY).size());
// reset default policy
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy(Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
PGPainless.getInstance().setAlgorithmPolicy(oldPolicy);
}
}

View file

@ -18,43 +18,43 @@ public class PolicySetterTest {
@Test
public void testSetCertificationSignatureHashAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setCertificationSignatureHashAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withCertificationSignatureHashAlgorithmPolicy(null));
}
@Test
public void testSetDataSignatureHashAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setDataSignatureHashAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withDataSignatureHashAlgorithmPolicy(null));
}
@Test
public void testSetRevocationSignatureHashAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setRevocationSignatureHashAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withRevocationSignatureHashAlgorithmPolicy(null));
}
@Test
public void testSetSymmetricKeyEncryptionAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setSymmetricKeyEncryptionAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withSymmetricKeyEncryptionAlgorithmPolicy(null));
}
@Test
public void testSetSymmetricKeyDecryptionAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setSymmetricKeyDecryptionAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withSymmetricKeyDecryptionAlgorithmPolicy(null));
}
@Test
public void testSetCompressionAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setCompressionAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withCompressionAlgorithmPolicy(null));
}
@Test
public void testSetPublicKeyAlgorithmPolicy_NullFails() {
Policy policy = Policy.getInstance();
assertThrows(NullPointerException.class, () -> policy.setPublicKeyAlgorithmPolicy(null));
assertThrows(NullPointerException.class, () -> policy.copy().withPublicKeyAlgorithmPolicy(null));
}
@Test
@ -62,7 +62,7 @@ public class PolicySetterTest {
Policy policy = new Policy();
Map<PublicKeyAlgorithm, Integer> acceptableAlgorithms = new HashMap<>();
acceptableAlgorithms.put(PublicKeyAlgorithm.RSA_GENERAL, 2000);
policy.setPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(acceptableAlgorithms));
policy = policy.copy().withPublicKeyAlgorithmPolicy(new Policy.PublicKeyAlgorithmPolicy(acceptableAlgorithms)).build();
// Policy does not contain ECDSA
assertFalse(policy.getPublicKeyAlgorithmPolicy().isAcceptable(PublicKeyAlgorithm.ECDSA, 256));

View file

@ -28,23 +28,12 @@ public class PolicyTest {
@BeforeAll
public static void setup() {
policy = new Policy();
policy.setCompressionAlgorithmPolicy(new Policy.CompressionAlgorithmPolicy(CompressionAlgorithm.UNCOMPRESSED,
Arrays.asList(CompressionAlgorithm.ZIP, CompressionAlgorithm.ZLIB, CompressionAlgorithm.UNCOMPRESSED)));
policy.setSymmetricKeyEncryptionAlgorithmPolicy(new Policy.SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm.AES_256,
Arrays.asList(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128)));
policy.setSymmetricKeyDecryptionAlgorithmPolicy(new Policy.SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm.AES_256,
Arrays.asList(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128, SymmetricKeyAlgorithm.BLOWFISH)));
Map<HashAlgorithm, Date> sigHashAlgoMap = new HashMap<>();
sigHashAlgoMap.put(HashAlgorithm.SHA512, null);
sigHashAlgoMap.put(HashAlgorithm.SHA384, null);
sigHashAlgoMap.put(HashAlgorithm.SHA256, null);
sigHashAlgoMap.put(HashAlgorithm.SHA224, null);
sigHashAlgoMap.put(HashAlgorithm.SHA1, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC"));
policy.setCertificationSignatureHashAlgorithmPolicy(new Policy.HashAlgorithmPolicy(HashAlgorithm.SHA512, sigHashAlgoMap));
Map<HashAlgorithm, Date> revHashAlgoMap = new HashMap<>();
revHashAlgoMap.put(HashAlgorithm.SHA512, null);
@ -53,10 +42,26 @@ public class PolicyTest {
revHashAlgoMap.put(HashAlgorithm.SHA224, null);
revHashAlgoMap.put(HashAlgorithm.SHA1, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC"));
revHashAlgoMap.put(HashAlgorithm.RIPEMD160, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC"));
policy.setRevocationSignatureHashAlgorithmPolicy(new Policy.HashAlgorithmPolicy(HashAlgorithm.SHA512,
revHashAlgoMap));
policy.setPublicKeyAlgorithmPolicy(Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy());
policy = new Policy().copy()
.withCompressionAlgorithmPolicy(new Policy.CompressionAlgorithmPolicy(CompressionAlgorithm.UNCOMPRESSED,
Arrays.asList(CompressionAlgorithm.ZIP, CompressionAlgorithm.ZLIB, CompressionAlgorithm.UNCOMPRESSED)))
.withSymmetricKeyEncryptionAlgorithmPolicy(new Policy.SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm.AES_256,
Arrays.asList(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128)))
.withSymmetricKeyDecryptionAlgorithmPolicy(new Policy.SymmetricKeyAlgorithmPolicy(SymmetricKeyAlgorithm.AES_256,
Arrays.asList(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128, SymmetricKeyAlgorithm.BLOWFISH)))
.withCertificationSignatureHashAlgorithmPolicy(new Policy.HashAlgorithmPolicy(HashAlgorithm.SHA512, sigHashAlgoMap))
.withRevocationSignatureHashAlgorithmPolicy(new Policy.HashAlgorithmPolicy(HashAlgorithm.SHA512,
revHashAlgoMap))
.withPublicKeyAlgorithmPolicy(Policy.PublicKeyAlgorithmPolicy.bsi2021PublicKeyAlgorithmPolicy())
.build();
}
@Test