From 671d45a9116e36a3124610e735c3fb122968923b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 1 May 2023 10:15:24 +0200 Subject: [PATCH 001/483] PGPainless 1.5.2-rc2-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 46a173b3..564100d4 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.5.2-rc1' - isSnapshot = false + shortVersion = '1.5.2-rc2' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.73' From 9c81137f4884c9d401dbc0435d40ba872dcb704e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 13:51:34 +0200 Subject: [PATCH 002/483] Add template methods to generate RSA keys with primary and subkeys --- .../key/generation/KeyRingTemplates.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java index 42eb7efa..07f2235f 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java @@ -26,6 +26,78 @@ public final class KeyRingTemplates { } + /** + * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, + * a dedicated RSA subkey used for signing and a third RSA subkey used for encryption. + * + * @param userId userId or null + * @param length length of the RSA keys + * @return key + * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters + * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider + * @throws PGPException in case of an OpenPGP related error + */ + public PGPSecretKeyRing rsaKeyRing(@Nullable CharSequence userId, + @Nonnull RsaLength length) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + return rsaKeyRing(userId, length, Passphrase.emptyPassphrase()); + } + + /** + * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, + * a dedicated RSA subkey used for signing and a third RSA subkey used for encryption. + * + * @param userId userId or null + * @param length length of the RSA keys + * @param password passphrase to encrypt the key with + * @return key + * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters + * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider + * @throws PGPException in case of an OpenPGP related error + */ + public PGPSecretKeyRing rsaKeyRing(@Nullable CharSequence userId, + @Nonnull RsaLength length, + @Nonnull String password) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + Passphrase passphrase = Passphrase.emptyPassphrase(); + if (!isNullOrEmpty(password)) { + passphrase = Passphrase.fromPassword(password); + } + return rsaKeyRing(userId, length, passphrase); + } + + /** + * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, + * a dedicated RSA subkey used for signing and a third RSA subkey used for encryption. + * + * @param userId userId or null + * @param length length of the RSA keys + * @param passphrase passphrase to encrypt the key with + * @return key + * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters + * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider + * @throws PGPException in case of an OpenPGP related error + */ + public PGPSecretKeyRing rsaKeyRing(@Nullable CharSequence userId, + @Nonnull RsaLength length, + @Nonnull Passphrase passphrase) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + KeyRingBuilder builder = PGPainless.buildKeyRing() + .setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.SIGN_DATA)) + .addSubkey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)); + + if (userId != null) { + builder.addUserId(userId.toString()); + } + + if (!passphrase.isEmpty()) { + builder.setPassphrase(passphrase); + } + + return builder.build(); + } + /** * Creates a simple, unencrypted RSA KeyPair of length {@code length} with user-id {@code userId}. * The KeyPair consists of a single RSA master key which is used for signing, encryption and certification. From 8869d9bd783ead32c70725f7dd1cd17d070f085f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 13:51:59 +0200 Subject: [PATCH 003/483] Simplify key template methods by replacing String and UserID args with CharSequence --- .../key/generation/KeyRingTemplates.java | 113 +++--------------- 1 file changed, 19 insertions(+), 94 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java index 07f2235f..6966232b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingTemplates.java @@ -17,7 +17,6 @@ import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.eddsa.EdDSACurve; import org.pgpainless.key.generation.type.rsa.RsaLength; import org.pgpainless.key.generation.type.xdh.XDHSpec; -import org.pgpainless.key.util.UserId; import org.pgpainless.util.Passphrase; public final class KeyRingTemplates { @@ -111,46 +110,20 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing simpleRsaKeyRing(@Nullable UserId userId, @Nonnull RsaLength length) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - return simpleRsaKeyRing(userId == null ? null : userId.toString(), length); - } - - /** - * Creates a simple, unencrypted RSA KeyPair of length {@code length} with user-id {@code userId}. - * The KeyPair consists of a single RSA master key which is used for signing, encryption and certification. - * - * @param userId user id. - * @param length length in bits. - * - * @return {@link PGPSecretKeyRing} containing the KeyPair. - * - * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters - * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider - * @throws PGPException in case of an OpenPGP related error - */ - public PGPSecretKeyRing simpleRsaKeyRing(@Nullable String userId, @Nonnull RsaLength length) + public PGPSecretKeyRing simpleRsaKeyRing(@Nullable CharSequence userId, @Nonnull RsaLength length) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { return simpleRsaKeyRing(userId, length, Passphrase.emptyPassphrase()); } - /** - * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. - * The KeyPair consists of a single RSA master key which is used for signing, encryption and certification. - * - * @param userId user id. - * @param length length in bits. - * @param password Password of the key. Can be null for unencrypted keys. - * - * @return {@link PGPSecretKeyRing} containing the KeyPair. - * - * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters - * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider - * @throws PGPException in case of an OpenPGP related error - */ - public PGPSecretKeyRing simpleRsaKeyRing(@Nullable UserId userId, @Nonnull RsaLength length, @Nullable String password) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - return simpleRsaKeyRing(userId == null ? null : userId.toString(), length, password); + public PGPSecretKeyRing simpleRsaKeyRing(@Nullable CharSequence userId, @Nonnull RsaLength length, @Nonnull Passphrase passphrase) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + KeyRingBuilder builder = PGPainless.buildKeyRing() + .setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) + .setPassphrase(passphrase); + if (userId != null) { + builder.addUserId(userId.toString()); + } + return builder.build(); } /** @@ -167,7 +140,7 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing simpleRsaKeyRing(@Nullable String userId, @Nonnull RsaLength length, @Nullable String password) + public PGPSecretKeyRing simpleRsaKeyRing(@Nullable CharSequence userId, @Nonnull RsaLength length, @Nullable String password) throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { Passphrase passphrase = Passphrase.emptyPassphrase(); if (!isNullOrEmpty(password)) { @@ -176,17 +149,6 @@ public final class KeyRingTemplates { return simpleRsaKeyRing(userId, length, passphrase); } - public PGPSecretKeyRing simpleRsaKeyRing(@Nullable String userId, @Nonnull RsaLength length, @Nonnull Passphrase passphrase) - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { - KeyRingBuilder builder = PGPainless.buildKeyRing() - .setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) - .setPassphrase(passphrase); - if (userId != null) { - builder.addUserId(userId); - } - return builder.build(); - } - /** * Creates a key ring consisting of an ed25519 EdDSA primary key and a curve25519 XDH subkey. * The EdDSA primary key is used for signing messages and certifying the sub key. @@ -200,48 +162,11 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing simpleEcKeyRing(@Nullable UserId userId) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - return simpleEcKeyRing(userId == null ? null : userId.toString()); - } - - /** - * Creates a key ring consisting of an ed25519 EdDSA primary key and a curve25519 XDH subkey. - * The EdDSA primary key is used for signing messages and certifying the sub key. - * The XDH subkey is used for encryption and decryption of messages. - * - * @param userId user-id - * - * @return {@link PGPSecretKeyRing} containing the key pairs. - * - * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters - * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider - * @throws PGPException in case of an OpenPGP related error - */ - public PGPSecretKeyRing simpleEcKeyRing(@Nullable String userId) + public PGPSecretKeyRing simpleEcKeyRing(@Nullable CharSequence userId) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { return simpleEcKeyRing(userId, Passphrase.emptyPassphrase()); } - /** - * Creates a key ring consisting of an ed25519 EdDSA primary key and a curve25519 XDH subkey. - * The EdDSA primary key is used for signing messages and certifying the sub key. - * The XDH subkey is used for encryption and decryption of messages. - * - * @param userId user-id - * @param password Password of the private key. Can be null for an unencrypted key. - * - * @return {@link PGPSecretKeyRing} containing the key pairs. - * - * @throws InvalidAlgorithmParameterException in case of invalid key generation parameters - * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider - * @throws PGPException in case of an OpenPGP related error - */ - public PGPSecretKeyRing simpleEcKeyRing(@Nullable UserId userId, String password) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { - return simpleEcKeyRing(userId == null ? null : userId.toString(), password); - } - /** * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. * The EdDSA primary key is used for signing messages and certifying the sub key. @@ -256,7 +181,7 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing simpleEcKeyRing(@Nullable String userId, String password) + public PGPSecretKeyRing simpleEcKeyRing(@Nullable CharSequence userId, String password) throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { Passphrase passphrase = Passphrase.emptyPassphrase(); if (!isNullOrEmpty(password)) { @@ -265,14 +190,14 @@ public final class KeyRingTemplates { return simpleEcKeyRing(userId, passphrase); } - public PGPSecretKeyRing simpleEcKeyRing(@Nullable String userId, @Nonnull Passphrase passphrase) + public PGPSecretKeyRing simpleEcKeyRing(@Nullable CharSequence userId, @Nonnull Passphrase passphrase) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { KeyRingBuilder builder = PGPainless.buildKeyRing() .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)) .setPassphrase(passphrase); if (userId != null) { - builder.addUserId(userId); + builder.addUserId(userId.toString()); } return builder.build(); } @@ -288,7 +213,7 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing modernKeyRing(@Nullable String userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public PGPSecretKeyRing modernKeyRing(@Nullable CharSequence userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { return modernKeyRing(userId, Passphrase.emptyPassphrase()); } @@ -304,13 +229,13 @@ public final class KeyRingTemplates { * @throws NoSuchAlgorithmException in case of missing algorithm implementation in the crypto provider * @throws PGPException in case of an OpenPGP related error */ - public PGPSecretKeyRing modernKeyRing(@Nullable String userId, @Nullable String password) + public PGPSecretKeyRing modernKeyRing(@Nullable CharSequence userId, @Nullable String password) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { Passphrase passphrase = (password != null ? Passphrase.fromPassword(password) : Passphrase.emptyPassphrase()); return modernKeyRing(userId, passphrase); } - public PGPSecretKeyRing modernKeyRing(@Nullable String userId, @Nonnull Passphrase passphrase) + public PGPSecretKeyRing modernKeyRing(@Nullable CharSequence userId, @Nonnull Passphrase passphrase) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { KeyRingBuilder builder = PGPainless.buildKeyRing() .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) @@ -318,7 +243,7 @@ public final class KeyRingTemplates { .addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)) .setPassphrase(passphrase); if (userId != null) { - builder.addUserId(userId); + builder.addUserId(userId.toString()); } return builder.build(); } From a8ab93a49a3faff3f0c9bd7d3074e53fa0f46cee Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 14:07:33 +0200 Subject: [PATCH 004/483] SOP: GenerateKey with --profile=rfc4880 now generates RSA key with subkeys --- .../src/main/java/org/pgpainless/sop/GenerateKeyImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java index ba788dac..f86d2893 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java @@ -126,7 +126,7 @@ public class GenerateKeyImpl implements GenerateKey { // RSA 4096 else if (profile.equals(RSA4096_PROFILE.getName())) { key = PGPainless.generateKeyRing() - .simpleRsaKeyRing(primaryUserId, RsaLength._4096, passphrase); + .rsaKeyRing(primaryUserId, RsaLength._4096, passphrase); } else { // Missing else-if branch for profile. Oops. From 15f6cc70b1979882a0ccb0b68db4c0c32e534a86 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 14:30:08 +0200 Subject: [PATCH 005/483] Add MessageMetadata.getRecipientKeyIds() Fixes #376 --- .../decryption_verification/MessageMetadata.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java index 648b99ab..cc97e81a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/MessageMetadata.java @@ -92,6 +92,21 @@ public class MessageMetadata { return false; } + /** + * Return a list containing all recipient keyIDs. + * + * @return list of recipients + */ + public List getRecipientKeyIds() { + List keyIds = new ArrayList<>(); + Iterator encLayers = getEncryptionLayers(); + while (encLayers.hasNext()) { + EncryptedData layer = encLayers.next(); + keyIds.addAll(layer.getRecipients()); + } + return keyIds; + } + public @Nonnull Iterator getEncryptionLayers() { return new LayerIterator(message) { @Override From 304350fe5c7b771fb6ab3f8a0dd17cbbd5efd0c2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 14:38:38 +0200 Subject: [PATCH 006/483] Add p-tags to EncryptionOptions javadoc --- .../pgpainless/encryption_signing/EncryptionOptions.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java index b5baee83..2d0fb156 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java @@ -33,7 +33,7 @@ import org.pgpainless.util.Passphrase; /** * Options for the encryption process. * This class can be used to set encryption parameters, like encryption keys and passphrases, algorithms etc. - * + *

* A typical use might look like follows: *

  * {@code
@@ -42,11 +42,11 @@ import org.pgpainless.util.Passphrase;
  * opt.addPassphrase(Passphrase.fromPassword("AdditionalDecryptionPassphrase123"));
  * }
  * 
- * + *

* To use a custom symmetric encryption algorithm, use {@link #overrideEncryptionAlgorithm(SymmetricKeyAlgorithm)}. * This will cause PGPainless to use the provided algorithm for message encryption, instead of negotiating an algorithm * by inspecting the provided recipient keys. - * + *

* By default, PGPainless will encrypt to all suitable, encryption capable subkeys on each recipient's certificate. * This behavior can be changed per recipient, e.g. by calling *

@@ -83,7 +83,7 @@ public class EncryptionOptions {
      * Factory method to create an {@link EncryptionOptions} object which will encrypt for keys
      * which carry either the {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS} or
      * {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE} flag.
-     *
+     * 

* Use this if you are not sure. * * @return encryption options From 64c6d7a90409c57837e47cb5ee1eb4f95c73a5a4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 14:38:52 +0200 Subject: [PATCH 007/483] Annotate EncryptionOptions methods with @Nonnull --- .../encryption_signing/EncryptionOptions.java | 34 +++++++++++-------- .../EncryptionOptionsTest.java | 6 ++-- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java index 2d0fb156..63320853 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionOptions.java @@ -75,7 +75,7 @@ public class EncryptionOptions { this(EncryptionPurpose.ANY); } - public EncryptionOptions(EncryptionPurpose purpose) { + public EncryptionOptions(@Nonnull EncryptionPurpose purpose) { this.purpose = purpose; } @@ -118,7 +118,7 @@ public class EncryptionOptions { * @param keys keys * @return this */ - public EncryptionOptions addRecipients(Iterable keys) { + public EncryptionOptions addRecipients(@Nonnull Iterable keys) { if (!keys.iterator().hasNext()) { throw new IllegalArgumentException("Set of recipient keys cannot be empty."); } @@ -154,7 +154,7 @@ public class EncryptionOptions { * @param userId user id * @return this */ - public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId) { + public EncryptionOptions addRecipient(@Nonnull PGPPublicKeyRing key, @Nonnull CharSequence userId) { return addRecipient(key, userId, encryptionKeySelector); } @@ -167,11 +167,13 @@ public class EncryptionOptions { * @param encryptionKeySelectionStrategy strategy to select one or more encryption subkeys to encrypt to * @return this */ - public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId, EncryptionKeySelector encryptionKeySelectionStrategy) { + public EncryptionOptions addRecipient(@Nonnull PGPPublicKeyRing key, + @Nonnull CharSequence userId, + @Nonnull EncryptionKeySelector encryptionKeySelectionStrategy) { KeyRingInfo info = new KeyRingInfo(key, new Date()); List encryptionSubkeys = encryptionKeySelectionStrategy - .selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose)); + .selectEncryptionSubkeys(info.getEncryptionSubkeys(userId.toString(), purpose)); if (encryptionSubkeys.isEmpty()) { throw new KeyException.UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key)); } @@ -179,7 +181,7 @@ public class EncryptionOptions { for (PGPPublicKey encryptionSubkey : encryptionSubkeys) { SubkeyIdentifier keyId = new SubkeyIdentifier(key, encryptionSubkey.getKeyID()); keyRingInfo.put(keyId, info); - keyViews.put(keyId, new KeyAccessor.ViaUserId(info, keyId, userId)); + keyViews.put(keyId, new KeyAccessor.ViaUserId(info, keyId, userId.toString())); addRecipientKey(key, encryptionSubkey); } @@ -192,7 +194,7 @@ public class EncryptionOptions { * @param key key ring * @return this */ - public EncryptionOptions addRecipient(PGPPublicKeyRing key) { + public EncryptionOptions addRecipient(@Nonnull PGPPublicKeyRing key) { return addRecipient(key, encryptionKeySelector); } @@ -203,7 +205,8 @@ public class EncryptionOptions { * @param encryptionKeySelectionStrategy strategy used to select one or multiple encryption subkeys. * @return this */ - public EncryptionOptions addRecipient(PGPPublicKeyRing key, EncryptionKeySelector encryptionKeySelectionStrategy) { + public EncryptionOptions addRecipient(@Nonnull PGPPublicKeyRing key, + @Nonnull EncryptionKeySelector encryptionKeySelectionStrategy) { Date evaluationDate = new Date(); KeyRingInfo info; info = new KeyRingInfo(key, evaluationDate); @@ -234,7 +237,8 @@ public class EncryptionOptions { return this; } - private void addRecipientKey(PGPPublicKeyRing keyRing, PGPPublicKey key) { + private void addRecipientKey(@Nonnull PGPPublicKeyRing keyRing, + @Nonnull PGPPublicKey key) { encryptionKeys.add(new SubkeyIdentifier(keyRing, key.getKeyID())); PGPKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory .getInstance().getPublicKeyKeyEncryptionMethodGenerator(key); @@ -247,7 +251,7 @@ public class EncryptionOptions { * @param passphrase passphrase * @return this */ - public EncryptionOptions addPassphrase(Passphrase passphrase) { + public EncryptionOptions addPassphrase(@Nonnull Passphrase passphrase) { if (passphrase.isEmpty()) { throw new IllegalArgumentException("Passphrase must not be empty."); } @@ -267,7 +271,7 @@ public class EncryptionOptions { * @param encryptionMethod encryption method * @return this */ - public EncryptionOptions addEncryptionMethod(PGPKeyEncryptionMethodGenerator encryptionMethod) { + public EncryptionOptions addEncryptionMethod(@Nonnull PGPKeyEncryptionMethodGenerator encryptionMethod) { encryptionMethods.add(encryptionMethod); return this; } @@ -303,7 +307,7 @@ public class EncryptionOptions { * @param encryptionAlgorithm encryption algorithm override * @return this */ - public EncryptionOptions overrideEncryptionAlgorithm(SymmetricKeyAlgorithm encryptionAlgorithm) { + public EncryptionOptions overrideEncryptionAlgorithm(@Nonnull SymmetricKeyAlgorithm encryptionAlgorithm) { if (encryptionAlgorithm == SymmetricKeyAlgorithm.NULL) { throw new IllegalArgumentException("Plaintext encryption can only be used to denote unencrypted secret keys."); } @@ -322,7 +326,7 @@ public class EncryptionOptions { } public interface EncryptionKeySelector { - List selectEncryptionSubkeys(List encryptionCapableKeys); + List selectEncryptionSubkeys(@Nonnull List encryptionCapableKeys); } /** @@ -333,7 +337,7 @@ public class EncryptionOptions { public static EncryptionKeySelector encryptToFirstSubkey() { return new EncryptionKeySelector() { @Override - public List selectEncryptionSubkeys(List encryptionCapableKeys) { + public List selectEncryptionSubkeys(@Nonnull List encryptionCapableKeys) { return encryptionCapableKeys.isEmpty() ? Collections.emptyList() : Collections.singletonList(encryptionCapableKeys.get(0)); } }; @@ -347,7 +351,7 @@ public class EncryptionOptions { public static EncryptionKeySelector encryptToAllCapableSubkeys() { return new EncryptionKeySelector() { @Override - public List selectEncryptionSubkeys(List encryptionCapableKeys) { + public List selectEncryptionSubkeys(@Nonnull List encryptionCapableKeys) { return encryptionCapableKeys; } }; 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 3436ba69..7d2fa453 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 @@ -36,6 +36,8 @@ import org.pgpainless.key.generation.type.xdh.XDHSpec; import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.util.Passphrase; +import javax.annotation.Nonnull; + public class EncryptionOptionsTest { private static PGPSecretKeyRing secretKeys; @@ -149,7 +151,7 @@ public class EncryptionOptionsTest { assertThrows(KeyException.UnacceptableEncryptionKeyException.class, () -> options.addRecipient(publicKeys, new EncryptionOptions.EncryptionKeySelector() { @Override - public List selectEncryptionSubkeys(List encryptionCapableKeys) { + public List selectEncryptionSubkeys(@Nonnull List encryptionCapableKeys) { return Collections.emptyList(); } })); @@ -157,7 +159,7 @@ public class EncryptionOptionsTest { assertThrows(KeyException.UnacceptableEncryptionKeyException.class, () -> options.addRecipient(publicKeys, "test@pgpainless.org", new EncryptionOptions.EncryptionKeySelector() { @Override - public List selectEncryptionSubkeys(List encryptionCapableKeys) { + public List selectEncryptionSubkeys(@Nonnull List encryptionCapableKeys) { return Collections.emptyList(); } })); From 1d26751b45b00519894f91f80dbf5d3bc52b4d04 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 15:59:21 +0200 Subject: [PATCH 008/483] Remove unused KeyRingEditorTest --- .../key/modification/KeyRingEditorTest.java | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/key/modification/KeyRingEditorTest.java diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/KeyRingEditorTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/KeyRingEditorTest.java deleted file mode 100644 index 68774cfc..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/KeyRingEditorTest.java +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.key.modification; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import org.junit.jupiter.api.Test; -import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor; - -public class KeyRingEditorTest { - - @Test - public void testConstructorThrowsNpeForNull() { - assertThrows(NullPointerException.class, - () -> new SecretKeyRingEditor(null)); - } -} From 3b8a1b47d7e2cd24d63a562cd246254159b645b5 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 16:03:12 +0200 Subject: [PATCH 009/483] Add javadoc p-tags --- .../src/main/java/org/pgpainless/PGPainless.java | 6 +++--- .../encryption_signing/SigningOptions.java | 12 ++++++------ .../java/org/pgpainless/key/info/KeyAccessor.java | 2 +- .../java/org/pgpainless/key/info/KeyRingInfo.java | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 6da77c80..16928210 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -152,7 +152,7 @@ public final class PGPainless { /** * Make changes to a secret key. * This method can be used to change key expiration dates and passphrases, or add/revoke subkeys. - * + *

* After making the desired changes in the builder, the modified key ring can be extracted using {@link SecretKeyRingEditorInterface#done()}. * * @param secretKeys secret key ring @@ -165,7 +165,7 @@ public final class PGPainless { /** * Make changes to a secret key at the given reference time. * This method can be used to change key expiration dates and passphrases, or add/revoke user-ids and subkeys. - * + *

* After making the desired changes in the builder, the modified key can be extracted using {@link SecretKeyRingEditorInterface#done()}. * * @param secretKeys secret key ring @@ -179,7 +179,7 @@ public final class PGPainless { /** * Quickly access information about a {@link org.bouncycastle.openpgp.PGPPublicKeyRing} / {@link PGPSecretKeyRing}. * This method can be used to determine expiration dates, key flags and other information about a key. - * + *

* To evaluate a key at a given date (e.g. to determine if the key was allowed to create a certain signature) * use {@link #inspectKeyRing(PGPKeyRing, Date)} instead. * diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java index 0af07fc9..77f95efb 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java @@ -158,7 +158,7 @@ public final class SigningOptions { * Add an inline-signature. * Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use * of one-pass-signature packets. - * + *

* This method uses the passed in user-id to select user-specific hash algorithms. * * @param secretKeyDecryptor decryptor to unlock the signing secret key @@ -182,7 +182,7 @@ public final class SigningOptions { * Add an inline-signature. * Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use * of one-pass-signature packets. - * + *

* This method uses the passed in user-id to select user-specific hash algorithms. * * @param secretKeyDecryptor decryptor to unlock the signing secret key @@ -295,7 +295,7 @@ public final class SigningOptions { * Detached signatures are not being added into the PGP message itself. * Instead, they can be distributed separately to the message. * Detached signatures are useful if the data that is being signed shall not be modified (e.g. when signing a file). - * + *

* This method uses the passed in user-id to select user-specific hash algorithms. * * @param secretKeyDecryptor decryptor to unlock the secret signing key @@ -320,7 +320,7 @@ public final class SigningOptions { * Detached signatures are not being added into the PGP message itself. * Instead, they can be distributed separately to the message. * Detached signatures are useful if the data that is being signed shall not be modified (e.g. when signing a file). - * + *

* This method uses the passed in user-id to select user-specific hash algorithms. * * @param secretKeyDecryptor decryptor to unlock the secret signing key @@ -406,7 +406,7 @@ public final class SigningOptions { /** * Negotiate, which hash algorithm to use. - * + *

* This method gives the highest priority to the algorithm override, which can be set via {@link #overrideHashAlgorithm(HashAlgorithm)}. * After that, the signing keys hash algorithm preferences are iterated to find the first acceptable algorithm. * Lastly, should no acceptable algorithm be found, the {@link Policy Policies} default signature hash algorithm is @@ -451,7 +451,7 @@ public final class SigningOptions { /** * Override hash algorithm negotiation by dictating which hash algorithm needs to be used. * If no override has been set, an accetable algorithm will be negotiated instead. - * + *

* Note: To override the hash algorithm for signing, call this method *before* calling * {@link #addInlineSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)} or * {@link #addDetachedSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)}. diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java index 5fa71d46..48102931 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java @@ -28,7 +28,7 @@ public abstract class KeyAccessor { /** * Depending on the way we address the key (key-id or user-id), return the respective {@link PGPSignature} * which contains the algorithm preferences we are going to use. - * + *

* If we address a key via its user-id, we want to rely on the algorithm preferences in the user-id certification, * while we would instead rely on those in the direct-key signature if we'd address the key by key-id. * diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java index 75d9e16c..45de52f3 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java @@ -292,7 +292,7 @@ public class KeyRingInfo { /** * Return the current primary user-id of the key ring. - * + *

* Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet, * this method returns the first user-id on the key, otherwise null. * @@ -472,7 +472,7 @@ public class KeyRingInfo { /** * Return the latest direct-key self signature. - * + *

* Note: This signature might be expired (check with {@link SignatureUtils#isSignatureExpired(PGPSignature)}). * * @return latest direct key self-signature or null @@ -782,7 +782,7 @@ public class KeyRingInfo { * Return the latest date on which the key ring is still usable for the given key flag. * If only a subkey is carrying the required flag and the primary key expires earlier than the subkey, * the expiry date of the primary key is returned. - * + *

* This method might return null, if the primary key and a subkey with the required flag does not expire. * @param use key flag representing the use case, e.g. {@link KeyFlag#SIGN_DATA} or * {@link KeyFlag#ENCRYPT_COMMS}/{@link KeyFlag#ENCRYPT_STORAGE}. @@ -1133,7 +1133,7 @@ public class KeyRingInfo { /** * Returns true, if this {@link KeyRingInfo} is based on a {@link PGPSecretKeyRing}, which has a valid signing key * which is ready to be used (i.e. secret key is present and is not on a smart-card). - * + *

* If you just want to check, whether a key / certificate has signing capable subkeys, * use {@link #isSigningCapable()} instead. * From 953206b4ed39b692a4c7caa9d9b586382bf81729 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 16:03:50 +0200 Subject: [PATCH 010/483] Make more of the API null-safe by using @Nonnull/@Nullable --- .../main/java/org/pgpainless/PGPainless.java | 26 ++- .../OpenPgpMessageInputStream.java | 2 +- .../BcPGPHashContextContentSignerBuilder.java | 9 +- .../encryption_signing/SigningOptions.java | 114 ++++++----- .../org/pgpainless/key/info/KeyAccessor.java | 39 +++- .../org/pgpainless/key/info/KeyRingInfo.java | 183 ++++++++++++------ .../secretkeyring/SecretKeyRingEditor.java | 10 +- .../algorithm/RevocationStateTest.java | 5 - .../key/info/UserIdRevocationTest.java | 1 + .../SignatureSubpacketsUtilTest.java | 3 + ...artyCertificationSignatureBuilderTest.java | 6 +- .../subpackets/SignatureSubpacketsTest.java | 2 +- 12 files changed, 263 insertions(+), 137 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 16928210..3da54177 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -40,6 +40,7 @@ public final class PGPainless { * Generate a fresh OpenPGP key ring from predefined templates. * @return templates */ + @Nonnull public static KeyRingTemplates generateKeyRing() { return new KeyRingTemplates(); } @@ -49,6 +50,7 @@ public final class PGPainless { * * @return builder */ + @Nonnull public static KeyRingBuilder buildKeyRing() { return new KeyRingBuilder(); } @@ -57,6 +59,7 @@ public final class PGPainless { * Read an existing OpenPGP key ring. * @return builder */ + @Nonnull public static KeyRingReader readKeyRing() { return new KeyRingReader(); } @@ -67,6 +70,7 @@ public final class PGPainless { * @param secretKey secret key * @return public key certificate */ + @Nonnull public static PGPPublicKeyRing extractCertificate(@Nonnull PGPSecretKeyRing secretKey) { return KeyRingUtils.publicKeyRingFrom(secretKey); } @@ -79,6 +83,7 @@ public final class PGPainless { * @return merged certificate * @throws PGPException in case of an error */ + @Nonnull public static PGPPublicKeyRing mergeCertificate( @Nonnull PGPPublicKeyRing originalCopy, @Nonnull PGPPublicKeyRing updatedCopy) @@ -94,6 +99,7 @@ public final class PGPainless { * * @throws IOException in case of an error in the {@link ArmoredOutputStream} */ + @Nonnull public static String asciiArmor(@Nonnull PGPKeyRing key) throws IOException { if (key instanceof PGPSecretKeyRing) { @@ -111,6 +117,7 @@ public final class PGPainless { * * @throws IOException in case of an error in the {@link ArmoredOutputStream} */ + @Nonnull public static String asciiArmor(@Nonnull PGPSignature signature) throws IOException { return ArmorUtils.toAsciiArmoredString(signature); @@ -136,6 +143,7 @@ public final class PGPainless { * * @return builder */ + @Nonnull public static EncryptionBuilder encryptAndOrSign() { return new EncryptionBuilder(); } @@ -145,6 +153,7 @@ public final class PGPainless { * * @return builder */ + @Nonnull public static DecryptionBuilder decryptAndOrVerify() { return new DecryptionBuilder(); } @@ -158,8 +167,9 @@ public final class PGPainless { * @param secretKeys secret key ring * @return builder */ - public static SecretKeyRingEditorInterface modifyKeyRing(PGPSecretKeyRing secretKeys) { - return modifyKeyRing(secretKeys, null); + @Nonnull + public static SecretKeyRingEditorInterface modifyKeyRing(@Nonnull PGPSecretKeyRing secretKeys) { + return modifyKeyRing(secretKeys, new Date()); } /** @@ -172,7 +182,9 @@ public final class PGPainless { * @param referenceTime reference time used as signature creation date * @return builder */ - public static SecretKeyRingEditorInterface modifyKeyRing(PGPSecretKeyRing secretKeys, Date referenceTime) { + @Nonnull + public static SecretKeyRingEditorInterface modifyKeyRing(@Nonnull PGPSecretKeyRing secretKeys, + @Nonnull Date referenceTime) { return new SecretKeyRingEditor(secretKeys, referenceTime); } @@ -186,7 +198,8 @@ public final class PGPainless { * @param keyRing key ring * @return access object */ - public static KeyRingInfo inspectKeyRing(PGPKeyRing keyRing) { + @Nonnull + public static KeyRingInfo inspectKeyRing(@Nonnull PGPKeyRing keyRing) { return new KeyRingInfo(keyRing); } @@ -198,7 +211,8 @@ public final class PGPainless { * @param referenceTime date of inspection * @return access object */ - public static KeyRingInfo inspectKeyRing(PGPKeyRing keyRing, Date referenceTime) { + @Nonnull + public static KeyRingInfo inspectKeyRing(@Nonnull PGPKeyRing keyRing, @Nonnull Date referenceTime) { return new KeyRingInfo(keyRing, referenceTime); } @@ -207,6 +221,7 @@ public final class PGPainless { * * @return policy */ + @Nonnull public static Policy getPolicy() { return Policy.getInstance(); } @@ -216,6 +231,7 @@ public final class PGPainless { * * @return builder */ + @Nonnull public static CertifyCertificate certify() { return new CertifyCertificate(); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java index d51a6cf7..04d823d1 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java @@ -359,7 +359,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { PGPCompressedData compressedData = packetInputStream.readCompressedData(); // Extract Metadata MessageMetadata.CompressedData compressionLayer = new MessageMetadata.CompressedData( - CompressionAlgorithm.fromId(compressedData.getAlgorithm()), + CompressionAlgorithm.requireFromId(compressedData.getAlgorithm()), metadata.depth + 1); LOGGER.debug("Compressed Data Packet (" + compressionLayer.algorithm + ") at depth " + metadata.depth + " encountered"); diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.java index df6f6ca3..5cdf9e36 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.java @@ -44,10 +44,15 @@ class BcPGPHashContextContentSignerBuilder extends PGPHashContextContentSignerBu BcPGPHashContextContentSignerBuilder(MessageDigest messageDigest) { this.messageDigest = messageDigest; - this.hashAlgorithm = HashAlgorithm.fromName(messageDigest.getAlgorithm()); + this.hashAlgorithm = requireFromName(messageDigest.getAlgorithm()); + } + + private static HashAlgorithm requireFromName(String digestName) { + HashAlgorithm hashAlgorithm = HashAlgorithm.fromName(digestName); if (hashAlgorithm == null) { - throw new IllegalArgumentException("Cannot recognize OpenPGP Hash Algorithm: " + messageDigest.getAlgorithm()); + throw new IllegalArgumentException("Cannot recognize OpenPGP Hash Algorithm: " + digestName); } + return hashAlgorithm; } @Override diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java index 77f95efb..a899bd12 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/SigningOptions.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPException; @@ -46,7 +47,9 @@ public final class SigningOptions { private final boolean detached; private final HashAlgorithm hashAlgorithm; - private SigningMethod(PGPSignatureGenerator signatureGenerator, boolean detached, HashAlgorithm hashAlgorithm) { + private SigningMethod(@Nonnull PGPSignatureGenerator signatureGenerator, + boolean detached, + @Nonnull HashAlgorithm hashAlgorithm) { this.signatureGenerator = signatureGenerator; this.detached = detached; this.hashAlgorithm = hashAlgorithm; @@ -60,7 +63,8 @@ public final class SigningOptions { * @param hashAlgorithm hash algorithm used to generate the signature * @return inline signing method */ - public static SigningMethod inlineSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) { + public static SigningMethod inlineSignature(@Nonnull PGPSignatureGenerator signatureGenerator, + @Nonnull HashAlgorithm hashAlgorithm) { return new SigningMethod(signatureGenerator, false, hashAlgorithm); } @@ -73,7 +77,8 @@ public final class SigningOptions { * @param hashAlgorithm hash algorithm used to generate the signature * @return detached signing method */ - public static SigningMethod detachedSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) { + public static SigningMethod detachedSignature(@Nonnull PGPSignatureGenerator signatureGenerator, + @Nonnull HashAlgorithm hashAlgorithm) { return new SigningMethod(signatureGenerator, true, hashAlgorithm); } @@ -93,6 +98,7 @@ public final class SigningOptions { private final Map signingMethods = new HashMap<>(); private HashAlgorithm hashAlgorithmOverride; + @Nonnull public static SigningOptions get() { return new SigningOptions(); } @@ -107,8 +113,9 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be unlocked or a signing method cannot be created */ - public SigningOptions addSignature(SecretKeyRingProtector signingKeyProtector, - PGPSecretKeyRing signingKey) + @Nonnull + public SigningOptions addSignature(@Nonnull SecretKeyRingProtector signingKeyProtector, + @Nonnull PGPSecretKeyRing signingKey) throws PGPException { return addInlineSignature(signingKeyProtector, signingKey, DocumentSignatureType.BINARY_DOCUMENT); } @@ -124,9 +131,10 @@ public final class SigningOptions { * @throws KeyException if something is wrong with any of the keys * @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be created */ - public SigningOptions addInlineSignatures(SecretKeyRingProtector secrectKeyDecryptor, - Iterable signingKeys, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addInlineSignatures(@Nonnull SecretKeyRingProtector secrectKeyDecryptor, + @Nonnull Iterable signingKeys, + @Nonnull DocumentSignatureType signatureType) throws KeyException, PGPException { for (PGPSecretKeyRing signingKey : signingKeys) { addInlineSignature(secrectKeyDecryptor, signingKey, signatureType); @@ -147,9 +155,10 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be unlocked or the signing method cannot be created */ - public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addInlineSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nonnull DocumentSignatureType signatureType) throws KeyException, PGPException { return addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType); } @@ -170,10 +179,11 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be unlocked or the signing method cannot be created */ - public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - String userId, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addInlineSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nullable CharSequence userId, + @Nonnull DocumentSignatureType signatureType) throws KeyException, PGPException { return addInlineSignature(secretKeyDecryptor, secretKey, userId, signatureType, null); } @@ -195,17 +205,18 @@ public final class SigningOptions { * @throws KeyException if the key is invalid * @throws PGPException if the key cannot be unlocked or the signing method cannot be created */ - public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - String userId, - DocumentSignatureType signatureType, + @Nonnull + public SigningOptions addInlineSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nullable CharSequence userId, + @Nonnull DocumentSignatureType signatureType, @Nullable BaseSignatureSubpackets.Callback subpacketsCallback) throws KeyException, PGPException { KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date()); if (userId != null && !keyRingInfo.isUserIdValid(userId)) { throw new KeyException.UnboundUserIdException( OpenPgpFingerprint.of(secretKey), - userId, + userId.toString(), keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId) ); @@ -242,9 +253,10 @@ public final class SigningOptions { * @throws KeyException if something is wrong with any of the keys * @throws PGPException if any of the keys cannot be validated or unlocked, or if any signing method cannot be created */ - public SigningOptions addDetachedSignatures(SecretKeyRingProtector secretKeyDecryptor, - Iterable signingKeys, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addDetachedSignatures(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull Iterable signingKeys, + @Nonnull DocumentSignatureType signatureType) throws PGPException { for (PGPSecretKeyRing signingKey : signingKeys) { addDetachedSignature(secretKeyDecryptor, signingKey, signatureType); @@ -263,8 +275,9 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created */ - public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing signingKey) + @Nonnull + public SigningOptions addDetachedSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing signingKey) throws PGPException { return addDetachedSignature(secretKeyDecryptor, signingKey, DocumentSignatureType.BINARY_DOCUMENT); } @@ -283,9 +296,10 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created */ - public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addDetachedSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nonnull DocumentSignatureType signatureType) throws PGPException { return addDetachedSignature(secretKeyDecryptor, secretKey, null, signatureType); } @@ -307,10 +321,11 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created */ - public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - String userId, - DocumentSignatureType signatureType) + @Nonnull + public SigningOptions addDetachedSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nullable CharSequence userId, + @Nonnull DocumentSignatureType signatureType) throws PGPException { return addDetachedSignature(secretKeyDecryptor, secretKey, userId, signatureType, null); } @@ -333,17 +348,18 @@ public final class SigningOptions { * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created */ - public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor, - PGPSecretKeyRing secretKey, - String userId, - DocumentSignatureType signatureType, + @Nonnull + public SigningOptions addDetachedSignature(@Nonnull SecretKeyRingProtector secretKeyDecryptor, + @Nonnull PGPSecretKeyRing secretKey, + @Nullable CharSequence userId, + @Nonnull DocumentSignatureType signatureType, @Nullable BaseSignatureSubpackets.Callback subpacketCallback) throws PGPException { KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date()); if (userId != null && !keyRingInfo.isUserIdValid(userId)) { throw new KeyException.UnboundUserIdException( OpenPgpFingerprint.of(secretKey), - userId, + userId.toString(), keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId) ); @@ -369,11 +385,11 @@ public final class SigningOptions { return this; } - private void addSigningMethod(PGPSecretKeyRing secretKey, - PGPPrivateKey signingSubkey, + private void addSigningMethod(@Nonnull PGPSecretKeyRing secretKey, + @Nonnull PGPPrivateKey signingSubkey, @Nullable BaseSignatureSubpackets.Callback subpacketCallback, - HashAlgorithm hashAlgorithm, - DocumentSignatureType signatureType, + @Nonnull HashAlgorithm hashAlgorithm, + @Nonnull DocumentSignatureType signatureType, boolean detached) throws PGPException { SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(secretKey, signingSubkey.getKeyID()); @@ -416,7 +432,9 @@ public final class SigningOptions { * @param policy policy * @return selected hash algorithm */ - private HashAlgorithm negotiateHashAlgorithm(Set preferences, Policy policy) { + @Nonnull + private HashAlgorithm negotiateHashAlgorithm(@Nonnull Set preferences, + @Nonnull Policy policy) { if (hashAlgorithmOverride != null) { return hashAlgorithmOverride; } @@ -425,9 +443,10 @@ public final class SigningOptions { .negotiateHashAlgorithm(preferences); } - private PGPSignatureGenerator createSignatureGenerator(PGPPrivateKey privateKey, - HashAlgorithm hashAlgorithm, - DocumentSignatureType signatureType) + @Nonnull + private PGPSignatureGenerator createSignatureGenerator(@Nonnull PGPPrivateKey privateKey, + @Nonnull HashAlgorithm hashAlgorithm, + @Nonnull DocumentSignatureType signatureType) throws PGPException { int publicKeyAlgorithm = privateKey.getPublicKeyPacket().getAlgorithm(); PGPContentSignerBuilder signerBuilder = ImplementationFactory.getInstance() @@ -444,6 +463,7 @@ public final class SigningOptions { * * @return signing methods */ + @Nonnull Map getSigningMethods() { return Collections.unmodifiableMap(signingMethods); } @@ -459,7 +479,8 @@ public final class SigningOptions { * @param hashAlgorithmOverride override hash algorithm * @return this */ - public SigningOptions overrideHashAlgorithm(HashAlgorithm hashAlgorithmOverride) { + @Nonnull + public SigningOptions overrideHashAlgorithm(@Nonnull HashAlgorithm hashAlgorithmOverride) { this.hashAlgorithmOverride = hashAlgorithmOverride; return this; } @@ -469,6 +490,7 @@ public final class SigningOptions { * * @return hash algorithm override */ + @Nullable public HashAlgorithm getHashAlgorithmOverride() { return hashAlgorithmOverride; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java index 48102931..8ab8a9c4 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyAccessor.java @@ -20,7 +20,7 @@ public abstract class KeyAccessor { protected final KeyRingInfo info; protected final SubkeyIdentifier key; - KeyAccessor(KeyRingInfo info, SubkeyIdentifier key) { + KeyAccessor(@Nonnull KeyRingInfo info, @Nonnull SubkeyIdentifier key) { this.info = info; this.key = key; } @@ -34,13 +34,15 @@ public abstract class KeyAccessor { * * @return signature */ - public abstract @Nonnull PGPSignature getSignatureWithPreferences(); + @Nonnull + public abstract PGPSignature getSignatureWithPreferences(); /** * Return preferred symmetric key encryption algorithms. * * @return preferred symmetric algorithms */ + @Nonnull public Set getPreferredSymmetricKeyAlgorithms() { return SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(getSignatureWithPreferences()); } @@ -50,6 +52,7 @@ public abstract class KeyAccessor { * * @return preferred hash algorithms */ + @Nonnull public Set getPreferredHashAlgorithms() { return SignatureSubpacketsUtil.parsePreferredHashAlgorithms(getSignatureWithPreferences()); } @@ -59,6 +62,7 @@ public abstract class KeyAccessor { * * @return preferred compression algorithms */ + @Nonnull public Set getPreferredCompressionAlgorithms() { return SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(getSignatureWithPreferences()); } @@ -78,13 +82,16 @@ public abstract class KeyAccessor { * @param key id of the subkey * @param userId user-id */ - public ViaUserId(KeyRingInfo info, SubkeyIdentifier key, String userId) { + public ViaUserId(@Nonnull KeyRingInfo info, + @Nonnull SubkeyIdentifier key, + @Nonnull String userId) { super(info, key); this.userId = userId; } @Override - public @Nonnull PGPSignature getSignatureWithPreferences() { + @Nonnull + public PGPSignature getSignatureWithPreferences() { PGPSignature signature = info.getLatestUserIdCertification(userId); if (signature != null) { return signature; @@ -104,19 +111,26 @@ public abstract class KeyAccessor { * @param info info about the key at a given date * @param key key-id */ - public ViaKeyId(KeyRingInfo info, SubkeyIdentifier key) { + public ViaKeyId(@Nonnull KeyRingInfo info, + @Nonnull SubkeyIdentifier key) { super(info, key); } @Override - public @Nonnull PGPSignature getSignatureWithPreferences() { + @Nonnull + public PGPSignature getSignatureWithPreferences() { String primaryUserId = info.getPrimaryUserId(); // If the key is located by Key ID, the algorithm of the primary User ID of the key provides the // preferred symmetric algorithm. - PGPSignature signature = info.getLatestUserIdCertification(primaryUserId); + PGPSignature signature = null; + if (primaryUserId != null) { + signature = info.getLatestUserIdCertification(primaryUserId); + } + if (signature == null) { signature = info.getLatestDirectKeySelfSignature(); } + if (signature == null) { throw new IllegalStateException("No valid signature found."); } @@ -126,22 +140,27 @@ public abstract class KeyAccessor { public static class SubKey extends KeyAccessor { - public SubKey(KeyRingInfo info, SubkeyIdentifier key) { + public SubKey(@Nonnull KeyRingInfo info, + @Nonnull SubkeyIdentifier key) { super(info, key); } @Override - public @Nonnull PGPSignature getSignatureWithPreferences() { + @Nonnull + public PGPSignature getSignatureWithPreferences() { PGPSignature signature; if (key.getPrimaryKeyId() == key.getSubkeyId()) { signature = info.getLatestDirectKeySelfSignature(); - if (signature == null) { + if (signature == null && info.getPrimaryUserId() != null) { signature = info.getLatestUserIdCertification(info.getPrimaryUserId()); } } else { signature = info.getCurrentSubkeyBindingSignature(key.getSubkeyId()); } + if (signature == null) { + throw new IllegalStateException("No valid signature found."); + } return signature; } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java index 45de52f3..1c20a06c 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/info/KeyRingInfo.java @@ -74,7 +74,9 @@ public class KeyRingInfo { * @param signature signature * @return info of key ring at signature creation time */ - public static KeyRingInfo evaluateForSignature(PGPKeyRing keyRing, PGPSignature signature) { + @Nonnull + public static KeyRingInfo evaluateForSignature(@Nonnull PGPKeyRing keyRing, + @Nonnull PGPSignature signature) { return new KeyRingInfo(keyRing, signature.getCreationTime()); } @@ -83,7 +85,7 @@ public class KeyRingInfo { * * @param keys key ring */ - public KeyRingInfo(PGPKeyRing keys) { + public KeyRingInfo(@Nonnull PGPKeyRing keys) { this(keys, new Date()); } @@ -93,7 +95,8 @@ public class KeyRingInfo { * @param keys key ring * @param referenceDate date of validation */ - public KeyRingInfo(PGPKeyRing keys, Date referenceDate) { + public KeyRingInfo(@Nonnull PGPKeyRing keys, + @Nonnull Date referenceDate) { this(keys, PGPainless.getPolicy(), referenceDate); } @@ -104,14 +107,17 @@ public class KeyRingInfo { * @param policy policy * @param referenceDate validation date */ - public KeyRingInfo(PGPKeyRing keys, Policy policy, Date referenceDate) { - this.referenceDate = referenceDate != null ? referenceDate : new Date(); + public KeyRingInfo(@Nonnull PGPKeyRing keys, + @Nonnull Policy policy, + @Nonnull Date referenceDate) { + this.referenceDate = referenceDate; this.keys = keys; this.signatures = new Signatures(keys, this.referenceDate, policy); this.primaryUserId = findPrimaryUserId(); this.revocationState = findRevocationState(); } + @Nonnull private RevocationState findRevocationState() { PGPSignature revocation = signatures.primaryKeyRevocation; if (revocation != null) { @@ -126,6 +132,7 @@ public class KeyRingInfo { * * @return public key */ + @Nonnull public PGPPublicKey getPublicKey() { return keys.getPublicKey(); } @@ -136,7 +143,8 @@ public class KeyRingInfo { * @param fingerprint fingerprint * @return public key or null */ - public @Nullable PGPPublicKey getPublicKey(OpenPgpFingerprint fingerprint) { + @Nullable + public PGPPublicKey getPublicKey(@Nonnull OpenPgpFingerprint fingerprint) { return getPublicKey(fingerprint.getKeyId()); } @@ -146,7 +154,8 @@ public class KeyRingInfo { * @param keyId key id * @return public key or null */ - public @Nullable PGPPublicKey getPublicKey(long keyId) { + @Nullable + public PGPPublicKey getPublicKey(long keyId) { return getPublicKey(keys, keyId); } @@ -157,7 +166,8 @@ public class KeyRingInfo { * @param keyId key id * @return public key or null */ - public static @Nullable PGPPublicKey getPublicKey(PGPKeyRing keyRing, long keyId) { + @Nullable + public static PGPPublicKey getPublicKey(@Nonnull PGPKeyRing keyRing, long keyId) { return keyRing.getPublicKey(keyId); } @@ -210,6 +220,7 @@ public class KeyRingInfo { * * @return list of public keys */ + @Nonnull public List getPublicKeys() { Iterator iterator = keys.getPublicKeys(); List list = iteratorToList(iterator); @@ -221,7 +232,8 @@ public class KeyRingInfo { * * @return primary secret key or null if the key ring is public */ - public @Nullable PGPSecretKey getSecretKey() { + @Nullable + public PGPSecretKey getSecretKey() { if (keys instanceof PGPSecretKeyRing) { PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) keys; return secretKeys.getSecretKey(); @@ -235,7 +247,8 @@ public class KeyRingInfo { * @param fingerprint fingerprint * @return secret key or null */ - public @Nullable PGPSecretKey getSecretKey(OpenPgpFingerprint fingerprint) { + @Nullable + public PGPSecretKey getSecretKey(@Nonnull OpenPgpFingerprint fingerprint) { return getSecretKey(fingerprint.getKeyId()); } @@ -245,7 +258,8 @@ public class KeyRingInfo { * @param keyId key id * @return secret key or null */ - public @Nullable PGPSecretKey getSecretKey(long keyId) { + @Nullable + public PGPSecretKey getSecretKey(long keyId) { if (keys instanceof PGPSecretKeyRing) { return ((PGPSecretKeyRing) keys).getSecretKey(keyId); } @@ -259,6 +273,7 @@ public class KeyRingInfo { * * @return list of secret keys */ + @Nonnull public List getSecretKeys() { if (keys instanceof PGPSecretKeyRing) { PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) keys; @@ -282,11 +297,13 @@ public class KeyRingInfo { * * @return fingerprint */ + @Nonnull public OpenPgpFingerprint getFingerprint() { return OpenPgpFingerprint.of(getPublicKey()); } - public @Nullable String getPrimaryUserId() { + @Nullable + public String getPrimaryUserId() { return primaryUserId; } @@ -298,6 +315,7 @@ public class KeyRingInfo { * * @return primary user-id or null */ + @Nullable private String findPrimaryUserId() { String primaryUserId = null; Date currentModificationDate = null; @@ -342,10 +360,10 @@ public class KeyRingInfo { * * @return list of user-ids */ + @Nonnull public List getUserIds() { Iterator iterator = getPublicKey().getUserIDs(); - List userIds = iteratorToList(iterator); - return userIds; + return iteratorToList(iterator); } /** @@ -353,6 +371,7 @@ public class KeyRingInfo { * * @return valid user-ids */ + @Nonnull public List getValidUserIds() { List valid = new ArrayList<>(); List userIds = getUserIds(); @@ -369,6 +388,7 @@ public class KeyRingInfo { * * @return bound user-ids */ + @Nonnull public List getValidAndExpiredUserIds() { List probablyExpired = new ArrayList<>(); List userIds = getUserIds(); @@ -407,21 +427,25 @@ public class KeyRingInfo { * @param userId user-id * @return true if user-id is valid */ - public boolean isUserIdValid(String userId) { + public boolean isUserIdValid(@Nonnull CharSequence userId) { + if (primaryUserId == null) { + // No primary userID? No userID at all! + return false; + } + if (!userId.equals(primaryUserId)) { if (!isUserIdBound(primaryUserId)) { - // primary user-id not valid + // primary user-id not valid? UserID not valid! return false; } } return isUserIdBound(userId); } - - private boolean isUserIdBound(String userId) { - - PGPSignature certification = signatures.userIdCertifications.get(userId); - PGPSignature revocation = signatures.userIdRevocations.get(userId); + private boolean isUserIdBound(@Nonnull CharSequence userId) { + String userIdString = userId.toString(); + PGPSignature certification = signatures.userIdCertifications.get(userIdString); + PGPSignature revocation = signatures.userIdRevocations.get(userIdString); if (certification == null) { return false; @@ -453,6 +477,7 @@ public class KeyRingInfo { * * @return email addresses */ + @Nonnull public List getEmailAddresses() { List userIds = getUserIds(); List emails = new ArrayList<>(); @@ -477,7 +502,8 @@ public class KeyRingInfo { * * @return latest direct key self-signature or null */ - public @Nullable PGPSignature getLatestDirectKeySelfSignature() { + @Nullable + public PGPSignature getLatestDirectKeySelfSignature() { return signatures.primaryKeySelfSignature; } @@ -486,7 +512,8 @@ public class KeyRingInfo { * * @return revocation or null */ - public @Nullable PGPSignature getRevocationSelfSignature() { + @Nullable + public PGPSignature getRevocationSelfSignature() { return signatures.primaryKeyRevocation; } @@ -496,8 +523,9 @@ public class KeyRingInfo { * @param userId user-id * @return certification signature or null */ - public @Nullable PGPSignature getLatestUserIdCertification(String userId) { - return signatures.userIdCertifications.get(userId); + @Nullable + public PGPSignature getLatestUserIdCertification(@Nonnull CharSequence userId) { + return signatures.userIdCertifications.get(userId.toString()); } /** @@ -506,8 +534,9 @@ public class KeyRingInfo { * @param userId user-id * @return revocation or null */ - public @Nullable PGPSignature getUserIdRevocation(String userId) { - return signatures.userIdRevocations.get(userId); + @Nullable + public PGPSignature getUserIdRevocation(@Nonnull CharSequence userId) { + return signatures.userIdRevocations.get(userId.toString()); } /** @@ -516,7 +545,8 @@ public class KeyRingInfo { * @param keyId subkey id * @return subkey binding signature or null */ - public @Nullable PGPSignature getCurrentSubkeyBindingSignature(long keyId) { + @Nullable + public PGPSignature getCurrentSubkeyBindingSignature(long keyId) { return signatures.subkeyBindings.get(keyId); } @@ -526,7 +556,8 @@ public class KeyRingInfo { * @param keyId subkey id * @return subkey binding revocation or null */ - public @Nullable PGPSignature getSubkeyRevocationSignature(long keyId) { + @Nullable + public PGPSignature getSubkeyRevocationSignature(long keyId) { return signatures.subkeyRevocations.get(keyId); } @@ -535,7 +566,8 @@ public class KeyRingInfo { * @param keyId key-id * @return list of key flags */ - public @Nonnull List getKeyFlagsOf(long keyId) { + @Nonnull + public List getKeyFlagsOf(long keyId) { // key is primary key if (getPublicKey().getKeyID() == keyId) { @@ -575,7 +607,8 @@ public class KeyRingInfo { * @param userId user-id * @return key flags */ - public @Nonnull List getKeyFlagsOf(String userId) { + @Nonnull + public List getKeyFlagsOf(String userId) { if (!isUserIdValid(userId)) { return Collections.emptyList(); } @@ -607,6 +640,7 @@ public class KeyRingInfo { * * @return creation date */ + @Nonnull public Date getCreationDate() { return getPublicKey().getCreationTime(); } @@ -617,7 +651,8 @@ public class KeyRingInfo { * * @return last modification date. */ - public @Nonnull Date getLastModified() { + @Nonnull + public Date getLastModified() { PGPSignature mostRecent = getMostRecentSignature(); if (mostRecent == null) { // No sigs found. Return public key creation date instead. @@ -631,7 +666,8 @@ public class KeyRingInfo { * * @return latest key creation time */ - public @Nonnull Date getLatestKeyCreationDate() { + @Nonnull + public Date getLatestKeyCreationDate() { Date latestCreation = null; for (PGPPublicKey key : getPublicKeys()) { if (!isKeyValidlyBound(key.getKeyID())) { @@ -648,7 +684,8 @@ public class KeyRingInfo { return latestCreation; } - private @Nullable PGPSignature getMostRecentSignature() { + @Nullable + private PGPSignature getMostRecentSignature() { Set allSignatures = new HashSet<>(); PGPSignature mostRecentSelfSignature = getLatestDirectKeySelfSignature(); PGPSignature revocationSelfSignature = getRevocationSelfSignature(); @@ -668,6 +705,7 @@ public class KeyRingInfo { return mostRecent; } + @Nonnull public RevocationState getRevocationState() { return revocationState; } @@ -677,7 +715,8 @@ public class KeyRingInfo { * * @return revocation date or null */ - public @Nullable Date getRevocationDate() { + @Nullable + public Date getRevocationDate() { return getRevocationState().isSoftRevocation() ? getRevocationState().getDate() : null; } @@ -686,7 +725,8 @@ public class KeyRingInfo { * * @return expiration date */ - public @Nullable Date getPrimaryKeyExpirationDate() { + @Nullable + public Date getPrimaryKeyExpirationDate() { PGPSignature directKeySig = getLatestDirectKeySelfSignature(); Date directKeyExpirationDate = null; if (directKeySig != null) { @@ -722,6 +762,7 @@ public class KeyRingInfo { return userIdExpirationDate; } + @Nullable public String getPossiblyExpiredPrimaryUserId() { String validPrimaryUserId = getPrimaryUserId(); if (validPrimaryUserId != null) { @@ -760,7 +801,8 @@ public class KeyRingInfo { * @param fingerprint subkey fingerprint * @return expiration date or null */ - public @Nullable Date getSubkeyExpirationDate(OpenPgpFingerprint fingerprint) { + @Nullable + public Date getSubkeyExpirationDate(OpenPgpFingerprint fingerprint) { if (getPublicKey().getKeyID() == fingerprint.getKeyId()) { return getPrimaryKeyExpirationDate(); } @@ -788,6 +830,7 @@ public class KeyRingInfo { * {@link KeyFlag#ENCRYPT_COMMS}/{@link KeyFlag#ENCRYPT_STORAGE}. * @return latest date on which the key ring can be used for the given use case, or null if it can be used indefinitely. */ + @Nullable public Date getExpirationDateForUse(KeyFlag use) { if (use == KeyFlag.SPLIT || use == KeyFlag.SHARED) { throw new IllegalArgumentException("SPLIT and SHARED are not uses, but properties."); @@ -814,20 +857,18 @@ public class KeyRingInfo { } if (nonExpiringSubkeys.isEmpty()) { - if (latestSubkeyExpirationDate != null) { - if (primaryExpiration == null) { - return latestSubkeyExpirationDate; - } - if (latestSubkeyExpirationDate.before(primaryExpiration)) { - return latestSubkeyExpirationDate; - } + if (primaryExpiration == null) { + return latestSubkeyExpirationDate; + } + if (latestSubkeyExpirationDate.before(primaryExpiration)) { + return latestSubkeyExpirationDate; } } return primaryExpiration; } - public boolean isHardRevoked(String userId) { - PGPSignature revocation = signatures.userIdRevocations.get(userId); + public boolean isHardRevoked(@Nonnull CharSequence userId) { + PGPSignature revocation = signatures.userIdRevocations.get(userId.toString()); if (revocation == null) { return false; } @@ -907,7 +948,8 @@ public class KeyRingInfo { * @param purpose purpose (encrypt data at rest / communications) * @return encryption subkeys */ - public @Nonnull List getEncryptionSubkeys(EncryptionPurpose purpose) { + @Nonnull + public List getEncryptionSubkeys(@Nonnull EncryptionPurpose purpose) { Date primaryExpiration = getPrimaryKeyExpirationDate(); if (primaryExpiration != null && primaryExpiration.before(referenceDate)) { LOGGER.debug("Certificate is expired: Primary key is expired on " + DateUtil.formatUTCDate(primaryExpiration)); @@ -966,7 +1008,8 @@ public class KeyRingInfo { * * @return decryption keys */ - public @Nonnull List getDecryptionSubkeys() { + @Nonnull + public List getDecryptionSubkeys() { Iterator subkeys = keys.getPublicKeys(); List decryptionKeys = new ArrayList<>(); @@ -999,7 +1042,8 @@ public class KeyRingInfo { * @param flag flag * @return keys with flag */ - public List getKeysWithKeyFlag(KeyFlag flag) { + @Nonnull + public List getKeysWithKeyFlag(@Nonnull KeyFlag flag) { List keysWithFlag = new ArrayList<>(); for (PGPPublicKey key : getPublicKeys()) { List keyFlags = getKeyFlagsOf(key.getKeyID()); @@ -1021,11 +1065,13 @@ public class KeyRingInfo { * @param purpose encryption purpose * @return encryption subkeys */ - public @Nonnull List getEncryptionSubkeys(String userId, EncryptionPurpose purpose) { + @Nonnull + public List getEncryptionSubkeys(@Nullable CharSequence userId, + @Nonnull EncryptionPurpose purpose) { if (userId != null && !isUserIdValid(userId)) { throw new KeyException.UnboundUserIdException( OpenPgpFingerprint.of(keys), - userId, + userId.toString(), getLatestUserIdCertification(userId), getUserIdRevocation(userId) ); @@ -1039,7 +1085,8 @@ public class KeyRingInfo { * * @return signing keys */ - public @Nonnull List getSigningSubkeys() { + @Nonnull + public List getSigningSubkeys() { Iterator subkeys = keys.getPublicKeys(); List signingKeys = new ArrayList<>(); while (subkeys.hasNext()) { @@ -1057,39 +1104,48 @@ public class KeyRingInfo { return signingKeys; } + @Nonnull public Set getPreferredHashAlgorithms() { return getPreferredHashAlgorithms(getPrimaryUserId()); } - public Set getPreferredHashAlgorithms(String userId) { + @Nonnull + public Set getPreferredHashAlgorithms(@Nullable CharSequence userId) { return getKeyAccessor(userId, getKeyId()).getPreferredHashAlgorithms(); } + @Nonnull public Set getPreferredHashAlgorithms(long keyId) { return new KeyAccessor.SubKey(this, new SubkeyIdentifier(keys, keyId)) .getPreferredHashAlgorithms(); } + @Nonnull public Set getPreferredSymmetricKeyAlgorithms() { return getPreferredSymmetricKeyAlgorithms(getPrimaryUserId()); } - public Set getPreferredSymmetricKeyAlgorithms(String userId) { + @Nonnull + public Set getPreferredSymmetricKeyAlgorithms(@Nullable CharSequence userId) { return getKeyAccessor(userId, getKeyId()).getPreferredSymmetricKeyAlgorithms(); } + @Nonnull public Set getPreferredSymmetricKeyAlgorithms(long keyId) { return new KeyAccessor.SubKey(this, new SubkeyIdentifier(keys, keyId)).getPreferredSymmetricKeyAlgorithms(); } + @Nonnull public Set getPreferredCompressionAlgorithms() { return getPreferredCompressionAlgorithms(getPrimaryUserId()); } - public Set getPreferredCompressionAlgorithms(String userId) { + @Nonnull + public Set getPreferredCompressionAlgorithms(@Nullable CharSequence userId) { return getKeyAccessor(userId, getKeyId()).getPreferredCompressionAlgorithms(); } + @Nonnull public Set getPreferredCompressionAlgorithms(long keyId) { return new KeyAccessor.SubKey(this, new SubkeyIdentifier(keys, keyId)).getPreferredCompressionAlgorithms(); } @@ -1173,15 +1229,20 @@ public class KeyRingInfo { return true; } - private KeyAccessor getKeyAccessor(@Nullable String userId, long keyID) { + private KeyAccessor getKeyAccessor(@Nullable CharSequence userId, long keyID) { if (getPublicKey(keyID) == null) { throw new NoSuchElementException("No subkey with key id " + Long.toHexString(keyID) + " found on this key."); } - if (userId != null && !getUserIds().contains(userId)) { + + if (userId != null && !getUserIds().contains(userId.toString())) { throw new NoSuchElementException("No user-id '" + userId + "' found on this key."); } - return userId == null ? new KeyAccessor.ViaKeyId(this, new SubkeyIdentifier(keys, keyID)) - : new KeyAccessor.ViaUserId(this, new SubkeyIdentifier(keys, keyID), userId); + + if (userId != null) { + return new KeyAccessor.ViaUserId(this, new SubkeyIdentifier(keys, keyID), userId.toString()); + } else { + return new KeyAccessor.ViaKeyId(this, new SubkeyIdentifier(keys, keyID)); + } } public static class Signatures { @@ -1193,7 +1254,9 @@ public class KeyRingInfo { private final Map subkeyRevocations; private final Map subkeyBindings; - public Signatures(PGPKeyRing keyRing, Date referenceDate, Policy policy) { + public Signatures(@Nonnull PGPKeyRing keyRing, + @Nonnull Date referenceDate, + @Nonnull Policy policy) { primaryKeyRevocation = SignaturePicker.pickCurrentRevocationSelfSignature(keyRing, policy, referenceDate); primaryKeySelfSignature = SignaturePicker.pickLatestDirectKeySignature(keyRing, policy, referenceDate); userIdRevocations = new HashMap<>(); diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 01c09903..6210ef5b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -72,14 +72,12 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { private PGPSecretKeyRing secretKeyRing; private final Date referenceTime; - public SecretKeyRingEditor(PGPSecretKeyRing secretKeyRing) { - this(secretKeyRing, null); + public SecretKeyRingEditor(@Nonnull PGPSecretKeyRing secretKeyRing) { + this(secretKeyRing, new Date()); } - public SecretKeyRingEditor(PGPSecretKeyRing secretKeyRing, Date referenceTime) { - if (secretKeyRing == null) { - throw new NullPointerException("SecretKeyRing MUST NOT be null."); - } + public SecretKeyRingEditor(@Nonnull PGPSecretKeyRing secretKeyRing, + @Nonnull Date referenceTime) { this.secretKeyRing = secretKeyRing; this.referenceTime = referenceTime; } diff --git a/pgpainless-core/src/test/java/org/pgpainless/algorithm/RevocationStateTest.java b/pgpainless-core/src/test/java/org/pgpainless/algorithm/RevocationStateTest.java index e23d92d3..c24084b4 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/algorithm/RevocationStateTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/algorithm/RevocationStateTest.java @@ -60,11 +60,6 @@ public class RevocationStateTest { assertEquals("softRevoked (2022-08-03 18:26:35 UTC)", state.toString()); } - @Test - public void testSoftRevokedNullDateThrows() { - assertThrows(NullPointerException.class, () -> RevocationState.softRevoked(null)); - } - @Test public void orderTest() { assertEquals(RevocationState.notRevoked(), RevocationState.notRevoked()); 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 fd665901..0caf8b75 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 @@ -95,6 +95,7 @@ public class UserIdRevocationTest { KeyRingInfo info = new KeyRingInfo(secretKeys); PGPSignature signature = info.getUserIdRevocation("secondary@key.id"); + assertNotNull(signature); RevocationReason reason = (RevocationReason) signature.getHashedSubPackets() .getSubpacket(SignatureSubpacketTags.REVOCATION_REASON); assertNotNull(reason); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureSubpacketsUtilTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureSubpacketsUtilTest.java index d5cfb4f5..5b93415e 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureSubpacketsUtilTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/SignatureSubpacketsUtilTest.java @@ -145,6 +145,7 @@ public class SignatureSubpacketsUtilTest { PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); Set featureSet = SignatureSubpacketsUtil.parseFeatures(signature); + assertNotNull(featureSet); assertEquals(2, featureSet.size()); assertTrue(featureSet.contains(Feature.MODIFICATION_DETECTION)); assertTrue(featureSet.contains(Feature.AEAD_ENCRYPTED_DATA)); @@ -216,6 +217,7 @@ public class SignatureSubpacketsUtilTest { PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); RevocationKey revocationKey = SignatureSubpacketsUtil.getRevocationKey(signature); + assertNotNull(revocationKey); assertArrayEquals(secretKeys.getPublicKey().getFingerprint(), revocationKey.getFingerprint()); assertEquals(secretKeys.getPublicKey().getAlgorithm(), revocationKey.getAlgorithm()); } @@ -277,6 +279,7 @@ public class SignatureSubpacketsUtilTest { PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); TrustSignature trustSignature = SignatureSubpacketsUtil.getTrustSignature(signature); + assertNotNull(trustSignature); assertEquals(10, trustSignature.getDepth()); assertEquals(3, trustSignature.getTrustAmount()); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilderTest.java index ced6a466..bf1cb694 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyCertificationSignatureBuilderTest.java @@ -4,6 +4,7 @@ package org.pgpainless.signature.builder; +import org.bouncycastle.bcpg.sig.Exportable; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -22,6 +23,7 @@ import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -61,7 +63,9 @@ public class ThirdPartyCertificationSignatureBuilderTest { assertEquals(SignatureType.GENERIC_CERTIFICATION, SignatureType.valueOf(certification.getSignatureType())); assertEquals(secretKeys.getPublicKey().getKeyID(), certification.getKeyID()); assertArrayEquals(secretKeys.getPublicKey().getFingerprint(), certification.getHashedSubPackets().getIssuerFingerprint().getFingerprint()); - assertFalse(SignatureSubpacketsUtil.getExportableCertification(certification).isExportable()); + Exportable exportable = SignatureSubpacketsUtil.getExportableCertification(certification); + assertNotNull(exportable); + assertFalse(exportable.isExportable()); // test sig correctness certification.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), secretKeys.getPublicKey()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java index 988bc776..3c2eda91 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/subpackets/SignatureSubpacketsTest.java @@ -376,7 +376,7 @@ public class SignatureSubpacketsTest { public void testSetSignatureTarget() { byte[] hash = new byte[20]; new Random().nextBytes(hash); - wrapper.setSignatureTarget(PublicKeyAlgorithm.fromId(key.getAlgorithm()), HashAlgorithm.SHA512, hash); + wrapper.setSignatureTarget(PublicKeyAlgorithm.requireFromId(key.getAlgorithm()), HashAlgorithm.SHA512, hash); PGPSignatureSubpacketVector vector = SignatureSubpacketsHelper.toVector(wrapper); SignatureTarget target = vector.getSignatureTarget(); From d05ffd0451f47633c7def76d6e6fc834c4cc492d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 16:11:06 +0200 Subject: [PATCH 011/483] Make DateUtil null-safe --- .../src/main/java/org/pgpainless/util/DateUtil.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/DateUtil.java b/pgpainless-core/src/main/java/org/pgpainless/util/DateUtil.java index ad1fce09..f56020d4 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/util/DateUtil.java +++ b/pgpainless-core/src/main/java/org/pgpainless/util/DateUtil.java @@ -4,6 +4,7 @@ package org.pgpainless.util; +import javax.annotation.Nonnull; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -16,6 +17,7 @@ public final class DateUtil { } // Java's SimpleDateFormat is not thread-safe, therefore we return a new instance on every invocation. + @Nonnull public static SimpleDateFormat getParser() { SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); parser.setTimeZone(TimeZone.getTimeZone("UTC")); @@ -28,11 +30,12 @@ public final class DateUtil { * @param dateString timestamp * @return date */ - public static Date parseUTCDate(String dateString) { + @Nonnull + public static Date parseUTCDate(@Nonnull String dateString) { try { return getParser().parse(dateString); } catch (ParseException e) { - return null; + throw new IllegalArgumentException("Malformed UTC timestamp: " + dateString, e); } } @@ -42,6 +45,7 @@ public final class DateUtil { * @param date date * @return timestamp */ + @Nonnull public static String formatUTCDate(Date date) { return getParser().format(date); } @@ -51,7 +55,8 @@ public final class DateUtil { * @param date date * @return floored date */ - public static Date toSecondsPrecision(Date date) { + @Nonnull + public static Date toSecondsPrecision(@Nonnull Date date) { long millis = date.getTime(); long seconds = millis / 1000; long floored = seconds * 1000; @@ -63,6 +68,7 @@ public final class DateUtil { * * @return now */ + @Nonnull public static Date now() { return toSecondsPrecision(new Date()); } From 21ae48d8c11cc8686fb9c7b3d50ec02116f4a58d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:13:29 +0200 Subject: [PATCH 012/483] Use assert statements to flag impossible NPEs --- .../decryption_verification/OpenPgpMessageInputStream.java | 2 ++ .../pgpainless/key/certification/CertifyCertificate.java | 1 + .../key/modification/secretkeyring/SecretKeyRingEditor.java | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java index 04d823d1..07098269 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java @@ -544,6 +544,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { for (Tuple k : postponedDueToMissingPassphrase) { PGPSecretKey key = k.getA(); PGPSecretKeyRing keys = getDecryptionKey(key.getKeyID()); + assert (keys != null); keyIds.add(new SubkeyIdentifier(keys, key.getKeyID())); } if (!keyIds.isEmpty()) { @@ -556,6 +557,7 @@ public class OpenPgpMessageInputStream extends DecryptionStream { PGPSecretKey secretKey = missingPassphrases.getA(); long keyId = secretKey.getKeyID(); PGPSecretKeyRing decryptionKey = getDecryptionKey(keyId); + assert (decryptionKey != null); SubkeyIdentifier decryptionKeyId = new SubkeyIdentifier(decryptionKey, keyId); if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) { continue; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java index 05e64879..9340d93d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/certification/CertifyCertificate.java @@ -275,6 +275,7 @@ public class CertifyCertificate { // We only support certification-capable primary keys OpenPgpFingerprint fingerprint = info.getFingerprint(); PGPPublicKey certificationPubKey = info.getPublicKey(fingerprint); + assert (certificationPubKey != null); if (!info.isKeyValidlyBound(certificationPubKey.getKeyID())) { throw new KeyException.RevokedKeyException(fingerprint); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 6210ef5b..73c39c25 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -182,7 +182,10 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } // We need to unmark this user-id as primary - if (info.getLatestUserIdCertification(otherUserId).getHashedSubPackets().isPrimaryUserID()) { + PGPSignature userIdCertification = info.getLatestUserIdCertification(otherUserId); + assert (userIdCertification != null); + + if (userIdCertification.getHashedSubPackets().isPrimaryUserID()) { addUserId(otherUserId, new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -601,6 +604,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } if (prevUserIdSig.getHashedSubPackets().isPrimaryUserID()) { + assert (primaryUserId != null); PGPSignature userIdSig = reissueNonPrimaryUserId(secretKeyRingProtector, userId, prevUserIdSig); secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, primaryUserId, userIdSig); } From 09bacd40d1701d52dbb23d3360c7dca1532e2000 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:14:18 +0200 Subject: [PATCH 013/483] SecretKeyRingEditor: referenceTime cannot be null anymore --- .../secretkeyring/SecretKeyRingEditor.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 73c39c25..7c577600 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -124,9 +124,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } SelfSignatureBuilder builder = new SelfSignatureBuilder(primaryKey, protector); - if (referenceTime != null) { - builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); - } + builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); builder.setSignatureType(SignatureType.POSITIVE_CERTIFICATION); // Retain signature subpackets of previous signatures @@ -351,16 +349,12 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { .getV4FingerprintCalculator(), false, subkeyProtector.getEncryptor(subkey.getKeyID())); SubkeyBindingSignatureBuilder skBindingBuilder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector, hashAlgorithm); - if (referenceTime != null) { - skBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); - } + skBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); skBindingBuilder.getHashedSubpackets().setKeyFlags(flags); if (subkeyAlgorithm.isSigningCapable()) { PrimaryKeyBindingSignatureBuilder pkBindingBuilder = new PrimaryKeyBindingSignatureBuilder(secretSubkey, subkeyProtector, hashAlgorithm); - if (referenceTime != null) { - pkBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); - } + pkBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); PGPSignature pkBinding = pkBindingBuilder.build(primaryKey.getPublicKey()); skBindingBuilder.getHashedSubpackets().addEmbeddedSignature(pkBinding); } From 7a194c517ac9ce03d3e9eaffc770c12a1655580a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:15:30 +0200 Subject: [PATCH 014/483] Remove KeyRingUtils.removeSecretKey() in favor of stripSecretKey() --- .../org/pgpainless/key/util/KeyRingUtils.java | 24 ------------------- .../CertificateWithMissingSecretKeyTest.java | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java index 2ce058fd..23361c13 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java @@ -440,30 +440,6 @@ public final class KeyRingUtils { return newSecretKey; } - /** - * Remove the secret key of the subkey identified by the given secret key id from the key ring. - * The public part stays attached to the key ring, so that it can still be used for encryption / verification of signatures. - * - * This method is intended to be used to remove secret primary keys from live keys when those are kept in offline storage. - * - * @param secretKeys secret key ring - * @param secretKeyId id of the secret key to remove - * @return secret key ring with removed secret key - * - * @throws IOException in case of an error during serialization / deserialization of the key - * @throws PGPException in case of a broken key - * - * @deprecated use {@link #stripSecretKey(PGPSecretKeyRing, long)} instead. - * TODO: Remove in 1.2.X - */ - @Nonnull - @Deprecated - public static PGPSecretKeyRing removeSecretKey(@Nonnull PGPSecretKeyRing secretKeys, - long secretKeyId) - throws IOException, PGPException { - return stripSecretKey(secretKeys, secretKeyId); - } - /** * Remove the secret key of the subkey identified by the given secret key id from the key ring. * The public part stays attached to the key ring, so that it can still be used for encryption / verification of signatures. diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CertificateWithMissingSecretKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CertificateWithMissingSecretKeyTest.java index a53999a6..e5f9e370 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CertificateWithMissingSecretKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CertificateWithMissingSecretKeyTest.java @@ -83,7 +83,7 @@ public class CertificateWithMissingSecretKeyTest { encryptionSubkeyId = PGPainless.inspectKeyRing(secretKeys) .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getKeyID(); // remove the encryption/decryption secret key - missingDecryptionSecKey = KeyRingUtils.removeSecretKey(secretKeys, encryptionSubkeyId); + missingDecryptionSecKey = KeyRingUtils.stripSecretKey(secretKeys, encryptionSubkeyId); } @Test From 5c76f9046f28f013bf3e3ce70c7ac8f092abec6f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:16:10 +0200 Subject: [PATCH 015/483] Turn empty catch block into test failure --- .../IgnoreUnknownSignatureVersionsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/IgnoreUnknownSignatureVersionsTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/IgnoreUnknownSignatureVersionsTest.java index 2b60ea02..aa1da741 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/IgnoreUnknownSignatureVersionsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/IgnoreUnknownSignatureVersionsTest.java @@ -5,6 +5,7 @@ package org.pgpainless.decryption_verification; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -71,7 +72,7 @@ public class IgnoreUnknownSignatureVersionsTest { try { cert = PGPainless.readKeyRing().publicKeyRing(CERT); } catch (IOException e) { - + fail("Cannot parse certificate.", e); } } From 78cb2ec3d0f3b14ff16ff87d4dc5343c803b6c8e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:16:56 +0200 Subject: [PATCH 016/483] Do not catch and immediatelly rethrow exception --- .../decryption_verification/TeeBCPGInputStream.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java index bdbd9bcd..e1d33f70 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java @@ -7,7 +7,6 @@ package org.pgpainless.decryption_verification; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.NoSuchElementException; import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.MarkerPacket; @@ -49,13 +48,7 @@ public class TeeBCPGInputStream { return null; } - OpenPgpPacket packet; - try { - packet = OpenPgpPacket.requireFromTag(tag); - } catch (NoSuchElementException e) { - throw e; - } - return packet; + return OpenPgpPacket.requireFromTag(tag); } public Packet readPacket() throws IOException { From 3cea98536506dfa2a44d5898c27e096cd53cdfb9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:19:18 +0200 Subject: [PATCH 017/483] TeeBCPGInputStream: Annotate byte[] arg as @Nonnull --- .../decryption_verification/TeeBCPGInputStream.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java index e1d33f70..725c6f6e 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java @@ -19,6 +19,8 @@ import org.bouncycastle.openpgp.PGPOnePassSignature; import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.algorithm.OpenPgpPacket; +import javax.annotation.Nonnull; + /** * Since we need to update signatures with data from the underlying stream, this class is used to tee out the data. * Unfortunately we cannot simply override {@link BCPGInputStream#read()} to tee the data out though, since @@ -121,7 +123,7 @@ public class TeeBCPGInputStream { } @Override - public int read(byte[] b, int off, int len) throws IOException { + public int read(@Nonnull byte[] b, int off, int len) throws IOException { if (last != -1) { outputStream.write(last); } From fb581f11c792c17f516ec50683b50bcdad24ca53 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 May 2023 17:20:02 +0200 Subject: [PATCH 018/483] UserId.parse(): Prevent self-referencing javadoc --- .../src/main/java/org/pgpainless/key/util/UserId.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/UserId.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/UserId.java index 427f9060..b115afda 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/UserId.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/UserId.java @@ -88,7 +88,7 @@ public final class UserId implements CharSequence { *

  • John Doe <john@pgpainless.org>
  • *
  • John Doe (work email) <john@pgpainless.org>
  • * - * In these cases, {@link #parse(String)} will detect email addresses, names and comments and expose those + * In these cases, this method will detect email addresses, names and comments and expose those * via the respective getters. * This method does not support parsing mail addresses of the following formats: *