From 32f2bbede73d2f3905ee8f7457d7a7a26f91fc8b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 4 Jul 2022 12:52:49 +0200 Subject: [PATCH] Use PGPPublicKeyRing.join(first, second) for proper MergeCallback --- .../cli/commands/DefaultMergeCallback.java | 85 +++++++++++++++++++ .../java/pgp/cert_d/cli/commands/Import.java | 18 +--- .../pgp/cert_d/cli/commands/MultiImport.java | 12 +-- 3 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java new file mode 100644 index 0000000..2a679af --- /dev/null +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.cert_d.cli.commands; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.pgpainless.PGPainless; +import org.pgpainless.certificate_store.CertificateFactory; +import org.pgpainless.key.OpenPgpFingerprint; +import pgp.certificate_store.Certificate; +import pgp.certificate_store.MergeCallback; + +import java.io.IOException; +import java.util.Iterator; + +public class DefaultMergeCallback implements MergeCallback { + + @Override + public Certificate merge(Certificate data, Certificate existing) throws IOException { + try { + PGPPublicKeyRing existingCert = PGPainless.readKeyRing().publicKeyRing(existing.getInputStream()); + PGPPublicKeyRing updatedCert = PGPainless.readKeyRing().publicKeyRing(data.getInputStream()); + PGPPublicKeyRing mergedCert = PGPPublicKeyRing.join(existingCert, updatedCert); + + printOutDifferences(existingCert, mergedCert); + + return CertificateFactory.certificateFromPublicKeyRing(mergedCert); + } catch (PGPException e) { + throw new RuntimeException(e); + } + } + + private void printOutDifferences(PGPPublicKeyRing existingCert, PGPPublicKeyRing mergedCert) { + int numSigsBefore = countSigs(existingCert); + int numSigsAfter = countSigs(mergedCert); + int newSigs = numSigsAfter - numSigsBefore; + int numUidsBefore = count(existingCert.getPublicKey().getUserIDs()); + int numUidsAfter = count(mergedCert.getPublicKey().getUserIDs()); + int newUids = numUidsAfter - numUidsBefore; + + if (!existingCert.equals(mergedCert)) { + OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(mergedCert); + StringBuilder sb = new StringBuilder(); + sb.append(String.format("Certificate %s has", fingerprint)); + if (newSigs != 0) { + sb.append(String.format(" %d new signatures", newSigs)); + } + if (newUids != 0) { + if (newSigs != 0) { + sb.append(" and"); + } + sb.append(String.format(" %d new UIDs", newUids)); + } + if (newSigs == 0 && newUids == 0) { + sb.append(" changed"); + } + + // In this case it is okay to print to stdout, since we are a CLI app + // CHECKSTYLE:OFF + System.out.println(sb); + // CHECKSTYLE:ON + } + } + + private static int countSigs(PGPPublicKeyRing keys) { + int numSigs = 0; + for (PGPPublicKey key : keys) { + numSigs += count(key.getSignatures()); + } + return numSigs; + } + + // TODO: Use CollectionUtils.count() once available + private static int count(Iterator iterator) { + int num = 0; + while (iterator.hasNext()) { + iterator.next(); + num++; + } + return num; + } +} diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Import.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Import.java index de2cbcd..cfbbf03 100644 --- a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Import.java +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Import.java @@ -4,8 +4,6 @@ package pgp.cert_d.cli.commands; -import java.io.IOException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pgp.cert_d.cli.PGPCertDCli; @@ -14,27 +12,19 @@ import pgp.certificate_store.MergeCallback; import pgp.certificate_store.exception.BadDataException; import picocli.CommandLine; +import java.io.IOException; + @CommandLine.Command(name = "import", description = "Import or update a certificate") public class Import implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(Import.class); - - // TODO: Replace with proper merge callback - private final MergeCallback dummyMerge = new MergeCallback() { - @Override - public Certificate merge(Certificate data, Certificate existing) throws IOException { - return data; - } - }; + private final MergeCallback mergeCallback = new DefaultMergeCallback(); @Override public void run() { try { - Certificate certificate = PGPCertDCli.getCertificateDirectory().insertCertificate(System.in, dummyMerge); - // CHECKSTYLE:OFF - System.out.println(certificate.getFingerprint()); - // CHECKSTYLE:ON + Certificate certificate = PGPCertDCli.getCertificateDirectory().insertCertificate(System.in, mergeCallback); } catch (IOException e) { LOGGER.error("IO-Error.", e); System.exit(-1); diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/MultiImport.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/MultiImport.java index 112403d..6a38460 100644 --- a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/MultiImport.java +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/MultiImport.java @@ -24,14 +24,7 @@ import java.io.IOException; public class MultiImport implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(MultiImport.class); - - // TODO: Replace with proper merge callback - private final MergeCallback dummyMerge = new MergeCallback() { - @Override - public Certificate merge(Certificate data, Certificate existing) throws IOException { - return data; - } - }; + private final MergeCallback dummyMerge = new DefaultMergeCallback(); @Override public void run() { @@ -41,9 +34,6 @@ public class MultiImport implements Runnable { ByteArrayInputStream certIn = new ByteArrayInputStream(cert.getEncoded()); Certificate certificate = PGPCertDCli.getCertificateDirectory() .insertCertificate(certIn, dummyMerge); - // CHECKSTYLE:OFF - System.out.println(certificate.getFingerprint()); - // CHECKSTYLE:ON } } catch (IOException e) { LOGGER.error("IO-Error.", e);