From c3dc3c9d8734894cc7bb0c6837a933fcc3a6216f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 3 Sep 2022 12:19:34 +0200 Subject: [PATCH] Allow modification of keys with custom reference date Also, bind subkeys using SubkeyBindingSignatureBuilder --- .../main/java/org/pgpainless/PGPainless.java | 6 +- .../org/pgpainless/key/info/KeyRingInfo.java | 14 +- .../secretkeyring/SecretKeyRingEditor.java | 124 ++++++++-------- .../PrimaryKeyBindingSignatureBuilder.java | 11 ++ .../SubkeyBindingSignatureBuilder.java | 9 ++ .../KeyGenerationSubpacketsTest.java | 12 +- .../modification/ChangeExpirationTest.java | 33 ++--- ...gePrimaryUserIdAndExpirationDatesTest.java | 140 ++++++++++-------- ...reSubpacketsArePreservedOnNewSigTest.java} | 18 +-- ...irdPartyDirectKeySignatureBuilderTest.java | 10 +- 10 files changed, 207 insertions(+), 170 deletions(-) rename pgpainless-core/src/test/java/org/pgpainless/key/modification/{OldSignatureSubpacketsArePreservedOnNewSig.java => OldSignatureSubpacketsArePreservedOnNewSigTest.java} (80%) diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index 74a1f239..fd670fc9 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -127,7 +127,11 @@ public final class PGPainless { * @return builder */ public static SecretKeyRingEditorInterface modifyKeyRing(PGPSecretKeyRing secretKeys) { - return new SecretKeyRingEditor(secretKeys); + return modifyKeyRing(secretKeys, null); + } + + public static SecretKeyRingEditorInterface modifyKeyRing(PGPSecretKeyRing secretKeys, Date referenceTime) { + return new SecretKeyRingEditor(secretKeys, referenceTime); } /** 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 995bded4..b818290b 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 @@ -85,10 +85,10 @@ public class KeyRingInfo { * Evaluate the key ring at the provided validation date. * * @param keys key ring - * @param validationDate date of validation + * @param referenceDate date of validation */ - public KeyRingInfo(PGPKeyRing keys, Date validationDate) { - this(keys, PGPainless.getPolicy(), validationDate); + public KeyRingInfo(PGPKeyRing keys, Date referenceDate) { + this(keys, PGPainless.getPolicy(), referenceDate); } /** @@ -96,12 +96,12 @@ public class KeyRingInfo { * * @param keys key ring * @param policy policy - * @param validationDate validation date + * @param referenceDate validation date */ - public KeyRingInfo(PGPKeyRing keys, Policy policy, Date validationDate) { + public KeyRingInfo(PGPKeyRing keys, Policy policy, Date referenceDate) { + this.referenceDate = referenceDate != null ? referenceDate : new Date(); this.keys = keys; - this.signatures = new Signatures(keys, validationDate, policy); - this.referenceDate = validationDate; + this.signatures = new Signatures(keys, this.referenceDate, policy); this.primaryUserId = findPrimaryUserId(); this.revocationState = findRevocationState(); } 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 e1b1b23e..20876c36 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 @@ -4,35 +4,17 @@ package org.pgpainless.key.modification.secretkeyring; -import static org.pgpainless.util.CollectionUtils.concat; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import org.bouncycastle.bcpg.S2K; import org.bouncycastle.bcpg.SecretKeyPacket; import org.bouncycastle.bcpg.sig.KeyExpirationTime; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; -import org.bouncycastle.openpgp.PGPKeyRingGenerator; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.AlgorithmSuite; import org.pgpainless.algorithm.CompressionAlgorithm; @@ -57,26 +39,49 @@ import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvi import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.signature.builder.DirectKeySelfSignatureBuilder; +import org.pgpainless.signature.builder.PrimaryKeyBindingSignatureBuilder; import org.pgpainless.signature.builder.RevocationSignatureBuilder; import org.pgpainless.signature.builder.SelfSignatureBuilder; +import org.pgpainless.signature.builder.SubkeyBindingSignatureBuilder; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -import org.pgpainless.util.CollectionUtils; import org.pgpainless.util.Passphrase; import org.pgpainless.util.selection.userid.SelectUserId; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import static org.pgpainless.util.CollectionUtils.concat; + public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { private PGPSecretKeyRing secretKeyRing; + private final Date referenceTime; public SecretKeyRingEditor(PGPSecretKeyRing secretKeyRing) { + this(secretKeyRing, null); + } + + public SecretKeyRingEditor(PGPSecretKeyRing secretKeyRing, Date referenceTime) { if (secretKeyRing == null) { throw new NullPointerException("SecretKeyRing MUST NOT be null."); } this.secretKeyRing = secretKeyRing; + this.referenceTime = referenceTime; } @Override @@ -99,7 +104,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { PGPSecretKey primaryKey = secretKeyRing.getSecretKey(); // retain key flags from previous signature - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); if (info.isHardRevoked(userId.toString())) { throw new IllegalArgumentException("User-ID " + userId + " is hard revoked and cannot be re-certified."); } @@ -121,6 +126,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } SelfSignatureBuilder builder = new SelfSignatureBuilder(primaryKey, protector); + if (referenceTime != null) { + builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } builder.setSignatureType(SignatureType.POSITIVE_CERTIFICATION); // Retain signature subpackets of previous signatures @@ -145,7 +153,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { // Determine previous key expiration date PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey(); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); String primaryUserId = info.getPrimaryUserId(); PGPSignature signature = primaryUserId == null ? info.getLatestDirectKeySelfSignature() : info.getLatestUserIdCertification(primaryUserId); @@ -169,7 +177,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { protector); // unmark previous primary user-ids to be non-primary - info = PGPainless.inspectKeyRing(secretKeyRing); + info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); for (String otherUserId : info.getValidAndExpiredUserIds()) { if (userId.toString().equals(otherUserId)) { continue; @@ -227,7 +235,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { throw new IllegalArgumentException("New user-id cannot be empty."); } - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); if (!info.isUserIdValid(oldUID)) { throw new NoSuchElementException("Key does not carry user-id '" + oldUID + "', or it is not valid."); } @@ -333,46 +341,34 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } PGPSecretKey primaryKey = secretKeyRing.getSecretKey(); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); - PublicKeyAlgorithm signingKeyAlgorithm = PublicKeyAlgorithm.requireFromId(primaryKey.getPublicKey().getAlgorithm()); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); HashAlgorithm hashAlgorithm = HashAlgorithmNegotiator .negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) .negotiateHashAlgorithm(info.getPreferredHashAlgorithms()); - // While we'd like to rely on our own BindingSignatureBuilder implementation, - // unfortunately we have to use BCs PGPKeyRingGenerator class since there is no public constructor - // for subkeys. See https://github.com/bcgit/bc-java/pull/1063 - PGPKeyRingGenerator ringGenerator = new PGPKeyRingGenerator( - secretKeyRing, - primaryKeyProtector.getDecryptor(primaryKey.getKeyID()), - ImplementationFactory.getInstance().getV4FingerprintCalculator(), - ImplementationFactory.getInstance().getPGPContentSignerBuilder( - signingKeyAlgorithm, hashAlgorithm), - subkeyProtector.getEncryptor(subkey.getKeyID())); + PGPSecretKey secretSubkey = new PGPSecretKey(subkey.getPrivateKey(), subkey.getPublicKey(), ImplementationFactory.getInstance() + .getV4FingerprintCalculator(), false, subkeyProtector.getEncryptor(subkey.getKeyID())); - SelfSignatureSubpackets hashedSubpackets = SignatureSubpackets.createHashedSubpackets(primaryKey.getPublicKey()); - SelfSignatureSubpackets unhashedSubpackets = SignatureSubpackets.createEmptySubpackets(); - hashedSubpackets.setKeyFlags(flags); + SubkeyBindingSignatureBuilder skBindingBuilder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector, hashAlgorithm); + if (referenceTime != null) { + skBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } + skBindingBuilder.getHashedSubpackets().setKeyFlags(flags); - if (bindingSignatureCallback != null) { - bindingSignatureCallback.modifyHashedSubpackets(hashedSubpackets); - bindingSignatureCallback.modifyUnhashedSubpackets(unhashedSubpackets); + if (subkeyAlgorithm.isSigningCapable()) { + PrimaryKeyBindingSignatureBuilder pkBindingBuilder = new PrimaryKeyBindingSignatureBuilder(secretSubkey, subkeyProtector, hashAlgorithm); + if (referenceTime != null) { + pkBindingBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } + PGPSignature pkBinding = pkBindingBuilder.build(primaryKey.getPublicKey()); + skBindingBuilder.getHashedSubpackets().addEmbeddedSignature(pkBinding); } - boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) || - CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER); - PGPContentSignerBuilder primaryKeyBindingSigner = null; - if (isSigningKey) { - primaryKeyBindingSigner = ImplementationFactory.getInstance().getPGPContentSignerBuilder(subkeyAlgorithm, hashAlgorithm); - } - - ringGenerator.addSubKey(subkey, - SignatureSubpacketsHelper.toVector((SignatureSubpackets) hashedSubpackets), - SignatureSubpacketsHelper.toVector((SignatureSubpackets) unhashedSubpackets), - primaryKeyBindingSigner); - - secretKeyRing = ringGenerator.generateSecretKeyRing(); + skBindingBuilder.applyCallback(bindingSignatureCallback); + PGPSignature skBinding = skBindingBuilder.build(secretSubkey.getPublicKey()); + secretSubkey = KeyRingUtils.secretKeyPlusSignature(secretSubkey, skBinding); + secretKeyRing = KeyRingUtils.keysPlusSecretKey(secretKeyRing, secretSubkey); return this; } @@ -558,6 +554,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { SignatureType.CERTIFICATION_REVOCATION, primarySecretKey, protector); + if (referenceTime != null) { + signatureBuilder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } signatureBuilder.applyCallback(callback); @@ -585,14 +584,14 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } // reissue primary user-id sig - String primaryUserId = PGPainless.inspectKeyRing(secretKeyRing).getPossiblyExpiredPrimaryUserId(); + String primaryUserId = PGPainless.inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId(); if (primaryUserId != null) { PGPSignature prevUserIdSig = getPreviousUserIdSignatures(primaryUserId); PGPSignature userIdSig = reissuePrimaryUserIdSig(expiration, secretKeyRingProtector, primaryUserId, prevUserIdSig); secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, primaryUserId, userIdSig); } - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); for (String userId : info.getValidUserIds()) { if (userId.equals(primaryUserId)) { continue; @@ -618,6 +617,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { PGPSignature prevUserIdSig) throws PGPException { SelfSignatureBuilder builder = new SelfSignatureBuilder(secretKeyRing.getSecretKey(), secretKeyRingProtector, prevUserIdSig); + if (referenceTime != null) { + builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } builder.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -638,6 +640,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { PGPPublicKey publicKey = primaryKey.getPublicKey(); SelfSignatureBuilder builder = new SelfSignatureBuilder(primaryKey, secretKeyRingProtector, prevUserIdSig); + if (referenceTime != null) { + builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } builder.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -662,6 +667,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { final Date keyCreationTime = publicKey.getCreationTime(); DirectKeySelfSignatureBuilder builder = new DirectKeySelfSignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig); + if (referenceTime != null) { + builder.getHashedSubpackets().setSignatureCreationTime(referenceTime); + } builder.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { @@ -677,12 +685,12 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { } private PGPSignature getPreviousDirectKeySignature() { - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); return info.getLatestDirectKeySelfSignature(); } private PGPSignature getPreviousUserIdSignatures(String userId) { - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing, referenceTime); return info.getLatestUserIdCertification(userId); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java index 93339f86..156e739f 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/PrimaryKeyBindingSignatureBuilder.java @@ -10,9 +10,11 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; +import org.pgpainless.signature.subpackets.SignatureSubpackets; public class PrimaryKeyBindingSignatureBuilder extends AbstractSignatureBuilder { @@ -21,6 +23,15 @@ public class PrimaryKeyBindingSignatureBuilder extends AbstractSignatureBuilder< super(SignatureType.PRIMARYKEY_BINDING, subkey, subkeyProtector); } + public PrimaryKeyBindingSignatureBuilder(PGPSecretKey secretSubKey, + SecretKeyRingProtector subkeyProtector, + HashAlgorithm hashAlgorithm) + throws PGPException { + super(SignatureType.PRIMARYKEY_BINDING, secretSubKey, subkeyProtector, hashAlgorithm, + SignatureSubpackets.createHashedSubpackets(secretSubKey.getPublicKey()), + SignatureSubpackets.createEmptySubpackets()); + } + public SelfSignatureSubpackets getHashedSubpackets() { return hashedSubpackets; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java index 9c51955d..c15e219e 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilder.java @@ -10,9 +10,11 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSignature; +import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; +import org.pgpainless.signature.subpackets.SignatureSubpackets; public class SubkeyBindingSignatureBuilder extends AbstractSignatureBuilder { @@ -21,6 +23,13 @@ public class SubkeyBindingSignatureBuilder extends AbstractSignatureBuilder - PGPainless.modifyKeyRing(finalSecretKeys).addUserId("A", protector)); + PGPainless.modifyKeyRing(finalSecretKeys, threeHoursLater).addUserId("A", protector)); } @Test public void generateA_primaryExpire_isExpired() - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("A"); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); @@ -76,71 +81,77 @@ public class ChangePrimaryUserIdAndExpirationDatesTest { KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); assertIsPrimaryUserId("A", info); - Thread.sleep(1000); + Date now = new Date(); + Date later = new Date(now.getTime() + millisInHour); - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(), protector) // expire the whole key + secretKeys = PGPainless.modifyKeyRing(secretKeys, now) + .setExpirationDate(later, protector) // expire the whole key .done(); - Thread.sleep(1000); + Date evenLater = new Date(now.getTime() + 2 * millisInHour); - info = PGPainless.inspectKeyRing(secretKeys); + info = PGPainless.inspectKeyRing(secretKeys, evenLater); assertFalse(info.isUserIdValid("A")); // is expired by now } @Test public void generateA_primaryB_primaryExpire_bIsStillPrimary() - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("A"); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); + Date now = new Date(); + // Generate key with primary user-id A KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); assertIsPrimaryUserId("A", info); - Thread.sleep(1000); - - secretKeys = PGPainless.modifyKeyRing(secretKeys) + // later set primary user-id to B + Date t1 = new Date(now.getTime() + millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, t1) .addPrimaryUserId("B", protector) .done(); - info = PGPainless.inspectKeyRing(secretKeys); - + info = PGPainless.inspectKeyRing(secretKeys, t1); assertIsPrimaryUserId("B", info); assertIsNotPrimaryUserId("A", info); - Thread.sleep(1000); - - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(new Date().getTime() + 1000), protector) // expire the whole key in 1 sec + // Even later expire the whole key + Date t2 = new Date(now.getTime() + 2 * millisInHour); + Date expiration = new Date(now.getTime() + 10 * millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, t2) + .setExpirationDate(expiration, protector) // expire the whole key in 1 hour .done(); - info = PGPainless.inspectKeyRing(secretKeys); + Date t3 = new Date(now.getTime() + 3 * millisInHour); + + info = PGPainless.inspectKeyRing(secretKeys, t3); assertIsValid("A", info); assertIsValid("B", info); assertIsPrimaryUserId("B", info); assertIsNotPrimaryUserId("A", info); - Thread.sleep(2000); - - info = PGPainless.inspectKeyRing(secretKeys); + info = PGPainless.inspectKeyRing(secretKeys, expiration); assertIsPrimaryUserId("B", info); // B is still primary, even though assertFalse(info.isUserIdValid("A")); // key is expired by now assertFalse(info.isUserIdValid("B")); } @Test - public void generateA_expire_certify() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { + public void generateA_expire_certify() + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A"); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(new Date().getTime() + 1000), protector) + Date now = new Date(); + Date t1 = new Date(now.getTime() + millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, now) + .setExpirationDate(t1, protector) .done(); - Thread.sleep(2000); - - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(new Date().getTime() + 2000), protector) + Date t2 = new Date(now.getTime() + 2 * millisInHour); + Date t4 = new Date(now.getTime() + 4 * millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, t2) + .setExpirationDate(t4, protector) .done(); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); @@ -150,49 +161,48 @@ public class ChangePrimaryUserIdAndExpirationDatesTest { @Test public void generateA_expire_primaryB_expire_isPrimaryB() - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A"); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); - Thread.sleep(1000); - - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(), protector) + Date now = new Date(); + Date t1 = new Date(now.getTime() + millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, t1) + .setExpirationDate(t1, protector) .done(); - Thread.sleep(2000); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); + Date t2 = new Date(now.getTime() + 2 * millisInHour); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys, t2); assertIsPrimaryUserId("A", info); assertIsNotValid("A", info); // A is expired - secretKeys = PGPainless.modifyKeyRing(secretKeys) + secretKeys = PGPainless.modifyKeyRing(secretKeys, t2) .addPrimaryUserId("B", protector) .done(); - info = PGPainless.inspectKeyRing(secretKeys); + Date t3 = new Date(now.getTime() + 3 * millisInHour); + info = PGPainless.inspectKeyRing(secretKeys, t3); assertIsPrimaryUserId("B", info); assertIsNotValid("B", info); // A and B are still expired assertIsNotValid("A", info); - Thread.sleep(1000); - - secretKeys = PGPainless.modifyKeyRing(secretKeys) - .setExpirationDate(new Date(new Date().getTime() + 10000), protector) + Date t4 = new Date(now.getTime() + 4 * millisInHour); + Date t5 = new Date(now.getTime() + 5 * millisInHour); + secretKeys = PGPainless.modifyKeyRing(secretKeys, t3) + .setExpirationDate(t5, protector) .done(); - Thread.sleep(1000); - info = PGPainless.inspectKeyRing(secretKeys); - + info = PGPainless.inspectKeyRing(secretKeys, t4); assertIsValid("B", info); assertIsValid("A", info); // A got re-validated when changing exp date assertIsPrimaryUserId("B", info); - secretKeys = PGPainless.modifyKeyRing(secretKeys) + secretKeys = PGPainless.modifyKeyRing(secretKeys, t4) .addUserId("A", protector) // re-certify A as non-primary user-id .done(); - info = PGPainless.inspectKeyRing(secretKeys); + info = PGPainless.inspectKeyRing(secretKeys, t4); assertIsValid("B", info); assertIsValid("A", info); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSigTest.java similarity index 80% rename from pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java rename to pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSigTest.java index f7d4d181..674cd6e5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSigTest.java @@ -10,7 +10,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import java.util.Calendar; import java.util.Date; import org.bouncycastle.openpgp.PGPException; @@ -23,12 +22,14 @@ import org.pgpainless.PGPainless; import org.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.util.TestAllImplementations; -public class OldSignatureSubpacketsArePreservedOnNewSig { +public class OldSignatureSubpacketsArePreservedOnNewSigTest { + + private static final long millisInHour = 1000 * 60 * 60; @TestTemplate @ExtendWith(TestAllImplementations.class) public void verifyOldSignatureSubpacketsArePreservedOnNewExpirationDateSig() - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, InterruptedException { + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .simpleEcKeyRing("Alice "); @@ -37,17 +38,14 @@ public class OldSignatureSubpacketsArePreservedOnNewSig { assertEquals(0, oldPackets.getKeyExpirationTime()); - Thread.sleep(1000); Date now = new Date(); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(now); - calendar.add(Calendar.DATE, 5); - Date expiration = calendar.getTime(); // in 5 days + Date t1 = new Date(now.getTime() + millisInHour); + Date expiration = new Date(now.getTime() + 5 * 24 * millisInHour); // in 5 days - secretKeys = PGPainless.modifyKeyRing(secretKeys) + secretKeys = PGPainless.modifyKeyRing(secretKeys, t1) .setExpirationDate(expiration, new UnprotectedKeysProtector()) .done(); - PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys).getLatestUserIdCertification("Alice "); + PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys, t1).getLatestUserIdCertification("Alice "); PGPSignatureSubpacketVector newPackets = newSignature.getHashedSubPackets(); assertNotEquals(0, newPackets.getKeyExpirationTime()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java index 46dc1ac5..72acc125 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/ThirdPartyDirectKeySignatureBuilderTest.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.Collections; +import java.util.Date; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -32,7 +33,7 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; public class ThirdPartyDirectKeySignatureBuilderTest { @Test - public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { + public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("Alice"); @@ -40,9 +41,12 @@ public class ThirdPartyDirectKeySignatureBuilderTest { secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); + Date now = new Date(); + Date t1 = new Date(now.getTime() + 1000 * 60 * 60); dsb.applyCallback(new SelfSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { + hashedSubpackets.setSignatureCreationTime(t1); hashedSubpackets.setKeyFlags(KeyFlag.CERTIFY_OTHER); hashedSubpackets.setPreferredHashAlgorithms(HashAlgorithm.SHA512); hashedSubpackets.setPreferredCompressionAlgorithms(CompressionAlgorithm.ZIP); @@ -51,13 +55,11 @@ public class ThirdPartyDirectKeySignatureBuilderTest { } }); - Thread.sleep(1000); - PGPSignature directKeySig = dsb.build(secretKeys.getPublicKey()); assertNotNull(directKeySig); secretKeys = KeyRingUtils.injectCertification(secretKeys, secretKeys.getPublicKey(), directKeySig); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys, t1); PGPSignature signature = info.getLatestDirectKeySelfSignature(); assertNotNull(signature);