From ee1f90e850937e27f1f5398a1008c5dac4a71adf Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 22 Jan 2021 18:28:48 +0100 Subject: [PATCH] Test and implement revocation of single userIDs --- .../org/pgpainless/key/info/KeyRingInfo.java | 54 ++++----- .../secretkeyring/SecretKeyRingEditor.java | 92 +++++++++++++++ .../SecretKeyRingEditorInterface.java | 95 +++++++++++++++ .../pgpainless/key/util/SignatureUtils.java | 16 +-- .../java/org/pgpainless/key/TestKeys.java | 2 + .../key/info/UserIdRevocationTest.java | 108 +++++++++++++++--- ...gnatureSubpacketsArePreservedOnNewSig.java | 4 +- 7 files changed, 313 insertions(+), 58 deletions(-) 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 aaa6a590..be45bb42 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 @@ -177,8 +177,12 @@ public class KeyRingInfo { } public boolean isUserIdValid(String userId) { + return isUserIdValid(getKeyId(), userId); + } + + public boolean isUserIdValid(long keyId, String userId) { try { - return SignatureUtils.isUserIdValid(getPublicKey(), userId); + return SignatureUtils.isUserIdValid(getPublicKey(keyId), userId); } catch (PGPException e) { return false; } @@ -308,17 +312,9 @@ public class KeyRingInfo { return true; } - public List getSelfSignatures() { - return getSelfSignatures(new OpenPgpV4Fingerprint(getPublicKey())); - } - - public List getSelfSignatures(OpenPgpV4Fingerprint subkeyFingerprint) { - return getSelfSignatures(subkeyFingerprint.getKeyId()); - } - - public List getSelfSignatures(long subkeyId) { + public List getSelfSignaturesOnKey(long subkeyId) { PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subkeyId); - Iterator it = publicKey.getSignaturesForKeyID(subkeyId); + Iterator it = publicKey.getSignaturesForKeyID(keys.getPublicKey().getKeyID()); List signatures = new ArrayList<>(); while (it.hasNext()) { signatures.add(it.next()); @@ -327,25 +323,25 @@ public class KeyRingInfo { return signatures; } - public PGPSignature getLatestValidSelfSignature() throws PGPException { - return getLatestValidSelfSignature(new OpenPgpV4Fingerprint(getPublicKey())); + public PGPSignature getLatestValidSelfSignatureOnKey() throws PGPException { + return getLatestValidSelfSignatureOnKey(new OpenPgpV4Fingerprint(getPublicKey())); } - public PGPSignature getLatestValidSelfSignature(OpenPgpV4Fingerprint fingerprint) throws PGPException { - return getLatestValidSelfSignature(fingerprint.getKeyId()); + public PGPSignature getLatestValidSelfSignatureOnKey(OpenPgpV4Fingerprint fingerprint) throws PGPException { + return getLatestValidSelfSignatureOnKey(fingerprint.getKeyId()); } - public PGPSignature getLatestValidSelfSignature(long subkeyId) throws PGPException { + public PGPSignature getLatestValidSelfSignatureOnKey(long subkeyId) throws PGPException { PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subkeyId); - List signatures = getSelfSignatures(subkeyId); + List signatures = getSelfSignaturesOnKey(keys.getPublicKey().getKeyID()); return getLatestValidSignature(publicKey, signatures, keys); } - public List getBindingSignatures(OpenPgpV4Fingerprint fingerprint) { - return getBindingSignatures(fingerprint.getKeyId()); + public List getBindingSignaturesOnKey(OpenPgpV4Fingerprint fingerprint) { + return getBindingSignaturesOnKey(fingerprint.getKeyId()); } - public List getBindingSignatures(long subkeyId) { + public List getBindingSignaturesOnKey(long subkeyId) { if (subkeyId == getKeyId()) { return Collections.emptyList(); } @@ -353,23 +349,19 @@ public class KeyRingInfo { return SignatureUtils.getBindingSignatures(publicKey, getKeyId()); } - public PGPSignature getLatestValidBindingSignature(long subKeyID) throws PGPException { + public PGPSignature getLatestValidBindingSignatureOnKey(long subKeyID) throws PGPException { PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(keys, subKeyID); - List signatures = getBindingSignatures(subKeyID); + List signatures = getBindingSignaturesOnKey(subKeyID); return getLatestValidSignature(publicKey, signatures, keys); } - public PGPSignature getLatestValidSelfOrBindingSignature() throws PGPException { - return getLatestValidSelfOrBindingSignature(new OpenPgpV4Fingerprint(getPublicKey())); + public PGPSignature getLatestValidSelfOrBindingSignatureOnKey(OpenPgpV4Fingerprint fingerprint) throws PGPException { + return getLatestValidSelfOrBindingSignatureOnKey(fingerprint.getKeyId()); } - public PGPSignature getLatestValidSelfOrBindingSignature(OpenPgpV4Fingerprint fingerprint) throws PGPException { - return getLatestValidSelfOrBindingSignature(fingerprint.getKeyId()); - } - - public PGPSignature getLatestValidSelfOrBindingSignature(long subKeyId) throws PGPException { - PGPSignature self = getLatestValidSelfSignature(subKeyId); - PGPSignature binding = getLatestValidBindingSignature(subKeyId); + public PGPSignature getLatestValidSelfOrBindingSignatureOnKey(long subKeyId) throws PGPException { + PGPSignature self = getLatestValidSelfSignatureOnKey(subKeyId); + PGPSignature binding = getLatestValidBindingSignatureOnKey(subKeyId); if (self == null) { return binding; } 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 72c1ab21..0522ea51 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 @@ -45,6 +45,7 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.pgpainless.PGPainless; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; @@ -52,6 +53,7 @@ import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.KeyRingProtectionSettings; import org.pgpainless.key.protection.PassphraseMapKeyRingProtector; import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; @@ -288,6 +290,96 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { return this; } + @Override + public SecretKeyRingEditorInterface revokeUserIdOnAllSubkeys(String userId, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException { + Iterator iterator = secretKeyRing.getPublicKeys(); + while (iterator.hasNext()) { + PGPPublicKey publicKey = iterator.next(); + try { + revokeUserId(userId, new OpenPgpV4Fingerprint(publicKey), secretKeyRingProtector, revocationAttributes); + } catch (IllegalArgumentException | NoSuchElementException e) { + // skip + } + } + return this; + } + + @Override + public SecretKeyRingEditorInterface revokeUserId(String userId, + OpenPgpV4Fingerprint subkeyFingerprint, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException { + PGPPublicKey publicKey = secretKeyRing.getPublicKey(subkeyFingerprint.getKeyId()); + if (publicKey == null) { + throw new IllegalArgumentException("Key ring does not carry a public key with fingerprint " + subkeyFingerprint); + } + + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + if (!info.getUserIds().contains(userId)) { + throw new NoSuchElementException("Key " + subkeyFingerprint + " does not carry userID '" + userId + '\''); + } + + return doRevokeUserId(userId, subkeyFingerprint.getKeyId(), secretKeyRingProtector, revocationAttributes); + } + + @Override + public SecretKeyRingEditorInterface revokeUserId(String userId, + long subkeyId, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException { + PGPPublicKey publicKey = secretKeyRing.getPublicKey(subkeyId); + if (publicKey == null) { + throw new IllegalArgumentException("Key ring does not carry a public key with ID " + Long.toHexString(subkeyId)); + } + + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + if (!info.getUserIds().contains(userId)) { + throw new NoSuchElementException("Key " + Long.toHexString(subkeyId) + " does not carry userID '" + userId + '\''); + } + + return doRevokeUserId(userId, subkeyId, secretKeyRingProtector, revocationAttributes); + } + + private SecretKeyRingEditorInterface doRevokeUserId(String userId, + long subKeyId, + SecretKeyRingProtector protector, + RevocationAttributes revocationAttributes) throws PGPException { + PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, subKeyId); + PGPSecretKey primaryKey = secretKeyRing.getSecretKey(); + PGPPrivateKey privateKey = unlockSecretKey(primaryKey, protector); + + PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(); + subpacketGenerator.setSignatureCreationTime(false, new Date()); + subpacketGenerator.setRevocable(false, false); + subpacketGenerator.setIssuerFingerprint(false, primaryKey); + if (revocationAttributes != null) { + RevocationAttributes.Reason reason = revocationAttributes.getReason(); + if (reason != RevocationAttributes.Reason.NO_REASON + && reason != RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) { + throw new IllegalArgumentException("Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID"); + } + subpacketGenerator.setRevocationReason(false, revocationAttributes.getReason().code(), revocationAttributes.getDescription()); + } + + PGPSignatureGenerator signatureGenerator = SignatureUtils.getSignatureGeneratorFor(primaryKey); + signatureGenerator.setHashedSubpackets(subpacketGenerator.generate()); + signatureGenerator.init(SignatureType.CERTIFICATION_REVOCATION.getCode(), privateKey); + + PGPSignature revocationSignature = signatureGenerator.generateCertification(userId, publicKey); + publicKey = PGPPublicKey.addCertification(publicKey, userId, revocationSignature); + + PGPPublicKeyRing publicKeyRing = KeyRingUtils.publicKeyRingFrom(secretKeyRing); + publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, publicKey); + secretKeyRing = PGPSecretKeyRing.replacePublicKeys(secretKeyRing, publicKeyRing); + + return this; + } + @Override public SecretKeyRingEditorInterface setExpirationDate(Date expiration, SecretKeyRingProtector secretKeyRingProtector) diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java index 84e3b02f..2e4771a6 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java @@ -198,6 +198,101 @@ public interface SecretKeyRingEditorInterface { RevocationAttributes revocationAttributes) throws PGPException; + /** + * Revoke the given userID on any key in the key ring that is currently carrying the userID. + * + * @param userId userId to revoke + * @param secretKeyRingProtector protector to unlock the primary key + * @return the builder + */ + default SecretKeyRingEditorInterface revokeUserIdOnAllSubkeys(String userId, + SecretKeyRingProtector secretKeyRingProtector) + throws PGPException { + return revokeUserIdOnAllSubkeys(userId, secretKeyRingProtector, null); + } + + /** + * Revoke the given userID on any key in the key ring that is currently carrying the userID. + * + * @param userId userId to revoke + * @param secretKeyRingProtector protector to unlock the primary key + * @param revocationAttributes reason for the revocation + * @return the builder + */ + SecretKeyRingEditorInterface revokeUserIdOnAllSubkeys(String userId, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException; + + /** + * Revoke the given userID on the key that belongs to the given fingerprint. + * + * @param userId userId to revoke + * @param subkeyFingerprint fingerprint of the key on which the userID should be revoked + * @param secretKeyRingProtector protector to unlock the primary key + * @return the builder + */ + default SecretKeyRingEditorInterface revokeUserId(String userId, + OpenPgpV4Fingerprint subkeyFingerprint, + SecretKeyRingProtector secretKeyRingProtector) + throws PGPException { + return revokeUserId(userId, subkeyFingerprint, secretKeyRingProtector, null); + } + + /** + * Revoke the given userID on the key that belongs to the given fingerprint. + * + * @param userId userId to revoke + * @param subkeyFingerprint fingerprint of the key on which the userID should be revoked + * @param secretKeyRingProtector protector to unlock the primary key + * @param revocationAttributes reason for the revocation + * @return the builder + */ + SecretKeyRingEditorInterface revokeUserId(String userId, + OpenPgpV4Fingerprint subkeyFingerprint, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException; + + /** + * Revoke the given userID on the key that belongs to the given key ID. + * + * @param userId userId to revoke + * @param subKeyId ID of the subkey on which we the userID should be revoked + * @param secretKeyRingProtector protector to unlock the primary key + * @return the builder + */ + default SecretKeyRingEditorInterface revokeUserId(String userId, + long subKeyId, + SecretKeyRingProtector secretKeyRingProtector) + throws PGPException { + return revokeUserId(userId, subKeyId, secretKeyRingProtector, null); + } + + /** + * Revoke the given userID on the key that belongs to the given key ID. + * + * @param userId userId to revoke + * @param subkeyId ID of the subkey on which we the userID should be revoked + * @param secretKeyRingProtector protector to unlock the primary key + * @param revocationAttributes reason for the revocation + * @return the builder + */ + SecretKeyRingEditorInterface revokeUserId(String userId, + long subkeyId, + SecretKeyRingProtector secretKeyRingProtector, + RevocationAttributes revocationAttributes) + throws PGPException; + + /** + * Set the expiration date for the primary key of the key ring. + * If the key is supposed to never expire, then an expiration date of null is expected. + * + * @param expiration new expiration date or null + * @param secretKeyRingProtector + * @return + * @throws PGPException + */ SecretKeyRingEditorInterface setExpirationDate(Date expiration, SecretKeyRingProtector secretKeyRingProtector) throws PGPException; diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java index 89ef141f..9f43b94f 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/SignatureUtils.java @@ -116,16 +116,12 @@ public class SignatureUtils { } public static boolean isSelfSignatureValid(PGPSignature signature, PGPPublicKey publicKey) throws PGPException { - switch (signature.getSignatureType()) { - case PGPSignature.POSITIVE_CERTIFICATION: - case PGPSignature.DEFAULT_CERTIFICATION: - for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { - String userId = it.next(); - boolean valid = isSelfSignatureOnUserIdValid(signature, userId, publicKey); - if (valid) { - return true; - } - } + for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { + String userId = it.next(); + boolean valid = isSelfSignatureOnUserIdValid(signature, userId, publicKey); + if (valid) { + return true; + } } return false; } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/TestKeys.java b/pgpainless-core/src/test/java/org/pgpainless/key/TestKeys.java index 4ea69ece..2bf1a286 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/TestKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/TestKeys.java @@ -27,6 +27,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.pgpainless.util.Passphrase; public class TestKeys { @@ -225,6 +226,7 @@ public class TestKeys { public static final String CRYPTIE_UID = "cryptie@encrypted.key"; public static final String CRYPTIE_PASSWORD = "password123"; + public static final Passphrase CRYPTIE_PASSPHRASE = Passphrase.fromPassword(CRYPTIE_PASSWORD); public static final long CRYPTIE_KEY_ID = -821156605394703576L; public static final String CRYPTIE_FINGERPRINT_STRING = "A395D3BA58CA3FA0DE8F2991F49AAA6B067BAB28"; public static final OpenPgpV4Fingerprint CRYPTIE_FINGERPRINT = new OpenPgpV4Fingerprint(CRYPTIE_FINGERPRINT_STRING); 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 35f831b3..c9987c4d 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 @@ -17,6 +17,8 @@ package org.pgpainless.key.info; 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; import java.io.IOException; @@ -24,23 +26,25 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; +import java.util.NoSuchElementException; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.RevocationReason; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.algorithm.SignatureType; +import org.pgpainless.key.TestKeys; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.eddsa.EdDSACurve; import org.pgpainless.key.generation.type.xdh.XDHCurve; +import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; +import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnprotectedKeysProtector; -import org.pgpainless.key.util.KeyRingUtils; +import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.key.util.SignatureUtils; public class UserIdRevocationTest { @@ -59,23 +63,97 @@ public class UserIdRevocationTest { .withoutPassphrase() .build(); - PGPPublicKey publicKey = secretKeys.getPublicKey(); Thread.sleep(1000); - PGPSignatureGenerator generator = SignatureUtils.getSignatureGeneratorFor(secretKeys.getSecretKey()); - generator.init(SignatureType.CERTIFICATION_REVOCATION.getCode(), secretKeys.getSecretKey().extractPrivateKey( - new UnprotectedKeysProtector().getDecryptor(secretKeys.getSecretKey().getKeyID()))); - PGPSignature signature = generator.generateCertification("secondary@key.id", publicKey); - publicKey = PGPPublicKey.addCertification(publicKey, "secondary@key.id", signature); - PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys); - publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, publicKey); - secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys); + // make a copy with revoked subkey + PGPSecretKeyRing revoked = PGPainless.modifyKeyRing(secretKeys) + .revokeUserIdOnAllSubkeys("secondary@key.id", new UnprotectedKeysProtector()) + .done(); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); + KeyRingInfo info = PGPainless.inspectKeyRing(revoked); List userIds = info.getUserIds(); assertEquals(Arrays.asList("primary@key.id", "secondary@key.id"), userIds); assertTrue(info.isUserIdValid("primary@key.id")); assertFalse(info.isUserIdValid("sedondary@key.id")); assertFalse(info.isUserIdValid("tertiary@key.id")); + + info = PGPainless.inspectKeyRing(secretKeys); + assertTrue(info.isUserIdValid("secondary@key.id")); // key on original secret key ring is still valid + + revoked = PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("secondary@key.id", secretKeys.getSecretKey().getKeyID(), new UnprotectedKeysProtector()) + .done(); + info = PGPainless.inspectKeyRing(revoked); + userIds = info.getUserIds(); + assertEquals(Arrays.asList("primary@key.id", "secondary@key.id"), userIds); + assertTrue(info.isUserIdValid("primary@key.id")); + assertFalse(info.isUserIdValid("sedondary@key.id")); + assertFalse(info.isUserIdValid("tertiary@key.id")); + } + + @Test + public void testRevocationWithRevocationReason() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, InterruptedException { + PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() + .withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHCurve._X25519)) + .withKeyFlags(KeyFlag.ENCRYPT_COMMS) + .withDefaultAlgorithms()) + .withMasterKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) + .withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER) + .withDefaultAlgorithms()) + .withPrimaryUserId("primary@key.id") + .withAdditionalUserId("secondary@key.id") + .withoutPassphrase() + .build(); + + Thread.sleep(1000); + + secretKeys = PGPainless.modifyKeyRing(secretKeys) + .revokeUserIdOnAllSubkeys("secondary@key.id", new UnprotectedKeysProtector(), + RevocationAttributes.createCertificateRevocation() + .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) + .withDescription("I lost my mail password")) + .done(); + PGPSignature s = PGPainless.inspectKeyRing(secretKeys).getLatestValidSelfSignatureOnKey(); + PGPSignature signature = SignatureUtils.getLatestSelfSignatureForUserId(secretKeys.getPublicKey(), "secondary@key.id"); + RevocationReason reason = (RevocationReason) signature.getHashedSubPackets() + .getSubpacket(SignatureSubpacketTags.REVOCATION_REASON); + assertNotNull(reason); + assertEquals("I lost my mail password", reason.getRevocationDescription()); + } + + @Test + public void unknownKeyThrowsIllegalArgumentException() throws IOException, PGPException { + PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing(); + SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector + .forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE); + + assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("cryptie@encrypted.key", 1L, protector)); + assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("cryptie@encrypted.key", TestKeys.EMIL_FINGERPRINT, protector)); + } + + @Test + public void unknownUserIdThrowsNoSuchElementException() throws IOException, PGPException { + PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing(); + SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector + .forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE); + + assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("invalid@user.id", TestKeys.CRYPTIE_FINGERPRINT, protector)); + assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("invalid@user.id", TestKeys.CRYPTIE_KEY_ID, protector)); + } + + @Test + public void invalidRevocationReasonThrowsIllegalArgumentException() throws IOException, PGPException { + PGPSecretKeyRing secretKeys = TestKeys.getCryptieSecretKeyRing(); + SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector + .forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE); + + assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys) + .revokeUserId("cryptie@encrypted.key", secretKeys.getSecretKey().getKeyID(), protector, + RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED) + .withDescription("This is not a valid certification revocation reason."))); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java index b0647864..75c03910 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/OldSignatureSubpacketsArePreservedOnNewSig.java @@ -42,7 +42,7 @@ public class OldSignatureSubpacketsArePreservedOnNewSig { OpenPgpV4Fingerprint subkeyFingerprint = new OpenPgpV4Fingerprint(PGPainless.inspectKeyRing(secretKeys).getPublicKeys().get(1)); - PGPSignature oldSignature = PGPainless.inspectKeyRing(secretKeys).getLatestValidSelfOrBindingSignature(subkeyFingerprint); + PGPSignature oldSignature = PGPainless.inspectKeyRing(secretKeys).getLatestValidSelfOrBindingSignatureOnKey(subkeyFingerprint); PGPSignatureSubpacketVector oldPackets = oldSignature.getHashedSubPackets(); assertEquals(0, oldPackets.getKeyExpirationTime()); @@ -51,7 +51,7 @@ public class OldSignatureSubpacketsArePreservedOnNewSig { secretKeys = PGPainless.modifyKeyRing(secretKeys) .setExpirationDate(subkeyFingerprint, new Date(), new UnprotectedKeysProtector()) .done(); - PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys).getLatestValidSelfOrBindingSignature(subkeyFingerprint); + PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys).getLatestValidSelfOrBindingSignatureOnKey(subkeyFingerprint); PGPSignatureSubpacketVector newPackets = newSignature.getHashedSubPackets(); assertNotEquals(0, newPackets.getKeyExpirationTime());