From ee1fd669edee211b2a27ef9f3c7fd93c84065186 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 4 Jul 2022 20:12:42 +0200 Subject: [PATCH] Implement storing of trust-root key --- README.md | 5 + .../java/pgp/cert_d/cli/MergeCallbacks.java | 124 ++++++++++++++++++ .../main/java/pgp/cert_d/cli/PGPCertDCli.java | 11 +- .../cli/commands/DefaultMergeCallback.java | 85 ------------ .../java/pgp/cert_d/cli/commands/Import.java | 5 +- .../java/pgp/cert_d/cli/commands/Insert.java | 6 +- .../java/pgp/cert_d/cli/commands/Setup.java | 107 +++++++++++++++ .../certificate_store/KeyFactory.java | 49 +++++++ .../certificate_store/KeyReader.java | 23 ++++ .../SharedPGPCertificateDirectoryAdapter.java | 32 ++++- ...redPGPCertificateDirectoryAdapterTest.java | 3 +- .../SharedPGPCertificateDirectoryTest.java | 9 +- version.gradle | 2 +- 13 files changed, 357 insertions(+), 104 deletions(-) create mode 100644 pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/MergeCallbacks.java delete mode 100644 pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java create mode 100644 pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Setup.java create mode 100644 pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyFactory.java create mode 100644 pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyReader.java diff --git a/README.md b/README.md index d661028..aa8997e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + # Shared PGP Certificate Directory for Java This repository contains implementations of the [Shared PGP Certificate Directory](https://sequoia-pgp.gitlab.io/pgp-cert-d/) specification using [PGPainless](https://pgpainless.org) as backend. diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/MergeCallbacks.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/MergeCallbacks.java new file mode 100644 index 0000000..02fb292 --- /dev/null +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/MergeCallbacks.java @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.cert_d.cli; + +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.CertificateMerger; +import pgp.certificate_store.Key; +import pgp.certificate_store.KeyMerger; + +import java.io.IOException; +import java.util.Iterator; + +public class MergeCallbacks { + + /** + * Return a {@link CertificateMerger} that merges the two copies of the same certificate (same primary key) into one + * combined certificate. + * + * @return merging callback + */ + public static CertificateMerger mergeCertificates() { + return new CertificateMerger() { + + @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 int countSigs(PGPPublicKeyRing keys) { + int numSigs = 0; + for (PGPPublicKey key : keys) { + numSigs += count(key.getSignatures()); + } + return numSigs; + } + + // TODO: Use CollectionUtils.count() once available + private int count(Iterator iterator) { + int num = 0; + while (iterator.hasNext()) { + iterator.next(); + num++; + } + return num; + } + }; + } + + /** + * Return an implementation of {@link CertificateMerger} that ignores the existing certificate and instead + * returns the first instance. + * + * @return overriding callback + */ + public static CertificateMerger overrideCertificate() { + // noinspection Convert2Lambda + return new CertificateMerger() { + @Override + public Certificate merge(Certificate data, Certificate existing) { + return data; + } + }; + } + + public static KeyMerger overrideKey() { + // noinspection Convert2Lambda + return new KeyMerger() { + @Override + public Key merge(Key data, Key existing) { + return data; + } + }; + } +} diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/PGPCertDCli.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/PGPCertDCli.java index fe0f1b0..4c10cd2 100644 --- a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/PGPCertDCli.java +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/PGPCertDCli.java @@ -5,6 +5,7 @@ package pgp.cert_d.cli; import org.pgpainless.certificate_store.CertificateReader; +import org.pgpainless.certificate_store.KeyReader; import org.pgpainless.certificate_store.SharedPGPCertificateDirectoryAdapter; import pgp.cert_d.BaseDirectoryProvider; import pgp.cert_d.SharedPGPCertificateDirectoryImpl; @@ -12,6 +13,7 @@ import pgp.cert_d.cli.commands.Export; import pgp.cert_d.cli.commands.Get; import pgp.cert_d.cli.commands.Insert; import pgp.cert_d.cli.commands.Import; +import pgp.cert_d.cli.commands.Setup; import pgp.cert_d.jdbc.sqlite.DatabaseSubkeyLookup; import pgp.cert_d.jdbc.sqlite.SqliteSubkeyLookupDaoImpl; import pgp.certificate_store.SubkeyLookup; @@ -26,15 +28,19 @@ import java.sql.SQLException; name = "certificate-store", description = "Store and manage public OpenPGP certificates", subcommands = { + CommandLine.HelpCommand.class, Export.class, Insert.class, Import.class, Get.class, + Setup.class } ) public class PGPCertDCli { - @CommandLine.Option(names = "--base-directory", paramLabel = "DIRECTORY", description = "Overwrite the default certificate directory") + @CommandLine.Option(names = {"-s", "--store"}, paramLabel = "DIRECTORY", + description = "Overwrite the default certificate directory path", + scope = CommandLine.ScopeType.INHERIT) File baseDirectory; private static CertificateDirectory certificateDirectory; @@ -57,7 +63,8 @@ public class PGPCertDCli { certificateDirectory = new SharedPGPCertificateDirectoryImpl( baseDirectory, - new CertificateReader()); + new CertificateReader(), + new KeyReader()); subkeyLookup = new DatabaseSubkeyLookup( SqliteSubkeyLookupDaoImpl.forDatabaseFile(new File(baseDirectory, "_pgpainless_subkey_map.db"))); 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 deleted file mode 100644 index 2a679af..0000000 --- a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/DefaultMergeCallback.java +++ /dev/null @@ -1,85 +0,0 @@ -// 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 a62d11a..9169963 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 @@ -10,9 +10,9 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.pgpainless.PGPainless; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import pgp.cert_d.cli.MergeCallbacks; import pgp.cert_d.cli.PGPCertDCli; import pgp.certificate_store.Certificate; -import pgp.certificate_store.MergeCallback; import pgp.certificate_store.exception.BadDataException; import picocli.CommandLine; @@ -24,7 +24,6 @@ import java.io.IOException; public class Import implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(Import.class); - private final MergeCallback mergeCallback = new DefaultMergeCallback(); @Override public void run() { @@ -33,7 +32,7 @@ public class Import implements Runnable { for (PGPPublicKeyRing cert : certificates) { ByteArrayInputStream certIn = new ByteArrayInputStream(cert.getEncoded()); Certificate certificate = PGPCertDCli.getCertificateDirectory() - .insertCertificate(certIn, mergeCallback); + .insertCertificate(certIn, MergeCallbacks.mergeCertificates()); } } catch (IOException e) { LOGGER.error("IO-Error.", e); diff --git a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Insert.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Insert.java index 2dfa0e8..ad76bb7 100644 --- a/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Insert.java +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Insert.java @@ -6,9 +6,9 @@ package pgp.cert_d.cli.commands; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import pgp.cert_d.cli.MergeCallbacks; import pgp.cert_d.cli.PGPCertDCli; import pgp.certificate_store.Certificate; -import pgp.certificate_store.MergeCallback; import pgp.certificate_store.exception.BadDataException; import picocli.CommandLine; @@ -19,12 +19,12 @@ import java.io.IOException; public class Insert implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(Insert.class); - private final MergeCallback mergeCallback = new DefaultMergeCallback(); @Override public void run() { try { - Certificate certificate = PGPCertDCli.getCertificateDirectory().insertCertificate(System.in, mergeCallback); + Certificate certificate = PGPCertDCli.getCertificateDirectory() + .insertCertificate(System.in, MergeCallbacks.mergeCertificates()); } 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/Setup.java b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Setup.java new file mode 100644 index 0000000..854d4ea --- /dev/null +++ b/pgpainless-cert-d-cli/src/main/java/pgp/cert_d/cli/commands/Setup.java @@ -0,0 +1,107 @@ +// 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.PGPSecretKeyRing; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.key.generation.KeyRingBuilder; +import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.type.KeyType; +import org.pgpainless.key.generation.type.eddsa.EdDSACurve; +import org.pgpainless.util.Passphrase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pgp.cert_d.cli.MergeCallbacks; +import pgp.cert_d.cli.PGPCertDCli; +import pgp.certificate_store.exception.BadDataException; +import picocli.CommandLine; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + +@CommandLine.Command(name = "setup", + description = "Setup a new certificate directory") +public class Setup implements Runnable { + + public static final Logger LOGGER = LoggerFactory.getLogger(Setup.class); + + @CommandLine.ArgGroup() + Exclusive exclusive; + + static class Exclusive { + @CommandLine.Option(names = "--with-password", + paramLabel = "PASSWORD", + description = "Ask for a password for the trust-root key") + String password; + + @CommandLine.Option(names = "--import-from-stdin", + description = "Import trust-root from stdin") + boolean importFromStdin; + } + + + @Override + public void run() { + PGPSecretKeyRing trustRoot; + if (exclusive == null) { + trustRoot = generateTrustRoot(Passphrase.emptyPassphrase()); + } else { + if (exclusive.importFromStdin) { + trustRoot = readTrustRoot(System.in); + } else { + trustRoot = generateTrustRoot(Passphrase.fromPassword(exclusive.password.trim())); + } + } + + try { + InputStream inputStream = new ByteArrayInputStream(trustRoot.getEncoded()); + PGPCertDCli.getCertificateDirectory().insertTrustRoot(inputStream, MergeCallbacks.overrideKey()); + + } catch (BadDataException e) { + throw new RuntimeException(e); + } catch (IOException e) { + LOGGER.error("IO error.", e); + System.exit(-1); + } catch (InterruptedException e) { + LOGGER.error("Thread interrupted.", e); + System.exit(-1); + } + } + + private PGPSecretKeyRing generateTrustRoot(Passphrase passphrase) { + PGPSecretKeyRing trustRoot; + KeyRingBuilder builder = PGPainless.buildKeyRing() + .addUserId("trust-root"); + if (passphrase != null) { + builder.setPassphrase(passphrase); + } + builder.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)); + try { + trustRoot = builder.build(); + } catch (NoSuchAlgorithmException | PGPException | InvalidAlgorithmParameterException e) { + throw new RuntimeException("Cannot generate trust-root OpenPGP key", e); + } + return trustRoot; + } + + private PGPSecretKeyRing readTrustRoot(InputStream inputStream) { + try { + PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(inputStream); + if (secretKeys == null) { + throw new BadDataException(); + } + return secretKeys; + } catch (IOException e) { + throw new RuntimeException("Cannot read trust-root OpenPGP key", e); + } catch (BadDataException e) { + throw new RuntimeException("trust-root does not contain OpenPGP key", e); + } + } +} diff --git a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyFactory.java b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyFactory.java new file mode 100644 index 0000000..aa461b0 --- /dev/null +++ b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyFactory.java @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.certificate_store; + +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.util.encoders.Base64; +import org.pgpainless.PGPainless; +import pgp.certificate_store.Certificate; +import pgp.certificate_store.Key; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class KeyFactory { + + public static Key keyFromSecretKeyRing(PGPSecretKeyRing secretKeyRing) { + + return new Key() { + @Override + public Certificate getCertificate() { + PGPPublicKeyRing publicKeys = PGPainless.extractCertificate(secretKeyRing); + return CertificateFactory.certificateFromPublicKeyRing(publicKeys); + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(secretKeyRing.getEncoded()); + } + + @Override + public String getTag() throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("No MessageDigest for SHA-256 instantiated, although BC is on the classpath: " + e.getMessage()); + } + digest.update(secretKeyRing.getEncoded()); + return Base64.toBase64String(digest.digest()); + } + }; + } +} diff --git a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyReader.java b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyReader.java new file mode 100644 index 0000000..c18569b --- /dev/null +++ b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyReader.java @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.certificate_store; + +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.pgpainless.PGPainless; +import pgp.certificate_store.Key; +import pgp.certificate_store.KeyReaderBackend; +import pgp.certificate_store.exception.BadDataException; + +import java.io.IOException; +import java.io.InputStream; + +public class KeyReader implements KeyReaderBackend { + + @Override + public Key readKey(InputStream data) throws IOException, BadDataException { + final PGPSecretKeyRing key = PGPainless.readKeyRing().secretKeyRing(data); + return KeyFactory.keyFromSecretKeyRing(key); + } +} diff --git a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/SharedPGPCertificateDirectoryAdapter.java b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/SharedPGPCertificateDirectoryAdapter.java index 6cb2ace..968485b 100644 --- a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/SharedPGPCertificateDirectoryAdapter.java +++ b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/SharedPGPCertificateDirectoryAdapter.java @@ -15,8 +15,10 @@ import pgp.cert_d.SharedPGPCertificateDirectory; import pgp.cert_d.SpecialNames; import pgp.certificate_store.Certificate; import pgp.certificate_store.CertificateDirectory; +import pgp.certificate_store.CertificateMerger; import pgp.certificate_store.CertificateStore; -import pgp.certificate_store.MergeCallback; +import pgp.certificate_store.Key; +import pgp.certificate_store.KeyMerger; import pgp.certificate_store.SubkeyLookup; import pgp.certificate_store.exception.BadDataException; import pgp.certificate_store.exception.BadNameException; @@ -66,7 +68,7 @@ public class SharedPGPCertificateDirectoryAdapter } @Override - public Certificate insertCertificate(InputStream data, MergeCallback merge) + public Certificate insertCertificate(InputStream data, CertificateMerger merge) throws IOException, InterruptedException, BadDataException { Certificate certificate = directory.insert(data, merge); storeIdentifierForSubkeys(certificate); @@ -74,7 +76,7 @@ public class SharedPGPCertificateDirectoryAdapter } @Override - public Certificate tryInsertCertificate(InputStream data, MergeCallback merge) + public Certificate tryInsertCertificate(InputStream data, CertificateMerger merge) throws IOException, BadDataException { Certificate certificate = directory.tryInsert(data, merge); storeIdentifierForSubkeys(certificate); @@ -82,13 +84,13 @@ public class SharedPGPCertificateDirectoryAdapter } @Override - public Certificate insertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge) + public Certificate insertCertificateBySpecialName(String specialName, InputStream data, CertificateMerger merge) throws IOException, InterruptedException, BadDataException, BadNameException { return directory.insertWithSpecialName(specialName, data, merge); } @Override - public Certificate tryInsertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge) + public Certificate tryInsertCertificateBySpecialName(String specialName, InputStream data, CertificateMerger merge) throws IOException, BadDataException, BadNameException { return directory.tryInsertWithSpecialName(specialName, data, merge); } @@ -120,4 +122,24 @@ public class SharedPGPCertificateDirectoryAdapter public void storeCertificateSubkeyIds(String certificate, List subkeyIds) throws IOException { subkeyLookup.storeCertificateSubkeyIds(certificate, subkeyIds); } + + @Override + public Key getTrustRoot() throws IOException, BadDataException { + return directory.getTrustRoot(); + } + + @Override + public Key getTrustRootIfChanged(String tag) throws IOException, BadDataException { + return directory.getTrustRootIfChanged(tag); + } + + @Override + public Key insertTrustRoot(InputStream data, KeyMerger keyMerger) throws IOException, InterruptedException, BadDataException { + return directory.insertTrustRoot(data, keyMerger); + } + + @Override + public Key tryInsertTrustRoot(InputStream data, KeyMerger keyMerger) throws IOException, BadDataException { + return directory.tryInsertTrustRoot(data, keyMerger); + } } diff --git a/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryAdapterTest.java b/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryAdapterTest.java index b82f151..d5cc211 100644 --- a/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryAdapterTest.java +++ b/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryAdapterTest.java @@ -27,6 +27,7 @@ import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.pgpainless.certificate_store.CertificateReader; +import org.pgpainless.certificate_store.KeyReader; import org.pgpainless.certificate_store.SharedPGPCertificateDirectoryAdapter; import pgp.cert_d.InMemorySubkeyLookup; import pgp.cert_d.SharedPGPCertificateDirectoryImpl; @@ -50,7 +51,7 @@ public class SharedPGPCertificateDirectoryAdapterTest { @BeforeEach public void setupInstance() throws IOException, NotAStoreException { adapter = new SharedPGPCertificateDirectoryAdapter( - new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader()), + new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader(), new KeyReader()), new InMemorySubkeyLookup()); store = adapter; } diff --git a/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryTest.java b/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryTest.java index 7826746..c43f749 100644 --- a/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryTest.java +++ b/pgpainless-cert-d/src/test/java/org/pgpainless/cert_d/SharedPGPCertificateDirectoryTest.java @@ -33,6 +33,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.certificate_store.CertificateReader; +import org.pgpainless.certificate_store.KeyReader; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.type.KeyType; @@ -41,15 +42,15 @@ import pgp.cert_d.CachingSharedPGPCertificateDirectoryWrapper; import pgp.cert_d.FileLockingMechanism; import pgp.cert_d.SharedPGPCertificateDirectory; import pgp.cert_d.SharedPGPCertificateDirectoryImpl; +import pgp.certificate_store.CertificateMerger; import pgp.certificate_store.exception.BadDataException; import pgp.certificate_store.exception.BadNameException; import pgp.certificate_store.exception.NotAStoreException; import pgp.certificate_store.Certificate; -import pgp.certificate_store.MergeCallback; public class SharedPGPCertificateDirectoryTest { - private static MergeCallback dummyMerge = new MergeCallback() { + private static CertificateMerger dummyMerge = new CertificateMerger() { @Override public Certificate merge(Certificate data, Certificate existing) { return data; @@ -58,9 +59,9 @@ public class SharedPGPCertificateDirectoryTest { private static Stream provideTestSubjects() throws IOException, NotAStoreException { return Stream.of( - new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader()), + new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader(), new KeyReader()), new CachingSharedPGPCertificateDirectoryWrapper( - new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader())) + new SharedPGPCertificateDirectoryImpl(tempDir(), new CertificateReader(), new KeyReader())) ); } diff --git a/version.gradle b/version.gradle index 905f77c..a7ce230 100644 --- a/version.gradle +++ b/version.gradle @@ -13,7 +13,7 @@ allprojects { junitVersion = '5.8.2' mockitoVersion = '4.5.1' pgpainlessVersion = '1.2.1' - pgpCertDJavaVersion = '0.1.1' + pgpCertDJavaVersion = '0.1.2-SNAPSHOT' picocliVersion = '4.6.3' } }