Prettify user-id info on armor

This commit is contained in:
Paul Schaub 2022-03-21 16:44:59 +01:00
parent e569c2c991
commit 3585203557
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
2 changed files with 54 additions and 15 deletions

View File

@ -108,12 +108,21 @@ public final class ArmorUtils {
} }
private static Tuple<String, Integer> getPrimaryUserIdAndUserIdCount(PGPPublicKey publicKey) { private static Tuple<String, Integer> getPrimaryUserIdAndUserIdCount(PGPPublicKey publicKey) {
// Quickly determine the primary user-id + number of total user-ids
// NOTE: THIS METHOD DOES NOT CRYPTOGRAPHICALLY VERIFY THE SIGNATURES
// DO NOT RELY ON IT!
Iterator<String> userIds = publicKey.getUserIDs(); Iterator<String> userIds = publicKey.getUserIDs();
int countIdentities = 0; int countIdentities = 0;
String first = null;
String primary = null; String primary = null;
while (userIds.hasNext()) { while (userIds.hasNext()) {
countIdentities++; countIdentities++;
String userId = userIds.next(); String userId = userIds.next();
// remember the first user-id
if (first == null) {
first = userId;
}
if (primary == null) { if (primary == null) {
Iterator<PGPSignature> signatures = publicKey.getSignaturesForID(userId); Iterator<PGPSignature> signatures = publicKey.getSignaturesForID(userId);
while (signatures.hasNext()) { while (signatures.hasNext()) {
@ -125,7 +134,10 @@ public final class ArmorUtils {
} }
} }
} }
return new Tuple<>(primary, countIdentities); // It may happen that no user-id is marked as primary
// in that case print the first one
String printed = primary != null ? primary : first;
return new Tuple<>(printed, countIdentities);
} }
private static MultiMap<String, String> keyToHeader(PGPPublicKey publicKey) { private static MultiMap<String, String> keyToHeader(PGPPublicKey publicKey) {
@ -133,16 +145,24 @@ public final class ArmorUtils {
OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(publicKey); OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(publicKey);
header.put(HEADER_COMMENT, fingerprint.prettyPrint()); header.put(HEADER_COMMENT, fingerprint.prettyPrint());
Tuple<String, Integer> idCount = getPrimaryUserIdAndUserIdCount(publicKey); setUserIdInfoOnHeader(header, publicKey);
if (idCount.getA() != null) {
header.put(HEADER_COMMENT, idCount.getA() + " (Primary)");
}
if (idCount.getB() != 1) {
header.put(HEADER_COMMENT, String.format("Public key contains %d identities", idCount.getB()));
}
return header; return header;
} }
private static void setUserIdInfoOnHeader(MultiMap<String, String> header, PGPPublicKey publicKey) {
Tuple<String, Integer> idCount = getPrimaryUserIdAndUserIdCount(publicKey);
String primary = idCount.getA();
int totalCount = idCount.getB();
if (primary != null) {
header.put(HEADER_COMMENT, primary);
}
if (totalCount == 2) {
header.put(HEADER_COMMENT, "1 further identity");
} else if (totalCount > 2) {
header.put(HEADER_COMMENT, String.format("%d further identities", totalCount - 1));
}
}
private static MultiMap<String, String> keysToHeader(PGPKeyRing keyRing) { private static MultiMap<String, String> keysToHeader(PGPKeyRing keyRing) {
PGPPublicKey publicKey = keyRing.getPublicKey(); PGPPublicKey publicKey = keyRing.getPublicKey();
return keyToHeader(publicKey); return keyToHeader(publicKey);

View File

@ -163,10 +163,27 @@ public class ArmorUtilsTest {
PGPPublicKey publicKey = secretKeyRing.getPublicKey(); PGPPublicKey publicKey = secretKeyRing.getPublicKey();
PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded()); PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded());
String armored = PGPainless.asciiArmor(publicKeyRing); String armored = PGPainless.asciiArmor(publicKeyRing);
Assertions.assertTrue(armored.contains("Comment: Juliet <juliet@montague.lit> (Primary)")); Assertions.assertTrue(armored.contains("Comment: Juliet <juliet@montague.lit>"));
Assertions.assertTrue(armored.contains("Comment: Public key contains 2 identities")); Assertions.assertTrue(armored.contains("Comment: 1 further identity"));
} }
@Test
public void testEvenMoreIdentitiesInHeader() throws Exception {
PGPSecretKeyRing secretKeyRing = PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
.addUserId("Juliet <juliet@montague.lit>")
.addUserId("xmpp:juliet@capulet.lit")
.addUserId("Juliet Montague <j@montague.lit>")
.setPassphrase(Passphrase.fromPassword("test"))
.build();
PGPPublicKey publicKey = secretKeyRing.getPublicKey();
PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded());
String armored = PGPainless.asciiArmor(publicKeyRing);
Assertions.assertTrue(armored.contains("Comment: Juliet <juliet@montague.lit>"));
Assertions.assertTrue(armored.contains("Comment: 2 further identities"));
}
@Test @Test
public void testSingleIdentityInHeader() throws Exception { public void testSingleIdentityInHeader() throws Exception {
PGPSecretKeyRing secretKeyRing = PGPainless.buildKeyRing() PGPSecretKeyRing secretKeyRing = PGPainless.buildKeyRing()
@ -177,13 +194,13 @@ public class ArmorUtilsTest {
PGPPublicKey publicKey = secretKeyRing.getPublicKey(); PGPPublicKey publicKey = secretKeyRing.getPublicKey();
PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded()); PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded());
String armored = PGPainless.asciiArmor(publicKeyRing); String armored = PGPainless.asciiArmor(publicKeyRing);
Assertions.assertTrue(armored.contains("Comment: Juliet <juliet@montague.lit> (Primary)")); Assertions.assertTrue(armored.contains("Comment: Juliet <juliet@montague.lit>"));
Assertions.assertFalse(armored.contains("Comment: Public key contains 1 identities")); Assertions.assertFalse(armored.contains("Comment: 1 total identities"));
} }
@Test @Test
public void testWithoutIdentityInHeader() throws Exception { public void testWithoutIdentityInHeader() throws Exception {
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing("-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" + "\n" +
"xsBNBGIgzE0BCACwxaYg6bpmp0POq1T6yalGE9XaL2IG9d9khDBweZ63s3Pu1pHB\n" + "xsBNBGIgzE0BCACwxaYg6bpmp0POq1T6yalGE9XaL2IG9d9khDBweZ63s3Pu1pHB\n" +
"JtmjgN7Tx3ts6hLzQm3YKYA6zu1MXQ8k2vqtdtGUpZPp18Pbars7yUDqh8QIdFjO\n" + "JtmjgN7Tx3ts6hLzQm3YKYA6zu1MXQ8k2vqtdtGUpZPp18Pbars7yUDqh8QIdFjO\n" +
@ -192,11 +209,13 @@ public class ArmorUtilsTest {
"36qZ5ehAgz9MthPQINnZKpnqidqkGFvjwVFlCMlVSmNCNJmpgGDH3gvkklZHzGsf\n" + "36qZ5ehAgz9MthPQINnZKpnqidqkGFvjwVFlCMlVSmNCNJmpgGDH3gvkklZHzGsf\n" +
"dfzQswd/BQjPsFH9cK+QFYMG6q2zrvM0X9mdABEBAAE=\n" + "dfzQswd/BQjPsFH9cK+QFYMG6q2zrvM0X9mdABEBAAE=\n" +
"=njg8\n" + "=njg8\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n"); "-----END PGP PUBLIC KEY BLOCK-----\n";
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(CERT);
PGPPublicKey publicKey = publicKeys.getPublicKey(); PGPPublicKey publicKey = publicKeys.getPublicKey();
PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded()); PGPPublicKeyRing publicKeyRing = PGPainless.readKeyRing().publicKeyRing(publicKey.getEncoded());
String armored = PGPainless.asciiArmor(publicKeyRing); String armored = PGPainless.asciiArmor(publicKeyRing);
Assertions.assertTrue(armored.contains("Comment: Public key contains 0 identities")); Assertions.assertFalse(armored.contains("Comment: 0 total identities"));
} }
@TestTemplate @TestTemplate