From 21f424551b10e328919ed07cb0b1e877f012b4a2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Sep 2021 19:20:19 +0200 Subject: [PATCH 1/7] Simplify KeySpecBuilder --- .../DecryptionStreamFactory.java | 2 +- .../key/generation/KeyRingBuilder.java | 32 ++- .../pgpainless/key/generation/KeySpec.java | 5 +- .../key/generation/KeySpecBuilder.java | 197 +++++++----------- .../generation/KeySpecBuilderInterface.java | 47 +---- .../java/org/pgpainless/policy/Policy.java | 24 ++- .../EncryptDecryptTest.java | 11 +- .../EncryptionOptionsTest.java | 17 +- .../org/pgpainless/example/GenerateKeys.java | 62 +++--- .../org/pgpainless/example/ModifyKeys.java | 5 +- .../BrainpoolKeyGenerationTest.java | 31 ++- ...rtificationKeyMustBeAbleToCertifyTest.java | 5 +- .../GenerateEllipticCurveKeyTest.java | 11 +- .../GenerateKeyWithAdditionalUserIdTest.java | 7 +- .../GenerateWithEmptyPassphrase.java | 7 +- .../key/generation/IllegalKeyFlagsTest.java | 31 +-- .../pgpainless/key/info/KeyRingInfoTest.java | 11 +- .../key/info/UserIdRevocationTest.java | 25 +-- .../key/modification/AddSubKeyTest.java | 4 +- .../org/pgpainless/policy/PolicyTest.java | 8 +- .../java/org/pgpainless/util/BCUtilTest.java | 11 +- .../util/GuessPreferredHashAlgorithmTest.java | 15 +- ...ncryptCommsStorageFlagsDifferentiated.java | 8 +- 23 files changed, 240 insertions(+), 336 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java index 1a6c27a3..b5d2000d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -384,7 +384,7 @@ public final class DecryptionStreamFactory { } private void throwIfAlgorithmIsRejected(SymmetricKeyAlgorithm algorithm) throws UnacceptableAlgorithmException { - if (!PGPainless.getPolicy().getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(algorithm)) { + if (!PGPainless.getPolicy().getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) { throw new UnacceptableAlgorithmException("Data is " + (algorithm == SymmetricKeyAlgorithm.NULL ? "unencrypted" : "encrypted with symmetric algorithm " + algorithm) + " which is not acceptable as per PGPainless' policy.\n" + "To mark this algorithm as acceptable, use PGPainless.getPolicy().setSymmetricKeyDecryptionAlgorithmPolicy()."); 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 33ee155d..230a0a9a 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 @@ -127,10 +127,9 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { public PGPSecretKeyRing simpleRsaKeyRing(@Nonnull String userId, @Nonnull RsaLength length, String password) throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { WithAdditionalUserIdOrPassphrase builder = this - .withPrimaryKey( - KeySpec.getBuilder(KeyType.RSA(length)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) + .withPrimaryKey(KeySpec + .getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) + .build()) .withPrimaryUserId(userId); if (password == null) { @@ -197,13 +196,11 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { WithAdditionalUserIdOrPassphrase builder = this .withSubKey( - KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) + KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) + .build()) .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) + KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .build()) .withPrimaryUserId(userId); if (password == null) { @@ -225,17 +222,14 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { WithAdditionalUserIdOrPassphrase builder = this .withSubKey( - KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) + KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) + .build()) .withSubKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) + KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA) + .build()) .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms()) + KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER) + .build()) .withPrimaryUserId(userId); if (password == null) { diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java index 4ecce7be..5572f449 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java @@ -20,6 +20,7 @@ import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; +import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.key.generation.type.KeyType; public class KeySpec { @@ -54,7 +55,7 @@ public class KeySpec { return inheritedSubPackets; } - public static KeySpecBuilder getBuilder(KeyType type) { - return new KeySpecBuilder(type); + public static KeySpecBuilder getBuilder(KeyType type, KeyFlag... flags) { + return new KeySpecBuilder(type, flags); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java index 17f4c55f..38e82e4e 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java @@ -15,10 +15,14 @@ */ package org.pgpainless.key.generation; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; import javax.annotation.Nonnull; -import org.bouncycastle.bcpg.sig.Features; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.pgpainless.PGPainless; import org.pgpainless.algorithm.AlgorithmSuite; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.Feature; @@ -30,20 +34,83 @@ import org.pgpainless.key.generation.type.KeyType; public class KeySpecBuilder implements KeySpecBuilderInterface { private final KeyType type; + private final KeyFlag[] keyFlags; private final PGPSignatureSubpacketGenerator hashedSubPackets = new PGPSignatureSubpacketGenerator(); + private final AlgorithmSuite algorithmSuite = PGPainless.getPolicy().getKeyGenerationAlgorithmSuite(); + private Set preferredCompressionAlgorithms = algorithmSuite.getCompressionAlgorithms(); + private Set preferredHashAlgorithms = algorithmSuite.getHashAlgorithms(); + private Set preferredSymmetricAlgorithms = algorithmSuite.getSymmetricKeyAlgorithms(); - KeySpecBuilder(@Nonnull KeyType type) { + KeySpecBuilder(@Nonnull KeyType type, KeyFlag... flags) { + if (flags == null || flags.length == 0) { + throw new IllegalArgumentException("KeyFlags cannot be empty."); + } + assureKeyCanCarryFlags(type, flags); this.type = type; + this.keyFlags = flags; } @Override - public WithDetailedConfiguration withKeyFlags(@Nonnull KeyFlag... flags) { - assureKeyCanCarryFlags(flags); - this.hashedSubPackets.setKeyFlags(false, KeyFlag.toBitmask(flags)); - return new WithDetailedConfigurationImpl(); + public KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms) { + this.preferredCompressionAlgorithms = new LinkedHashSet<>(Arrays.asList(compressionAlgorithms)); + return this; } - private void assureKeyCanCarryFlags(KeyFlag... flags) { + @Override + public KeySpecBuilder overridePreferredHashAlgorithms(@Nonnull HashAlgorithm... preferredHashAlgorithms) { + this.preferredHashAlgorithms = new LinkedHashSet<>(Arrays.asList(preferredHashAlgorithms)); + return this; + } + + @Override + public KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms) { + this.preferredSymmetricAlgorithms = new LinkedHashSet<>(Arrays.asList(preferredSymmetricKeyAlgorithms)); + return this; + } + + + @Override + public KeySpec build() { + this.hashedSubPackets.setKeyFlags(false, KeyFlag.toBitmask(keyFlags)); + this.hashedSubPackets.setPreferredCompressionAlgorithms(false, getPreferredCompressionAlgorithmIDs()); + this.hashedSubPackets.setPreferredHashAlgorithms(false, getPreferredHashAlgorithmIDs()); + this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, getPreferredSymmetricKeyAlgorithmIDs()); + this.hashedSubPackets.setFeature(false, Feature.MODIFICATION_DETECTION.getFeatureId()); + + return new KeySpec( + KeySpecBuilder.this.type, + hashedSubPackets, + false); + } + + private int[] getPreferredCompressionAlgorithmIDs() { + int[] ids = new int[preferredCompressionAlgorithms.size()]; + Iterator iterator = preferredCompressionAlgorithms.iterator(); + for (int i = 0; i < ids.length; i++) { + ids[i] = iterator.next().getAlgorithmId(); + } + return ids; + } + + private int[] getPreferredHashAlgorithmIDs() { + int[] ids = new int[preferredHashAlgorithms.size()]; + Iterator iterator = preferredHashAlgorithms.iterator(); + for (int i = 0; i < ids.length; i++) { + ids[i] = iterator.next().getAlgorithmId(); + } + return ids; + } + + private int[] getPreferredSymmetricKeyAlgorithmIDs() { + int[] ids = new int[preferredSymmetricAlgorithms.size()]; + Iterator iterator = preferredSymmetricAlgorithms.iterator(); + for (int i = 0; i < ids.length; i++) { + ids[i] = iterator.next().getAlgorithmId(); + } + return ids; + } + + private static void assureKeyCanCarryFlags(KeyType type, KeyFlag... flags) { final int mask = KeyFlag.toBitmask(flags); if (!type.canCertify() && KeyFlag.hasKeyFlag(mask, KeyFlag.CERTIFY_OTHER)) { @@ -66,120 +133,4 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag AUTHENTIACTION."); } } - - @Override - public KeySpec withInheritedSubPackets() { - return new KeySpec(type, null, true); - } - - class WithDetailedConfigurationImpl implements WithDetailedConfiguration { - - @Deprecated - @Override - public WithPreferredSymmetricAlgorithms withDetailedConfiguration() { - return new WithPreferredSymmetricAlgorithmsImpl(); - } - - @Override - public KeySpec withDefaultAlgorithms() { - AlgorithmSuite defaultSuite = AlgorithmSuite.getDefaultAlgorithmSuite(); - hashedSubPackets.setPreferredCompressionAlgorithms(false, defaultSuite.getCompressionAlgorithmIds()); - hashedSubPackets.setPreferredSymmetricAlgorithms(false, defaultSuite.getSymmetricKeyAlgorithmIds()); - hashedSubPackets.setPreferredHashAlgorithms(false, defaultSuite.getHashAlgorithmIds()); - hashedSubPackets.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION); - - return new KeySpec( - KeySpecBuilder.this.type, - KeySpecBuilder.this.hashedSubPackets, - false); - } - } - - class WithPreferredSymmetricAlgorithmsImpl implements WithPreferredSymmetricAlgorithms { - - @Override - public WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(@Nonnull SymmetricKeyAlgorithm... algorithms) { - int[] ids = new int[algorithms.length]; - for (int i = 0; i < ids.length; i++) { - ids[i] = algorithms[i].getAlgorithmId(); - } - KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, ids); - return new WithPreferredHashAlgorithmsImpl(); - } - - @Override - public WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms() { - KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds()); - return new WithPreferredHashAlgorithmsImpl(); - } - - @Override - public WithFeatures withDefaultAlgorithms() { - hashedSubPackets.setPreferredSymmetricAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds()); - hashedSubPackets.setPreferredCompressionAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds()); - hashedSubPackets.setPreferredHashAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds()); - return new WithFeaturesImpl(); - } - } - - class WithPreferredHashAlgorithmsImpl implements WithPreferredHashAlgorithms { - - @Override - public WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(@Nonnull HashAlgorithm... algorithms) { - int[] ids = new int[algorithms.length]; - for (int i = 0; i < ids.length; i++) { - ids[i] = algorithms[i].getAlgorithmId(); - } - KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false, ids); - return new WithPreferredCompressionAlgorithmsImpl(); - } - - @Override - public WithPreferredCompressionAlgorithms withDefaultHashAlgorithms() { - KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds()); - return new WithPreferredCompressionAlgorithmsImpl(); - } - } - - class WithPreferredCompressionAlgorithmsImpl implements WithPreferredCompressionAlgorithms { - - @Override - public WithFeatures withPreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... algorithms) { - int[] ids = new int[algorithms.length]; - for (int i = 0; i < ids.length; i++) { - ids[i] = algorithms[i].getAlgorithmId(); - } - KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false, ids); - return new WithFeaturesImpl(); - } - - @Override - public WithFeatures withDefaultCompressionAlgorithms() { - KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false, - AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds()); - return new WithFeaturesImpl(); - } - } - - class WithFeaturesImpl implements WithFeatures { - - @Override - public WithFeatures withFeature(@Nonnull Feature feature) { - KeySpecBuilder.this.hashedSubPackets.setFeature(false, feature.getFeatureId()); - return this; - } - - @Override - public KeySpec done() { - return new KeySpec( - KeySpecBuilder.this.type, - hashedSubPackets, - false); - } - } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java index f16959fd..2f366531 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java @@ -18,55 +18,16 @@ package org.pgpainless.key.generation; import javax.annotation.Nonnull; import org.pgpainless.algorithm.CompressionAlgorithm; -import org.pgpainless.algorithm.Feature; import org.pgpainless.algorithm.HashAlgorithm; -import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; public interface KeySpecBuilderInterface { - WithDetailedConfiguration withKeyFlags(@Nonnull KeyFlag... flags); + KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms); - KeySpec withInheritedSubPackets(); + KeySpecBuilder overridePreferredHashAlgorithms(@Nonnull HashAlgorithm... preferredHashAlgorithms); - interface WithDetailedConfiguration { - - WithPreferredSymmetricAlgorithms withDetailedConfiguration(); - - KeySpec withDefaultAlgorithms(); - } - - interface WithPreferredSymmetricAlgorithms { - - WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(@Nonnull SymmetricKeyAlgorithm... algorithms); - - WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms(); - - WithFeatures withDefaultAlgorithms(); - - } - - interface WithPreferredHashAlgorithms { - - WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(@Nonnull HashAlgorithm... algorithms); - - WithPreferredCompressionAlgorithms withDefaultHashAlgorithms(); - - } - - interface WithPreferredCompressionAlgorithms { - - WithFeatures withPreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... algorithms); - - WithFeatures withDefaultCompressionAlgorithms(); - - } - - interface WithFeatures { - - WithFeatures withFeature(@Nonnull Feature feature); - - KeySpec done(); - } + KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms); + KeySpec build(); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java b/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java index f2e3280a..44dd55b4 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java +++ b/pgpainless-core/src/main/java/org/pgpainless/policy/Policy.java @@ -21,6 +21,9 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import javax.annotation.Nonnull; + +import org.pgpainless.algorithm.AlgorithmSuite; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.PublicKeyAlgorithm; @@ -48,6 +51,8 @@ public final class Policy { PublicKeyAlgorithmPolicy.defaultPublicKeyAlgorithmPolicy(); private final NotationRegistry notationRegistry = new NotationRegistry(); + private AlgorithmSuite keyGenerationAlgorithmSuite = AlgorithmSuite.getDefaultAlgorithmSuite(); + Policy() { } @@ -122,7 +127,7 @@ public final class Policy { * * @return symmetric algorithm policy for decryption */ - public SymmetricKeyAlgorithmPolicy getSymmetricKeyDecryptionAlgoritmPolicy() { + public SymmetricKeyAlgorithmPolicy getSymmetricKeyDecryptionAlgorithmPolicy() { return symmetricKeyDecryptionAlgorithmPolicy; } @@ -459,4 +464,21 @@ public final class Policy { public NotationRegistry getNotationRegistry() { return notationRegistry; } + + /** + * Return the current {@link AlgorithmSuite} which defines preferred algorithms used during key generation. + * @return current algorithm suite + */ + public @Nonnull AlgorithmSuite getKeyGenerationAlgorithmSuite() { + return keyGenerationAlgorithmSuite; + } + + /** + * Set a custom {@link AlgorithmSuite} which defines preferred algorithms used during key generation. + * + * @param algorithmSuite custom algorithm suite + */ + public void setKeyGenerationAlgorithmSuite(@Nonnull AlgorithmSuite algorithmSuite) { + this.keyGenerationAlgorithmSuite = algorithmSuite; + } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java index 1fd80e02..c7bd68dc 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java @@ -88,8 +88,13 @@ public class EncryptDecryptTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._3072); PGPSecretKeyRing recipient = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(ElGamal.withLength(ElGamalLength._3072)).withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS).withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096)).withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER).withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder( + ElGamal.withLength(ElGamalLength._3072), + KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) + .build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._4096), + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER).build()) .withPrimaryUserId("juliet@capulet.lit").withoutPassphrase().build(); encryptDecryptForSecretKeyRings(sender, recipient); @@ -262,7 +267,7 @@ public class EncryptDecryptTest { EncryptionStream signer = PGPainless.encryptAndOrSign().onOutputStream(signOut) .withOptions(ProducerOptions.sign( SigningOptions.get() - .addInlineSignature(keyRingProtector, signingKeys, DocumentSignatureType.BINARY_DOCUMENT) + .addInlineSignature(keyRingProtector, signingKeys, DocumentSignatureType.BINARY_DOCUMENT) ).setAsciiArmor(true)); Streams.pipeAll(inputStream, signer); signer.close(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java index 78617349..f0aef576 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java @@ -59,13 +59,12 @@ public class EncryptionOptionsTest { @BeforeAll public static void generateKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS).withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_STORAGE).withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) + .build()) + .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE) + .build()) + .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER) + .build()) .withPrimaryUserId("test@pgpainless.org") .withoutPassphrase() .build(); @@ -140,8 +139,8 @@ public class EncryptionOptionsTest { public void testAddRecipient_KeyWithoutEncryptionKeyFails() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { EncryptionOptions options = new EncryptionOptions(); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA).withDefaultAlgorithms()) + .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .build()) .withPrimaryUserId("test@pgpainless.org") .withoutPassphrase().build(); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java index e9e65a0f..75e847f5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java @@ -27,16 +27,13 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.AlgorithmSuite; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.EncryptionPurpose; -import org.pgpainless.algorithm.Feature; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.PublicKeyAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.key.generation.KeySpec; -import org.pgpainless.key.generation.KeySpecBuilderInterface; import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.ecc.EllipticCurve; import org.pgpainless.key.generation.type.eddsa.EdDSACurve; @@ -162,24 +159,20 @@ public class GenerateKeys { * the specifications for the subkeys first (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withSubKey(KeySpec)}) * and add the primary key specification last (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withPrimaryKey(KeySpec)}. * - * {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType)} method and providing a {@link KeyType}. + * {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType, KeyFlag...)} method and providing a {@link KeyType}. * There are a bunch of factory methods for different {@link KeyType} implementations present in {@link KeyType} itself - * (such as {@link KeyType#ECDH(EllipticCurve)}. - * - * After that, the {@link org.pgpainless.key.generation.KeySpecBuilder} needs to be further configured. - * First of all, the keys {@link KeyFlag KeyFlags} need to be specified. {@link KeyFlag KeyFlags} determine + * (such as {@link KeyType#ECDH(EllipticCurve)}. {@link KeyFlag KeyFlags} determine * the use of the key, like encryption, signing data or certifying subkeys. - * KeyFlags can be set with {@link org.pgpainless.key.generation.KeySpecBuilder#withKeyFlags(KeyFlag...)}. * - * Next is algorithm setup. You can either trust PGPainless' defaults (see {@link AlgorithmSuite#getDefaultAlgorithmSuite()}), - * or specify your own algorithm preferences. - * To go with the defaults, call {@link KeySpecBuilderInterface.WithDetailedConfiguration#withDefaultAlgorithms()}, - * otherwise start detailed config with {@link KeySpecBuilderInterface.WithDetailedConfiguration#withDetailedConfiguration()}. + * If you so desire, you can now specify your own algorithm preferences. + * For that, see {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredCompressionAlgorithms(CompressionAlgorithm...)}, + * {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredHashAlgorithms(HashAlgorithm...)} or + * {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm...)}. * * Note, that if you set preferred algorithms, the preference lists are sorted from high priority to low priority. * * When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#withPrimaryKey(KeySpec)}), - * make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is an requirement + * make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is a requirement * for primary keys. * * Furthermore you have to set at least the primary user-id via @@ -187,7 +180,7 @@ public class GenerateKeys { * but you can also add additional user-ids via * {@link org.pgpainless.key.generation.KeyRingBuilderInterface.WithAdditionalUserIdOrPassphrase#withAdditionalUserId(String)}. * - * Lastly you can decide whether or not to set a passphrase to protect the secret key. + * Lastly you can decide whether to set a passphrase to protect the secret key. * * @throws PGPException * @throws InvalidAlgorithmParameterException @@ -211,38 +204,33 @@ public class GenerateKeys { // Add the first subkey (in this case encryption) .withSubKey( KeySpec.getBuilder( - // We choose an ECDH key over the brainpoolp256r1 curve - KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1) - ) - // Our key can encrypt both communication data, as well as data at rest - .withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) + // We choose an ECDH key over the brainpoolp256r1 curve + KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1), + // Our key can encrypt both communication data, as well as data at rest + KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS + ) // Optionally: Configure the subkey with custom algorithm preferences // Is is recommended though to go with PGPainless' defaults which can be found in the // AlgorithmSuite class. - .withDetailedConfiguration() - .withPreferredSymmetricAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128) - .withPreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256) - .withPreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB) - // Modification Detection is highly recommended - .withFeature(Feature.MODIFICATION_DETECTION) - .done() + .overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128) + .overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256) + .overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB) + .build() ) // Add the second subkey (signing) .withSubKey( KeySpec.getBuilder( - KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1) - ) - // This key is used for creating signatures only - .withKeyFlags(KeyFlag.SIGN_DATA) - // Instead of manually specifying algorithm preferences, we can simply use PGPainless' sane defaults - .withDefaultAlgorithms() + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), + // This key is used for creating signatures only + KeyFlag.SIGN_DATA + ).build() ) // Lastly we add the primary key (certification only in our case) .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - // The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags - .withKeyFlags(KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms() + KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), + // The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags + KeyFlag.CERTIFY_OTHER) + .build() ) // Set primary user-id .withPrimaryUserId(userId) diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java index 64c6814a..f8be9714 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java @@ -187,9 +187,8 @@ public class ModifyKeys { Passphrase subkeyPassphrase = Passphrase.fromPassword("subk3yP4ssphr4s3"); secretKey = PGPainless.modifyKeyRing(secretKey) .addSubKey( - KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP512R1)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms(), + KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP512R1), KeyFlag.ENCRYPT_COMMS) + .build(), subkeyPassphrase, protector) .done(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java index 8949726a..aeafaf59 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java @@ -54,12 +54,10 @@ public class BrainpoolKeyGenerationTest { for (EllipticCurve curve : EllipticCurve.values()) { PGPSecretKeyRing secretKeys = generateKey( - KeySpec.getBuilder(KeyType.ECDSA(curve)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDefaultAlgorithms(), - KeySpec.getBuilder(KeyType.ECDH(curve)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .withDefaultAlgorithms(), + KeySpec.getBuilder( + KeyType.ECDSA(curve), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA).build(), + KeySpec.getBuilder( + KeyType.ECDH(curve), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE).build(), "Elliptic Curve "); assertEquals(PublicKeyAlgorithm.ECDSA, PublicKeyAlgorithm.fromId(secretKeys.getPublicKey().getAlgorithm())); @@ -85,18 +83,15 @@ public class BrainpoolKeyGenerationTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA).build()) + .withSubKey(KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) + .build()) + .withSubKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), KeyFlag.SIGN_DATA) + .build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.CERTIFY_OTHER).build()) .withPrimaryUserId(UserId.nameAndEmail("Alice", "alice@pgpainless.org")) .withPassphrase(Passphrase.fromPassword("passphrase")) .build(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java index 9a631e15..65b7a71f 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java @@ -47,9 +47,8 @@ public class CertificationKeyMustBeAbleToCertifyTest { assertThrows(IllegalArgumentException.class, () -> PGPainless .generateKeyRing() .withPrimaryKey(KeySpec - .getBuilder(type) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) + .getBuilder(type, KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .build()) .withPrimaryUserId("should@throw.ex") .withoutPassphrase().build()); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java index 01dc2fcb..9e60eca6 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java @@ -41,12 +41,11 @@ public class GenerateEllipticCurveKeyTest { public void generateEllipticCurveKeys(ImplementationFactory implementationFactory) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing keyRing = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS).build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .build()) .withPrimaryUserId(UserId.onlyEmail("alice@wonderland.lit").toString()) .withoutPassphrase() .build(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java index 46a3a652..33db6fc5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java @@ -47,9 +47,10 @@ public class GenerateKeyWithAdditionalUserIdTest { ImplementationFactory.setFactoryImplementation(implementationFactory); Date expiration = new Date(DateUtil.now().getTime() + 60 * 1000); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) + .build()) .withPrimaryUserId(UserId.onlyEmail("primary@user.id")) .withAdditionalUserId(UserId.onlyEmail("additional@user.id")) .withAdditionalUserId(UserId.onlyEmail("additional2@user.id")) diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java index 0cc02f86..60e7c131 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java @@ -46,9 +46,10 @@ public class GenerateWithEmptyPassphrase { ImplementationFactory.setFactoryImplementation(implementationFactory); assertNotNull(PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) + .build()) .withPrimaryUserId("primary@user.id") .withPassphrase(Passphrase.emptyPassphrase()) .build()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/IllegalKeyFlagsTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/IllegalKeyFlagsTest.java index 5803f6ef..0a861518 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/IllegalKeyFlagsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/IllegalKeyFlagsTest.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.pgpainless.PGPainless; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.generation.type.KeyType; @@ -32,29 +31,19 @@ public class IllegalKeyFlagsTest { @MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories") public void testKeyCannotCarryFlagsTest(ImplementationFactory implementationFactory) { ImplementationFactory.setFactoryImplementation(implementationFactory); - assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.SIGN_DATA) // <- should throw - .withDefaultAlgorithms())); + assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.SIGN_DATA)); - assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER) // <- should throw - .withDefaultAlgorithms())); + assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.CERTIFY_OTHER)); - assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.AUTHENTICATION) // <- should throw - .withDefaultAlgorithms())); + assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.AUTHENTICATION)); - assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) // <- should throw - .withDefaultAlgorithms())); + assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.ENCRYPT_COMMS)); - assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.ENCRYPT_STORAGE) // <- should throw as well - .withDefaultAlgorithms())); + assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.ENCRYPT_STORAGE)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java index fefc1cf6..f1413a97 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java @@ -221,9 +221,14 @@ public class KeyRingInfoTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP384R1)).withKeyFlags(KeyFlag.ENCRYPT_STORAGE).withDefaultAlgorithms()) - .withSubKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1)).withKeyFlags(KeyFlag.SIGN_DATA).withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)).withKeyFlags(KeyFlag.CERTIFY_OTHER).withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder( + KeyType.ECDH(EllipticCurve._BRAINPOOLP384R1), + KeyFlag.ENCRYPT_STORAGE).build()) + .withSubKey(KeySpec.getBuilder( + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.SIGN_DATA) + .build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER).build()) .withPrimaryUserId(UserId.newBuilder().withName("Alice").withEmail("alice@pgpainless.org").build()) .withoutPassphrase() .build(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java index caaf6ac0..fce78fcd 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java @@ -51,12 +51,13 @@ public class UserIdRevocationTest { @Test public void testRevocationWithoutRevocationAttributes() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) + .build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) + .build()) .withPrimaryUserId("primary@key.id") .withAdditionalUserId("secondary@key.id") .withoutPassphrase() @@ -91,12 +92,12 @@ public class UserIdRevocationTest { @Test public void testRevocationWithRevocationReason() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) + .build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) + .build()) .withPrimaryUserId("primary@key.id") .withAdditionalUserId("secondary@key.id") .withoutPassphrase() diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubKeyTest.java index fb4b6b4b..c6a4dd8d 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubKeyTest.java @@ -61,9 +61,7 @@ public class AddSubKeyTest { secretKeys = PGPainless.modifyKeyRing(secretKeys) .addSubKey( - KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDefaultAlgorithms(), + KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA).build(), Passphrase.fromPassword("subKeyPassphrase"), PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("password123"))) .done(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/policy/PolicyTest.java b/pgpainless-core/src/test/java/org/pgpainless/policy/PolicyTest.java index 732f1b47..c4819861 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/policy/PolicyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/policy/PolicyTest.java @@ -89,14 +89,14 @@ public class PolicyTest { @Test public void testAcceptableSymmetricKeyDecryptionAlgorithm() { - assertTrue(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH)); - assertTrue(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH.getAlgorithmId())); + assertTrue(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH)); + assertTrue(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH.getAlgorithmId())); } @Test public void testUnAcceptableSymmetricKeyDecryptionAlgorithm() { - assertFalse(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128)); - assertFalse(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128.getAlgorithmId())); + assertFalse(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128)); + assertFalse(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128.getAlgorithmId())); } @Test diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java index eb707e34..7cee1a7c 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java @@ -47,12 +47,11 @@ public class BCUtilTest { throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException { PGPSecretKeyRing sec = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS) - .withDefaultAlgorithms()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDefaultAlgorithms()) + .withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072), KeyFlag.ENCRYPT_COMMS).build()) + .withPrimaryKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .build()) .withPrimaryUserId("donald@duck.tails").withoutPassphrase().build(); PGPPublicKeyRing pub = KeyRingUtils.publicKeyRingFrom(sec); diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java index ebc15725..05f676cf 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java @@ -41,15 +41,12 @@ public class GuessPreferredHashAlgorithmTest { @Test public void guessPreferredHashAlgorithmsAssumesHashAlgoUsedBySelfSig() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .withDetailedConfiguration() - // Do not specify preferred algorithms - .withPreferredSymmetricAlgorithms(new SymmetricKeyAlgorithm[] {}) - .withPreferredHashAlgorithms(new HashAlgorithm[] {}) - .withPreferredCompressionAlgorithms(new CompressionAlgorithm[] {}) - - .done()) + .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .overridePreferredHashAlgorithms(new HashAlgorithm[] {}) + .overridePreferredSymmetricKeyAlgorithms(new SymmetricKeyAlgorithm[] {}) + .overridePreferredCompressionAlgorithms(new CompressionAlgorithm[] {}) + .build()) .withPrimaryUserId("test@test.test") .withoutPassphrase() .build(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java index 772995cb..4bf93212 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java +++ b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java @@ -38,12 +38,12 @@ public class TestEncryptCommsStorageFlagsDifferentiated { @Test public void testThatEncryptionDifferentiatesBetweenPurposeKeyFlags() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072)) - .withKeyFlags(KeyFlag.CERTIFY_OTHER, + .withPrimaryKey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_STORAGE // no ENCRYPT_COMMS - ) - .withDefaultAlgorithms()) + ).build()) .withPrimaryUserId("cannot@encrypt.comms") .withoutPassphrase() .build(); From 11ad6361f85e0980ddf547519bd2c0c3b7b993c7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Sep 2021 19:36:03 +0200 Subject: [PATCH 2/7] Reformat arguments --- .../java/org/pgpainless/key/generation/KeySpecBuilder.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java index 38e82e4e..bbc7f14a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java @@ -77,10 +77,7 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, getPreferredSymmetricKeyAlgorithmIDs()); this.hashedSubPackets.setFeature(false, Feature.MODIFICATION_DETECTION.getFeatureId()); - return new KeySpec( - KeySpecBuilder.this.type, - hashedSubPackets, - false); + return new KeySpec(type, hashedSubPackets, false); } private int[] getPreferredCompressionAlgorithmIDs() { From fedf7c0cf889ecf950b5c2d6ca40a5f07aa282bf Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Sep 2021 19:39:42 +0200 Subject: [PATCH 3/7] Make AlgorithmSuite members final and remove setters --- .../pgpainless/algorithm/AlgorithmSuite.java | 46 +----------- .../algorithm/AlgorithmSuiteTest.java | 73 ------------------- 2 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/algorithm/AlgorithmSuiteTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/AlgorithmSuite.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/AlgorithmSuite.java index 17445df5..487ffad4 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/AlgorithmSuite.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/AlgorithmSuite.java @@ -15,7 +15,6 @@ */ package org.pgpainless.algorithm; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; @@ -45,9 +44,9 @@ public class AlgorithmSuite { CompressionAlgorithm.UNCOMPRESSED) ); - private Set symmetricKeyAlgorithms; - private Set hashAlgorithms; - private Set compressionAlgorithms; + private final Set symmetricKeyAlgorithms; + private final Set hashAlgorithms; + private final Set compressionAlgorithms; public AlgorithmSuite(List symmetricKeyAlgorithms, List hashAlgorithms, @@ -57,57 +56,18 @@ public class AlgorithmSuite { this.compressionAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(compressionAlgorithms)); } - public void setSymmetricKeyAlgorithms(List symmetricKeyAlgorithms) { - this.symmetricKeyAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(symmetricKeyAlgorithms)); - } - public Set getSymmetricKeyAlgorithms() { return new LinkedHashSet<>(symmetricKeyAlgorithms); } - public int[] getSymmetricKeyAlgorithmIds() { - int[] array = new int[symmetricKeyAlgorithms.size()]; - List list = new ArrayList<>(getSymmetricKeyAlgorithms()); - for (int i = 0; i < array.length; i++) { - array[i] = list.get(i).getAlgorithmId(); - } - return array; - } - - public void setHashAlgorithms(List hashAlgorithms) { - this.hashAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(hashAlgorithms)); - } - public Set getHashAlgorithms() { return new LinkedHashSet<>(hashAlgorithms); } - public int[] getHashAlgorithmIds() { - int[] array = new int[hashAlgorithms.size()]; - List list = new ArrayList<>(getHashAlgorithms()); - for (int i = 0; i < array.length; i++) { - array[i] = list.get(i).getAlgorithmId(); - } - return array; - } - - public void setCompressionAlgorithms(List compressionAlgorithms) { - this.compressionAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(compressionAlgorithms)); - } - public Set getCompressionAlgorithms() { return new LinkedHashSet<>(compressionAlgorithms); } - public int[] getCompressionAlgorithmIds() { - int[] array = new int[compressionAlgorithms.size()]; - List list = new ArrayList<>(getCompressionAlgorithms()); - for (int i = 0; i < array.length; i++) { - array[i] = list.get(i).getAlgorithmId(); - } - return array; - } - public static AlgorithmSuite getDefaultAlgorithmSuite() { return defaultAlgorithmSuite; } diff --git a/pgpainless-core/src/test/java/org/pgpainless/algorithm/AlgorithmSuiteTest.java b/pgpainless-core/src/test/java/org/pgpainless/algorithm/AlgorithmSuiteTest.java deleted file mode 100644 index 89ac5a2a..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/algorithm/AlgorithmSuiteTest.java +++ /dev/null @@ -1,73 +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.algorithm; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class AlgorithmSuiteTest { - - private AlgorithmSuite suite; - - @BeforeEach - public void resetEmptyAlgorithmSuite() { - suite = new AlgorithmSuite( - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList() - ); - } - - @Test - public void setSymmetricAlgorithmsTest() { - List algorithmList = Arrays.asList( - SymmetricKeyAlgorithm.AES_128, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_256 - ); - - suite.setSymmetricKeyAlgorithms(algorithmList); - - assertEquals(algorithmList, new ArrayList<>(suite.getSymmetricKeyAlgorithms())); - } - - @Test - public void setHashAlgorithmsTest() { - List algorithmList = Arrays.asList( - HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512 - ); - - suite.setHashAlgorithms(algorithmList); - - assertEquals(algorithmList, new ArrayList<>(suite.getHashAlgorithms())); - } - - @Test - public void setCompressionAlgorithmsTest() { - List algorithmList = Arrays.asList( - CompressionAlgorithm.ZLIB, CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2 - ); - - suite.setCompressionAlgorithms(algorithmList); - - assertEquals(algorithmList, new ArrayList<>(suite.getCompressionAlgorithms())); - } -} From cff69006f7e350c572614cfafe4dc3a69f435c48 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Sep 2021 19:50:24 +0200 Subject: [PATCH 4/7] Update readme --- README.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 64f2e002..406d4eca 100644 --- a/README.md +++ b/README.md @@ -76,22 +76,19 @@ There are some predefined key archetypes, but it is possible to fully customize // Customized key PGPSecretKeyRing keyRing = PGPainless.generateKeyRing() .withSubKey( - KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256)) - .withKeyFlags(KeyFlag.SIGN_DATA) - .withDetailedConfiguration() - .withDefaultSymmetricAlgorithms() - .withDefaultHashAlgorithms() - .withPreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB) - .withFeature(Feature.MODIFICATION_DETECTION) - .done() + KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA) + .overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB) + .build() ).withSubKey( - KeySpec.getBuilder(ECDH.fromCurve(EllipticCurve._P256)) - .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .withDefaultAlgorithms() + KeySpec.getBuilder( + ECDH.fromCurve(EllipticCurve._P256), + KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) + .build() ).withMasterKey( - KeySpec.getBuilder(RSA.withLength(RsaLength._8192)) - .withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .withDefaultAlgorithms() + KeySpec.getBuilder( + RSA.withLength(RsaLength._8192), + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) + .build() ).withPrimaryUserId("Juliet ") .withAdditionalUserId("xmpp:juliet@capulet.lit") .withPassphrase("romeo_oh_Romeo<3") From 387b2b4b4370897b6f36679d774bbda1ff293542 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 20 Sep 2021 11:26:00 +0200 Subject: [PATCH 5/7] Ensure that KeySpecBuilder gets at least one key flag --- .../java/org/pgpainless/key/generation/KeySpec.java | 4 ++-- .../org/pgpainless/key/generation/KeySpecBuilder.java | 11 ++++++++--- .../java/org/pgpainless/util/CollectionUtils.java | 8 ++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java index 5572f449..c53d91e8 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java @@ -55,7 +55,7 @@ public class KeySpec { return inheritedSubPackets; } - public static KeySpecBuilder getBuilder(KeyType type, KeyFlag... flags) { - return new KeySpecBuilder(type, flags); + public static KeySpecBuilder getBuilder(KeyType type, KeyFlag flag, KeyFlag... flags) { + return new KeySpecBuilder(type, flag, flags); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java index bbc7f14a..d6095201 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java @@ -30,6 +30,7 @@ import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.key.generation.type.KeyType; +import org.pgpainless.util.CollectionUtils; public class KeySpecBuilder implements KeySpecBuilderInterface { @@ -41,10 +42,14 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { private Set preferredHashAlgorithms = algorithmSuite.getHashAlgorithms(); private Set preferredSymmetricAlgorithms = algorithmSuite.getSymmetricKeyAlgorithms(); - KeySpecBuilder(@Nonnull KeyType type, KeyFlag... flags) { - if (flags == null || flags.length == 0) { - throw new IllegalArgumentException("KeyFlags cannot be empty."); + KeySpecBuilder(@Nonnull KeyType type, KeyFlag flag, KeyFlag... flags) { + if (flag == null) { + throw new IllegalArgumentException("Key MUST carry at least one key flag"); } + if (flags == null) { + throw new IllegalArgumentException("List of additional flags MUST NOT be null."); + } + flags = CollectionUtils.concat(flag, flags); assureKeyCanCarryFlags(type, flags); this.type = type; this.keyFlags = flags; diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/CollectionUtils.java b/pgpainless-core/src/main/java/org/pgpainless/util/CollectionUtils.java index 6ad81991..cb845df4 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/util/CollectionUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/util/CollectionUtils.java @@ -15,6 +15,7 @@ */ package org.pgpainless.util; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -33,4 +34,11 @@ public final class CollectionUtils { } return items; } + + public static T[] concat(T t, T[] ts) { + T[] concat = (T[]) Array.newInstance(t.getClass(), ts.length + 1); + concat[0] = t; + System.arraycopy(ts, 0, concat, 1, ts.length); + return concat; + } } From be47a960301868f186d48e18328270bf8ad3e7db Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 20 Sep 2021 12:30:03 +0200 Subject: [PATCH 6/7] Further simplify the KeyRingBuilder API --- .../key/generation/KeyRingBuilder.java | 413 ++++++++---------- .../generation/KeyRingBuilderInterface.java | 56 +-- .../EncryptDecryptTest.java | 13 +- .../EncryptionOptionsTest.java | 16 +- .../org/pgpainless/example/GenerateKeys.java | 77 ++-- .../BrainpoolKeyGenerationTest.java | 27 +- ...rtificationKeyMustBeAbleToCertifyTest.java | 8 +- .../GenerateEllipticCurveKeyTest.java | 10 +- .../GenerateKeyWithAdditionalUserIdTest.java | 14 +- ...a => GenerateWithEmptyPassphraseTest.java} | 11 +- .../pgpainless/key/info/KeyRingInfoTest.java | 16 +- .../key/info/UserIdRevocationTest.java | 30 +- .../java/org/pgpainless/util/BCUtilTest.java | 10 +- .../util/GuessPreferredHashAlgorithmTest.java | 8 +- ...ncryptCommsStorageFlagsDifferentiated.java | 7 +- 15 files changed, 316 insertions(+), 400 deletions(-) rename pgpainless-core/src/test/java/org/pgpainless/key/generation/{GenerateWithEmptyPassphrase.java => GenerateWithEmptyPassphraseTest.java} (88%) 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 230a0a9a..3c81332c 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 @@ -61,14 +61,18 @@ import org.pgpainless.provider.ProviderFactory; import org.pgpainless.util.Passphrase; import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil; -public class KeyRingBuilder implements KeyRingBuilderInterface { +public class KeyRingBuilder implements KeyRingBuilderInterface { private final Charset UTF8 = Charset.forName("UTF-8"); - private final List keySpecs = new ArrayList<>(); - private String userId; - private final Set additionalUserIds = new LinkedHashSet<>(); - private Passphrase passphrase; + private PGPSignatureGenerator signatureGenerator; + private PGPDigestCalculator digestCalculator; + private PBESecretKeyEncryptor secretKeyEncryptor; + + private KeySpec primaryKeySpec; + private final List subkeySpecs = new ArrayList<>(); + private final Set userIds = new LinkedHashSet<>(); + private Passphrase passphrase = null; private Date expirationDate = null; /** @@ -126,17 +130,14 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { */ public PGPSecretKeyRing simpleRsaKeyRing(@Nonnull String userId, @Nonnull RsaLength length, String password) throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - WithAdditionalUserIdOrPassphrase builder = this - .withPrimaryKey(KeySpec - .getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryUserId(userId); + KeyRingBuilder builder = new KeyRingBuilder() + .setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) + .addUserId(userId); - if (password == null) { - return builder.withoutPassphrase().build(); - } else { - return builder.withPassphrase(new Passphrase(password.toCharArray())).build(); + if (!isNullOrEmpty(password)) { + builder.setPassphrase(Passphrase.fromPassword(password)); } + return builder.build(); } /** @@ -194,20 +195,15 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { */ public PGPSecretKeyRing simpleEcKeyRing(@Nonnull String userId, String password) throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - WithAdditionalUserIdOrPassphrase builder = this - .withSubKey( - KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .build()) - .withPrimaryUserId(userId); + KeyRingBuilder builder = new KeyRingBuilder() + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)) + .addUserId(userId); - if (password == null) { - return builder.withoutPassphrase().build(); - } else { - return builder.withPassphrase(new Passphrase(password.toCharArray())).build(); + if (!isNullOrEmpty(password)) { + builder.setPassphrase(Passphrase.fromPassword(password)); } + return builder.build(); } /** @@ -220,37 +216,59 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { */ public PGPSecretKeyRing modernKeyRing(String userId, String password) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - WithAdditionalUserIdOrPassphrase builder = this - .withSubKey( - KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .build()) - .withSubKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA) - .build()) - .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER) - .build()) - .withPrimaryUserId(userId); - - if (password == null) { - return builder.withoutPassphrase().build(); - } else { - return builder.withPassphrase(new Passphrase(password.toCharArray())).build(); + KeyRingBuilder builder = new KeyRingBuilder() + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)) + .addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)) + .addUserId(userId); + if (!isNullOrEmpty(password)) { + builder.setPassphrase(Passphrase.fromPassword(password)); } + return builder.build(); } @Override - public KeyRingBuilderInterface withSubKey(@Nonnull KeySpec type) { - KeyRingBuilder.this.keySpecs.add(type); + public KeyRingBuilder setPrimaryKey(@Nonnull KeySpec keySpec) { + verifyMasterKeyCanCertify(keySpec); + this.primaryKeySpec = keySpec; return this; } @Override - public WithPrimaryUserId withPrimaryKey(@Nonnull KeySpec spec) { - verifyMasterKeyCanCertify(spec); + public KeyRingBuilder addSubkey(@Nonnull KeySpec keySpec) { + this.subkeySpecs.add(keySpec); + return this; + } - KeyRingBuilder.this.keySpecs.add(0, spec); - return new WithPrimaryUserIdImpl(); + @Override + public KeyRingBuilder addUserId(@Nonnull String userId) { + this.userIds.add(userId.trim()); + return this; + } + + @Override + public KeyRingBuilder addUserId(@Nonnull byte[] userId) { + return addUserId(new String(userId, UTF8)); + } + + @Override + public KeyRingBuilder setExpirationDate(@Nonnull Date expirationDate) { + Date now = new Date(); + if (now.after(expirationDate)) { + throw new IllegalArgumentException("Expiration date must be in the future."); + } + this.expirationDate = expirationDate; + return this; + } + + @Override + public KeyRingBuilder setPassphrase(@Nonnull Passphrase passphrase) { + this.passphrase = passphrase; + return this; + } + + private static boolean isNullOrEmpty(String password) { + return password == null || password.trim().isEmpty(); } private void verifyMasterKeyCanCertify(KeySpec spec) { @@ -270,196 +288,139 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { return keySpec.getKeyType().canCertify(); } - class WithPrimaryUserIdImpl implements WithPrimaryUserId { + @Override + public PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, + InvalidAlgorithmParameterException { + if (userIds.isEmpty()) { + throw new IllegalStateException("At least one user-id is required."); + } + digestCalculator = buildDigestCalculator(); + secretKeyEncryptor = buildSecretKeyEncryptor(); + PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor(); - @Override - public WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull String userId) { - KeyRingBuilder.this.userId = userId.trim(); - return new WithAdditionalUserIdOrPassphraseImpl(); + if (passphrase != null) { + passphrase.clear(); } - @Override - public WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull byte[] userId) { - return withPrimaryUserId(new String(userId, UTF8)); + // Generate Primary Key + PGPKeyPair certKey = generateKeyPair(primaryKeySpec); + PGPContentSignerBuilder signer = buildContentSigner(certKey); + signatureGenerator = new PGPSignatureGenerator(signer); + PGPSignatureSubpacketGenerator hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator(); + hashedSubPacketGenerator.setPrimaryUserID(false, true); + if (expirationDate != null) { + SignatureSubpacketGeneratorUtil.setExpirationDateInSubpacketGenerator( + expirationDate, new Date(), hashedSubPacketGenerator); + } + PGPSignatureSubpacketVector hashedSubPackets = hashedSubPacketGenerator.generate(); + + // Generator which the user can get the key pair from + PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, hashedSubPackets); + + addSubKeys(certKey, ringGenerator); + + // Generate secret key ring with only primary user id + PGPSecretKeyRing secretKeyRing = ringGenerator.generateSecretKeyRing(); + + Iterator secretKeys = secretKeyRing.getSecretKeys(); + + // Attempt to add additional user-ids to the primary public key + PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey(); + PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor); + Iterator additionalUserIds = userIds.iterator(); + additionalUserIds.next(); // Skip primary user id + while (additionalUserIds.hasNext()) { + String additionalUserId = additionalUserIds.next(); + signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); + PGPSignature additionalUserIdSignature = + signatureGenerator.generateCertification(additionalUserId, primaryPubKey); + primaryPubKey = PGPPublicKey.addCertification(primaryPubKey, + additionalUserId, additionalUserIdSignature); + } + + // "reassemble" secret key ring with modified primary key + PGPSecretKey primarySecKey = new PGPSecretKey( + privateKey, + primaryPubKey, digestCalculator, true, secretKeyEncryptor); + List secretKeyList = new ArrayList<>(); + secretKeyList.add(primarySecKey); + while (secretKeys.hasNext()) { + secretKeyList.add(secretKeys.next()); + } + secretKeyRing = new PGPSecretKeyRing(secretKeyList); + + return secretKeyRing; + } + + private PGPKeyRingGenerator buildRingGenerator(PGPKeyPair certKey, + PGPContentSignerBuilder signer, + PGPSignatureSubpacketVector hashedSubPackets) + throws PGPException { + String primaryUserId = userIds.iterator().next(); + return new PGPKeyRingGenerator( + SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey, + primaryUserId, digestCalculator, + hashedSubPackets, null, signer, secretKeyEncryptor); + } + + private void addSubKeys(PGPKeyPair primaryKey, PGPKeyRingGenerator ringGenerator) + throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException { + for (KeySpec subKeySpec : subkeySpecs) { + PGPKeyPair subKey = generateKeyPair(subKeySpec); + if (subKeySpec.isInheritedSubPackets()) { + ringGenerator.addSubKey(subKey); + } else { + PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets(); + try { + hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets); + } catch (IOException e) { + throw new PGPException("Exception while adding primary key binding signature to signing subkey", e); + } + ringGenerator.addSubKey(subKey, hashedSubpackets, null); + } } } - class WithAdditionalUserIdOrPassphraseImpl implements WithAdditionalUserIdOrPassphrase { - - @Override - public WithAdditionalUserIdOrPassphrase setExpirationDate(@Nonnull Date expirationDate) { - Date now = new Date(); - if (now.after(expirationDate)) { - throw new IllegalArgumentException("Expiration date must be in the future."); - } - KeyRingBuilder.this.expirationDate = expirationDate; - return this; + private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets) throws PGPException, IOException { + int keyFlagMask = hashedSubpackets.getKeyFlags(); + if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) { + return hashedSubpackets; } - @Override - public WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull String userId) { - String trimmed = userId.trim(); - if (KeyRingBuilder.this.userId.equals(trimmed)) { - throw new IllegalArgumentException("Additional user-id MUST NOT be equal to primary user-id."); - } - KeyRingBuilder.this.additionalUserIds.add(trimmed); - return this; - } + PGPSignatureGenerator bindingSignatureGenerator = new PGPSignatureGenerator(buildContentSigner(subKey)); + bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.getCode(), subKey.getPrivateKey()); + PGPSignature primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.getPublicKey(), subKey.getPublicKey()); + PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(hashedSubpackets); + subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig); + return subpacketGenerator.generate(); + } - @Override - public WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull byte[] userId) { - return withAdditionalUserId(new String(userId, UTF8)); - } + private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) { + HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm(); + return ImplementationFactory.getInstance().getPGPContentSignerBuilder( + certKey.getPublicKey().getAlgorithm(), + hashAlgorithm.getAlgorithmId()); + } - @Override - public Build withPassphrase(@Nonnull Passphrase passphrase) { - KeyRingBuilder.this.passphrase = passphrase; - return new BuildImpl(); - } + private PBESecretKeyEncryptor buildSecretKeyEncryptor() { + SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy() + .getDefaultSymmetricKeyAlgorithm(); + PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ? + null : // unencrypted key pair, otherwise AES-256 encrypted + ImplementationFactory.getInstance().getPBESecretKeyEncryptor( + keyEncryptionAlgorithm, digestCalculator, passphrase); + return encryptor; + } - @Override - public Build withoutPassphrase() { - KeyRingBuilder.this.passphrase = null; - return new BuildImpl(); - } + private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException { + PBESecretKeyDecryptor decryptor = passphrase == null || passphrase.isEmpty() ? + null : + ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase); + return decryptor; + } - class BuildImpl implements Build { - - private PGPSignatureGenerator signatureGenerator; - private PGPDigestCalculator digestCalculator; - private PBESecretKeyEncryptor secretKeyEncryptor; - - @Override - public PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, - InvalidAlgorithmParameterException { - digestCalculator = buildDigestCalculator(); - secretKeyEncryptor = buildSecretKeyEncryptor(); - PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor(); - - if (passphrase != null) { - passphrase.clear(); - } - - // First key is the Master Key - KeySpec certKeySpec = keySpecs.remove(0); - - // Generate Master Key - PGPKeyPair certKey = generateKeyPair(certKeySpec); - PGPContentSignerBuilder signer = buildContentSigner(certKey); - signatureGenerator = new PGPSignatureGenerator(signer); - PGPSignatureSubpacketGenerator hashedSubPacketGenerator = certKeySpec.getSubpacketGenerator(); - hashedSubPacketGenerator.setPrimaryUserID(false, true); - if (expirationDate != null) { - SignatureSubpacketGeneratorUtil.setExpirationDateInSubpacketGenerator( - expirationDate, new Date(), hashedSubPacketGenerator); - } - PGPSignatureSubpacketVector hashedSubPackets = hashedSubPacketGenerator.generate(); - - // Generator which the user can get the key pair from - PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, hashedSubPackets); - - addSubKeys(certKey, ringGenerator); - - // Generate secret key ring with only primary user id - PGPSecretKeyRing secretKeyRing = ringGenerator.generateSecretKeyRing(); - - Iterator secretKeys = secretKeyRing.getSecretKeys(); - - // Attempt to add additional user-ids to the primary public key - PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey(); - PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor); - for (String additionalUserId : additionalUserIds) { - signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); - PGPSignature additionalUserIdSignature = - signatureGenerator.generateCertification(additionalUserId, primaryPubKey); - primaryPubKey = PGPPublicKey.addCertification(primaryPubKey, - additionalUserId, additionalUserIdSignature); - } - - // "reassemble" secret key ring with modified primary key - PGPSecretKey primarySecKey = new PGPSecretKey( - privateKey, - primaryPubKey, digestCalculator, true, secretKeyEncryptor); - List secretKeyList = new ArrayList<>(); - secretKeyList.add(primarySecKey); - while (secretKeys.hasNext()) { - secretKeyList.add(secretKeys.next()); - } - secretKeyRing = new PGPSecretKeyRing(secretKeyList); - - return secretKeyRing; - } - - private PGPKeyRingGenerator buildRingGenerator(PGPKeyPair certKey, - PGPContentSignerBuilder signer, - PGPSignatureSubpacketVector hashedSubPackets) - throws PGPException { - return new PGPKeyRingGenerator( - SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey, - userId, digestCalculator, - hashedSubPackets, null, signer, secretKeyEncryptor); - } - - private void addSubKeys(PGPKeyPair primaryKey, PGPKeyRingGenerator ringGenerator) - throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException { - for (KeySpec subKeySpec : keySpecs) { - PGPKeyPair subKey = generateKeyPair(subKeySpec); - if (subKeySpec.isInheritedSubPackets()) { - ringGenerator.addSubKey(subKey); - } else { - PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets(); - try { - hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets); - } catch (IOException e) { - throw new PGPException("Exception while adding primary key binding signature to signing subkey", e); - } - ringGenerator.addSubKey(subKey, hashedSubpackets, null); - } - } - } - - private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets) throws PGPException, IOException { - int keyFlagMask = hashedSubpackets.getKeyFlags(); - if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) { - return hashedSubpackets; - } - - PGPSignatureGenerator bindingSignatureGenerator = new PGPSignatureGenerator(buildContentSigner(subKey)); - bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.getCode(), subKey.getPrivateKey()); - PGPSignature primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.getPublicKey(), subKey.getPublicKey()); - PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(hashedSubpackets); - subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig); - return subpacketGenerator.generate(); - } - - private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) { - HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm(); - return ImplementationFactory.getInstance().getPGPContentSignerBuilder( - certKey.getPublicKey().getAlgorithm(), - hashAlgorithm.getAlgorithmId()); - } - - private PBESecretKeyEncryptor buildSecretKeyEncryptor() { - SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy() - .getDefaultSymmetricKeyAlgorithm(); - PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ? - null : // unencrypted key pair, otherwise AES-256 encrypted - ImplementationFactory.getInstance().getPBESecretKeyEncryptor( - keyEncryptionAlgorithm, digestCalculator, passphrase); - return encryptor; - } - - private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException { - PBESecretKeyDecryptor decryptor = passphrase == null || passphrase.isEmpty() ? - null : - ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase); - return decryptor; - } - - private PGPDigestCalculator buildDigestCalculator() throws PGPException { - return ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1); - } - } + private PGPDigestCalculator buildDigestCalculator() throws PGPException { + return ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1); } public static PGPKeyPair generateKeyPair(KeySpec spec) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilderInterface.java index 8ddaff34..1113158b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilderInterface.java @@ -25,50 +25,32 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.pgpainless.key.util.UserId; import org.pgpainless.util.Passphrase; -public interface KeyRingBuilderInterface { +public interface KeyRingBuilderInterface> { - KeyRingBuilderInterface withSubKey(@Nonnull KeySpec keySpec); - - WithPrimaryUserId withPrimaryKey(@Nonnull KeySpec keySpec); - - interface WithPrimaryUserId { - - default WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull UserId userId) { - return withPrimaryUserId(userId.toString()); - } - - WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull String userId); - - WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull byte[] userId); + B setPrimaryKey(@Nonnull KeySpec keySpec); + default B setPrimaryKey(@Nonnull KeySpecBuilder builder) { + return setPrimaryKey(builder.build()); } - interface WithAdditionalUserIdOrPassphrase { + B addSubkey(@Nonnull KeySpec keySpec); - default WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull UserId userId) { - return withAdditionalUserId(userId.toString()); - } - - /** - * Set an expiration date for the key. - * - * @param expirationDate date on which the key will expire. - * @return builder - */ - WithAdditionalUserIdOrPassphrase setExpirationDate(@Nonnull Date expirationDate); - - WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull String userId); - - WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull byte[] userId); - - Build withPassphrase(@Nonnull Passphrase passphrase); - - Build withoutPassphrase(); + default B addSubkey(@Nonnull KeySpecBuilder builder) { + return addSubkey(builder.build()); } - interface Build { + default B addUserId(UserId userId) { + return addUserId(userId.toString()); + } - PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, + B addUserId(@Nonnull String userId); + + B addUserId(@Nonnull byte[] userId); + + B setExpirationDate(@Nonnull Date expirationDate); + + B setPassphrase(@Nonnull Passphrase passphrase); + + PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException; - } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java index c7bd68dc..175ea7ac 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptDecryptTest.java @@ -88,14 +88,13 @@ public class EncryptDecryptTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._3072); PGPSecretKeyRing recipient = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder( - ElGamal.withLength(ElGamalLength._3072), - KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.RSA(RsaLength._4096), - KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER).build()) - .withPrimaryUserId("juliet@capulet.lit").withoutPassphrase().build(); + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder( + ElGamal.withLength(ElGamalLength._3072), + KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)) + .addUserId("juliet@capulet.lit").build(); encryptDecryptForSecretKeyRings(sender, recipient); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java index f0aef576..f8971467 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/EncryptionOptionsTest.java @@ -59,14 +59,13 @@ public class EncryptionOptionsTest { @BeforeAll public static void generateKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER) .build()) - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) .build()) - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE) .build()) - .withPrimaryUserId("test@pgpainless.org") - .withoutPassphrase() + .addUserId("test@pgpainless.org") .build(); publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); @@ -139,10 +138,9 @@ public class EncryptionOptionsTest { public void testAddRecipient_KeyWithoutEncryptionKeyFails() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { EncryptionOptions options = new EncryptionOptions(); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .build()) - .withPrimaryUserId("test@pgpainless.org") - .withoutPassphrase().build(); + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addUserId("test@pgpainless.org") + .build(); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); assertThrows(IllegalArgumentException.class, () -> options.addRecipient(publicKeys)); diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java index 75e847f5..e88c906f 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/example/GenerateKeys.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import java.util.Date; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; @@ -34,6 +35,7 @@ import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.PublicKeyAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.KeySpecBuilder; import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.ecc.EllipticCurve; import org.pgpainless.key.generation.type.eddsa.EdDSACurve; @@ -156,10 +158,11 @@ public class GenerateKeys { * algorithm preferences. * * If the target key amalgamation (key ring) should consist of more than just a single (sub-)key, start by providing - * the specifications for the subkeys first (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withSubKey(KeySpec)}) - * and add the primary key specification last (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withPrimaryKey(KeySpec)}. + * the primary key specification using {@link org.pgpainless.key.generation.KeyRingBuilder#setPrimaryKey(KeySpec)}. + * Any additional subkeys can be then added using {@link org.pgpainless.key.generation.KeyRingBuilder#addSubkey(KeySpec)}. * - * {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType, KeyFlag...)} method and providing a {@link KeyType}. + * {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType, KeyFlag, KeyFlag...)} + * method and providing a {@link KeyType}. * There are a bunch of factory methods for different {@link KeyType} implementations present in {@link KeyType} itself * (such as {@link KeyType#ECDH(EllipticCurve)}. {@link KeyFlag KeyFlags} determine * the use of the key, like encryption, signing data or certifying subkeys. @@ -171,16 +174,18 @@ public class GenerateKeys { * * Note, that if you set preferred algorithms, the preference lists are sorted from high priority to low priority. * - * When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#withPrimaryKey(KeySpec)}), + * When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#setPrimaryKey(KeySpecBuilder)}), * make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is a requirement * for primary keys. * * Furthermore you have to set at least the primary user-id via - * {@link org.pgpainless.key.generation.KeyRingBuilderInterface.WithPrimaryUserId#withPrimaryUserId(String)}, - * but you can also add additional user-ids via - * {@link org.pgpainless.key.generation.KeyRingBuilderInterface.WithAdditionalUserIdOrPassphrase#withAdditionalUserId(String)}. + * {@link org.pgpainless.key.generation.KeyRingBuilder#addUserId(String)}, + * but you can also add additional user-ids. * - * Lastly you can decide whether to set a passphrase to protect the secret key. + * If you want the key to expire at a certain point in time, call + * {@link org.pgpainless.key.generation.KeyRingBuilder#setExpirationDate(Date)}. + * Lastly you can decide whether to set a passphrase to protect the secret key using + * {@link org.pgpainless.key.generation.KeyRingBuilder#setPassphrase(Passphrase)}. * * @throws PGPException * @throws InvalidAlgorithmParameterException @@ -201,43 +206,35 @@ public class GenerateKeys { Passphrase passphrase = Passphrase.fromPassword("1nters3x"); PGPSecretKeyRing secretKey = PGPainless.generateKeyRing() + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), + // The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags + KeyFlag.CERTIFY_OTHER)) // Add the first subkey (in this case encryption) - .withSubKey( - KeySpec.getBuilder( - // We choose an ECDH key over the brainpoolp256r1 curve - KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1), - // Our key can encrypt both communication data, as well as data at rest - KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS - ) - // Optionally: Configure the subkey with custom algorithm preferences - // Is is recommended though to go with PGPainless' defaults which can be found in the - // AlgorithmSuite class. - .overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128) - .overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256) - .overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB) - .build() - ) + .addSubkey(KeySpec.getBuilder( + // We choose an ECDH key over the brainpoolp256r1 curve + KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1), + // Our key can encrypt both communication data, as well as data at rest + KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS + ) + // Optionally: Configure the subkey with custom algorithm preferences + // Is is recommended though to go with PGPainless' defaults which can be found in the + // AlgorithmSuite class. + .overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128) + .overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256) + .overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB) + .build()) // Add the second subkey (signing) - .withSubKey( - KeySpec.getBuilder( - KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), - // This key is used for creating signatures only - KeyFlag.SIGN_DATA - ).build() - ) - // Lastly we add the primary key (certification only in our case) - .withPrimaryKey( - KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), - // The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags - KeyFlag.CERTIFY_OTHER) - .build() - ) + .addSubkey(KeySpec.getBuilder( + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), + // This key is used for creating signatures only + KeyFlag.SIGN_DATA + )) // Set primary user-id - .withPrimaryUserId(userId) + .addUserId(userId) // Add an additional user id. This step can be repeated - .withAdditionalUserId(additionalUserId) + .addUserId(additionalUserId) // Set passphrase. Alternatively use .withoutPassphrase() to leave key unprotected. - .withPassphrase(passphrase) + .setPassphrase(passphrase) .build(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java index aeafaf59..b94d5622 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/BrainpoolKeyGenerationTest.java @@ -83,17 +83,15 @@ public class BrainpoolKeyGenerationTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA).build()) - .withSubKey(KeySpec.getBuilder( - KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .build()) - .withSubKey(KeySpec.getBuilder( - KeyType.RSA(RsaLength._3072), KeyFlag.SIGN_DATA) - .build()) - .withPrimaryKey(KeySpec.getBuilder( - KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.CERTIFY_OTHER).build()) - .withPrimaryUserId(UserId.nameAndEmail("Alice", "alice@pgpainless.org")) - .withPassphrase(Passphrase.fromPassword("passphrase")) + .setPrimaryKey(KeySpec.getBuilder( + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)) + .addSubkey(KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)) + .addSubkey(KeySpec.getBuilder( + KeyType.RSA(RsaLength._3072), KeyFlag.SIGN_DATA)) + .addUserId(UserId.nameAndEmail("Alice", "alice@pgpainless.org")) + .setPassphrase(Passphrase.fromPassword("passphrase")) .build(); for (PGPSecretKey key : secretKeys) { @@ -131,10 +129,9 @@ public class BrainpoolKeyGenerationTest { public PGPSecretKeyRing generateKey(KeySpec primaryKey, KeySpec subKey, String userId) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(subKey) - .withPrimaryKey(primaryKey) - .withPrimaryUserId(userId) - .withoutPassphrase() + .setPrimaryKey(primaryKey) + .addSubkey(subKey) + .addUserId(userId) .build(); return secretKeys; } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java index 65b7a71f..ff48d159 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/CertificationKeyMustBeAbleToCertifyTest.java @@ -46,11 +46,9 @@ public class CertificationKeyMustBeAbleToCertifyTest { for (KeyType type : typesIncapableOfCreatingVerifications) { assertThrows(IllegalArgumentException.class, () -> PGPainless .generateKeyRing() - .withPrimaryKey(KeySpec - .getBuilder(type, KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .build()) - .withPrimaryUserId("should@throw.ex") - .withoutPassphrase().build()); + .setPrimaryKey(KeySpec.getBuilder(type, KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addUserId("should@throw.ex") + .build()); } } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java index 9e60eca6..bf28aac0 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateEllipticCurveKeyTest.java @@ -41,13 +41,11 @@ public class GenerateEllipticCurveKeyTest { public void generateEllipticCurveKeys(ImplementationFactory implementationFactory) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing keyRing = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS).build()) - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.EDDSA(EdDSACurve._Ed25519), - KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .build()) - .withPrimaryUserId(UserId.onlyEmail("alice@wonderland.lit").toString()) - .withoutPassphrase() + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS)) + .addUserId(UserId.onlyEmail("alice@wonderland.lit").toString()) .build(); assertEquals(PublicKeyAlgorithm.EDDSA.getAlgorithmId(), keyRing.getPublicKey().getAlgorithm()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java index 33db6fc5..f3378df0 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithAdditionalUserIdTest.java @@ -47,16 +47,14 @@ public class GenerateKeyWithAdditionalUserIdTest { ImplementationFactory.setFactoryImplementation(implementationFactory); Date expiration = new Date(DateUtil.now().getTime() + 60 * 1000); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.RSA(RsaLength._3072), - KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryUserId(UserId.onlyEmail("primary@user.id")) - .withAdditionalUserId(UserId.onlyEmail("additional@user.id")) - .withAdditionalUserId(UserId.onlyEmail("additional2@user.id")) - .withAdditionalUserId("\ttrimThis@user.id ") + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) + .addUserId(UserId.onlyEmail("primary@user.id")) + .addUserId(UserId.onlyEmail("additional@user.id")) + .addUserId(UserId.onlyEmail("additional2@user.id")) + .addUserId("\ttrimThis@user.id ") .setExpirationDate(expiration) - .withoutPassphrase() .build(); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphraseTest.java similarity index 88% rename from pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java rename to pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphraseTest.java index 60e7c131..39d1e6ef 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphrase.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateWithEmptyPassphraseTest.java @@ -38,7 +38,7 @@ import org.pgpainless.util.Passphrase; * The issue is that the implementation of {@link Passphrase#emptyPassphrase()} would set the underlying * char array to null, which caused an NPE later on. */ -public class GenerateWithEmptyPassphrase { +public class GenerateWithEmptyPassphraseTest { @ParameterizedTest @MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories") @@ -46,12 +46,11 @@ public class GenerateWithEmptyPassphrase { ImplementationFactory.setFactoryImplementation(implementationFactory); assertNotNull(PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.RSA(RsaLength._3072), - KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryUserId("primary@user.id") - .withPassphrase(Passphrase.emptyPassphrase()) + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) + .addUserId("primary@user.id") + .setPassphrase(Passphrase.emptyPassphrase()) .build()); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java index f1413a97..fe2191e5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/KeyRingInfoTest.java @@ -221,16 +221,14 @@ public class KeyRingInfoTest { ImplementationFactory.setFactoryImplementation(implementationFactory); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder( KeyType.ECDH(EllipticCurve._BRAINPOOLP384R1), - KeyFlag.ENCRYPT_STORAGE).build()) - .withSubKey(KeySpec.getBuilder( - KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.SIGN_DATA) - .build()) - .withPrimaryKey(KeySpec.getBuilder( - KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER).build()) - .withPrimaryUserId(UserId.newBuilder().withName("Alice").withEmail("alice@pgpainless.org").build()) - .withoutPassphrase() + KeyFlag.ENCRYPT_STORAGE)) + .addSubkey(KeySpec.getBuilder( + KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.SIGN_DATA)) + .addUserId(UserId.newBuilder().withName("Alice").withEmail("alice@pgpainless.org").build()) .build(); Iterator keys = secretKeys.iterator(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java index fce78fcd..a6d9a006 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java @@ -51,16 +51,13 @@ public class UserIdRevocationTest { @Test public void testRevocationWithoutRevocationAttributes() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder( - KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.EDDSA(EdDSACurve._Ed25519), - KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .build()) - .withPrimaryUserId("primary@key.id") - .withAdditionalUserId("secondary@key.id") - .withoutPassphrase() + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder( + KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS)) + .addUserId("primary@key.id") + .addUserId("secondary@key.id") .build(); // make a copy with revoked subkey @@ -92,15 +89,12 @@ public class UserIdRevocationTest { @Test public void testRevocationWithRevocationReason() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS) - .build()) - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.EDDSA(EdDSACurve._Ed25519), - KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .build()) - .withPrimaryUserId("primary@key.id") - .withAdditionalUserId("secondary@key.id") - .withoutPassphrase() + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS)) + .addUserId("primary@key.id") + .addUserId("secondary@key.id") .build(); secretKeys = PGPainless.modifyKeyRing(secretKeys) @@ -147,6 +141,6 @@ public class UserIdRevocationTest { assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys) .revokeUserId("cryptie@encrypted.key", protector, RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED) - .withDescription("This is not a valid certification revocation reason."))); + .withDescription("This is not a valid certification revocation reason."))); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java index 7cee1a7c..fc71b3f3 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/BCUtilTest.java @@ -47,12 +47,12 @@ public class BCUtilTest { throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException { PGPSecretKeyRing sec = PGPainless.generateKeyRing() - .withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072), KeyFlag.ENCRYPT_COMMS).build()) - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.RSA(RsaLength._3072), - KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) - .build()) - .withPrimaryUserId("donald@duck.tails").withoutPassphrase().build(); + KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) + .addSubkey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072), KeyFlag.ENCRYPT_COMMS)) + .addUserId("donald@duck.tails") + .build(); PGPPublicKeyRing pub = KeyRingUtils.publicKeyRingFrom(sec); diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java index 05f676cf..ab3ba986 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/GuessPreferredHashAlgorithmTest.java @@ -41,14 +41,12 @@ public class GuessPreferredHashAlgorithmTest { @Test public void guessPreferredHashAlgorithmsAssumesHashAlgoUsedBySelfSig() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) .overridePreferredHashAlgorithms(new HashAlgorithm[] {}) .overridePreferredSymmetricKeyAlgorithms(new SymmetricKeyAlgorithm[] {}) - .overridePreferredCompressionAlgorithms(new CompressionAlgorithm[] {}) - .build()) - .withPrimaryUserId("test@test.test") - .withoutPassphrase() + .overridePreferredCompressionAlgorithms(new CompressionAlgorithm[] {})) + .addUserId("test@test.test") .build(); PGPPublicKey publicKey = secretKeys.getPublicKey(); diff --git a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java index 4bf93212..e08bd54a 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java +++ b/pgpainless-core/src/test/java/org/pgpainless/weird_keys/TestEncryptCommsStorageFlagsDifferentiated.java @@ -38,14 +38,13 @@ public class TestEncryptCommsStorageFlagsDifferentiated { @Test public void testThatEncryptionDifferentiatesBetweenPurposeKeyFlags() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .withPrimaryKey(KeySpec.getBuilder( + .setPrimaryKey(KeySpec.getBuilder( KeyType.RSA(RsaLength._3072), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_STORAGE // no ENCRYPT_COMMS - ).build()) - .withPrimaryUserId("cannot@encrypt.comms") - .withoutPassphrase() + )) + .addUserId("cannot@encrypt.comms") .build(); PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); From 708282ba0a1b1409dc58f01154a2c4770ce77ce5 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 20 Sep 2021 12:32:15 +0200 Subject: [PATCH 7/7] Update README --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 406d4eca..ad36f104 100644 --- a/README.md +++ b/README.md @@ -75,23 +75,19 @@ There are some predefined key archetypes, but it is possible to fully customize // Customized key PGPSecretKeyRing keyRing = PGPainless.generateKeyRing() - .withSubKey( + .setPrimaryKey(KeySpec.getBuilder( + RSA.withLength(RsaLength._8192), + KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)) + .addSubkey( KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA) .overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB) - .build() - ).withSubKey( + ).addSubkey( KeySpec.getBuilder( ECDH.fromCurve(EllipticCurve._P256), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .build() - ).withMasterKey( - KeySpec.getBuilder( - RSA.withLength(RsaLength._8192), - KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) - .build() - ).withPrimaryUserId("Juliet ") - .withAdditionalUserId("xmpp:juliet@capulet.lit") - .withPassphrase("romeo_oh_Romeo<3") + ).addUserId("Juliet ") + .addUserId("xmpp:juliet@capulet.lit") + .setPassphrase("romeo_oh_Romeo<3") .build(); ```