Re-certify expired user-ids when changing key expiration date

This commit is contained in:
Paul Schaub 2021-12-20 13:28:16 +01:00
parent 710f961984
commit 3aa9e2915a
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
3 changed files with 39 additions and 12 deletions

View File

@ -349,6 +349,38 @@ public class KeyRingInfo {
return valid; return valid;
} }
/**
* Return a list of all user-ids that were valid at some point, but might be expired by now.
*
* @return bound user-ids
*/
public List<String> getBoundButPossiblyExpiredUserIds() {
List<String> probablyExpired = new ArrayList<>();
List<String> userIds = getUserIds();
for (String userId : userIds) {
PGPSignature certification = signatures.userIdCertifications.get(userId);
PGPSignature revocation = signatures.userIdRevocations.get(userId);
// Not revoked -> valid
if (revocation == null) {
probablyExpired.add(userId);
continue;
}
// Hard revocation -> invalid
if (SignatureUtils.isHardRevocation(revocation)) {
continue;
}
// Soft revocation -> valid if certification is newer than revocation (revalidation)
if (certification.getCreationTime().after(revocation.getCreationTime())) {
probablyExpired.add(userId);
}
}
return probablyExpired;
}
/** /**
* Return true if the provided user-id is valid. * Return true if the provided user-id is valid.
* *
@ -371,7 +403,6 @@ public class KeyRingInfo {
PGPSignature certification = signatures.userIdCertifications.get(userId); PGPSignature certification = signatures.userIdCertifications.get(userId);
PGPSignature revocation = signatures.userIdRevocations.get(userId); PGPSignature revocation = signatures.userIdRevocations.get(userId);
// If user-id is expired, certification will be null.
if (certification == null) { if (certification == null) {
return false; return false;
} }

View File

@ -146,15 +146,12 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
// Determine previous key expiration date // Determine previous key expiration date
PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey(); PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey();
/*
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
String primaryUserId = info.getPrimaryUserId(); String primaryUserId = info.getPrimaryUserId();
PGPSignature signature = primaryUserId == null ? PGPSignature signature = primaryUserId == null ?
info.getLatestDirectKeySelfSignature() : info.getLatestUserIdCertification(primaryUserId); info.getLatestDirectKeySelfSignature() : info.getLatestUserIdCertification(primaryUserId);
final Date previousKeyExpiration = signature == null ? null : final Date previousKeyExpiration = signature == null ? null :
SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(signature, primaryKey); SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(signature, primaryKey);
*/
final Date previousKeyExpiration = null;
// Add new primary user-id signature // Add new primary user-id signature
addUserId( addUserId(
@ -173,8 +170,8 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
protector); protector);
// unmark previous primary user-ids to be non-primary // unmark previous primary user-ids to be non-primary
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); info = PGPainless.inspectKeyRing(secretKeyRing);
for (String otherUserId : info.getValidUserIds()) { for (String otherUserId : info.getBoundButPossiblyExpiredUserIds()) {
if (userId.toString().equals(otherUserId)) { if (userId.toString().equals(otherUserId)) {
continue; continue;
} }

View File

@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Date; import java.util.Date;
@ -151,7 +150,7 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
@Test @Test
public void generateA_expire_primaryB_expire_isPrimaryB() public void generateA_expire_primaryB_expire_isPrimaryB()
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException, IOException { throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A", null); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A", null);
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
@ -165,7 +164,7 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
assertIsPrimaryUserId("A", info); assertIsPrimaryUserId("A", info);
assertIsNotValid("A", info); assertIsNotValid("A", info); // A is expired
secretKeys = PGPainless.modifyKeyRing(secretKeys) secretKeys = PGPainless.modifyKeyRing(secretKeys)
.addPrimaryUserId("B", protector) .addPrimaryUserId("B", protector)
@ -174,8 +173,8 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
info = PGPainless.inspectKeyRing(secretKeys); info = PGPainless.inspectKeyRing(secretKeys);
assertIsPrimaryUserId("B", info); assertIsPrimaryUserId("B", info);
assertIsValid("B", info); assertIsNotValid("B", info); // A and B are still expired
assertIsNotValid("A", info); // A is still expired assertIsNotValid("A", info);
Thread.sleep(1000); Thread.sleep(1000);
@ -187,7 +186,7 @@ public class ChangePrimaryUserIdAndExpirationDatesTest {
info = PGPainless.inspectKeyRing(secretKeys); info = PGPainless.inspectKeyRing(secretKeys);
assertIsValid("B", info); assertIsValid("B", info);
assertIsNotValid("A", info); // A was expired when the expiration date was changed, so it was not re-certified assertIsValid("A", info); // A got re-validated when changing exp date
assertIsPrimaryUserId("B", info); assertIsPrimaryUserId("B", info);
secretKeys = PGPainless.modifyKeyRing(secretKeys) secretKeys = PGPainless.modifyKeyRing(secretKeys)