From 5e5e3dd760bddd533cc68d17123c91f35bc77fe4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 25 Aug 2022 12:56:10 +0200 Subject: [PATCH 1/5] Convert certain IOExceptions into BadDataException --- .../certificate_store/KeyMaterialReader.java | 12 ++++- .../KeyMaterialReaderTest.java | 46 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java diff --git a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyMaterialReader.java b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyMaterialReader.java index 409a9fe..112bf18 100644 --- a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyMaterialReader.java +++ b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/KeyMaterialReader.java @@ -19,7 +19,17 @@ public class KeyMaterialReader implements KeyMaterialReaderBackend { @Override public KeyMaterial read(InputStream data, Long tag) throws IOException, BadDataException { - PGPKeyRing keyMaterial = PGPainless.readKeyRing().keyRing(data); + PGPKeyRing keyMaterial; + try { + keyMaterial = PGPainless.readKeyRing().keyRing(data); + } catch (IOException e) { + if (e.getMessage().contains("unknown object in stream") || + e.getMessage().contains("unexpected end of file in armored stream.")) { + throw new BadDataException(); + } else { + throw e; + } + } if (keyMaterial instanceof PGPSecretKeyRing) { return KeyFactory.keyFromSecretKeyRing((PGPSecretKeyRing) keyMaterial, tag); } else if (keyMaterial instanceof PGPPublicKeyRing) { diff --git a/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java new file mode 100644 index 0000000..40d8048 --- /dev/null +++ b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java @@ -0,0 +1,46 @@ +package org.pgpainless.certificate_store; + +import org.junit.jupiter.api.Test; + +public class CertificateAndKeyFactoryTest { + + private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Comment: B21A ABBF 15DF 0FDA 3742 4DE9 AD00 8384 AD0A 064C\n" + + "Comment: Volodymyr Zelenskyy \n" + + "\n" + + "xVgEYwdKchYJKwYBBAHaRw8BAQdARSvx9BDpV0AoNYTmN/wrZXQAB7VzOV0rKEQc\n" + + "OkhbP5wAAP0QE5FCIOvzea7wu3Yw3LDMmOOgMaWXngYp0948VPP2+xM7wsARBB8W\n" + + "CgCDBYJjB0pyBYkFn6YAAwsJBwkQrQCDhK0KBkxHFAAAAAAAHgAgc2FsdEBub3Rh\n" + + "dGlvbnMuc2VxdW9pYS1wZ3Aub3JnpqJFBi2xLa0V4D9k4rlmibhIsMRlcvK/MK83\n" + + "Hjfh2CIDFQoIApsBAh4BFiEEshqrvxXfD9o3Qk3prQCDhK0KBkwAAIwNAP41uywA\n" + + "z+qaRhWoj0stYmmefok4WHXk34jfameTopO1LAEA6L6/crPYzIcAZraaz0s5AM/2\n" + + "OvJZR8LaBSj92uBDbAzNJlZvbG9keW15ciBaZWxlbnNreXkgPHplbGVuc2t5eUBn\n" + + "b3YudWE+wsAUBBMWCgCGBYJjB0pyBYkFn6YAAwsJBwkQrQCDhK0KBkxHFAAAAAAA\n" + + "HgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnuD/qrSefYHLBUQ70qQhb\n" + + "cClzXkMQCoGO4S3WJzib/IADFQoIApkBApsBAh4BFiEEshqrvxXfD9o3Qk3prQCD\n" + + "hK0KBkwAAL8DAP42+z1sDJlv64PW4iq2ODYcdY1NSptjzfiQ2hyodNBFpgD+Mkiv\n" + + "9e2bFvlRj2Q5Brypy3ZwKvGtikUe3s+NlKMPlgTHWARjB0pyFgkrBgEEAdpHDwEB\n" + + "B0BNjMb280vf8zNJ/YAtcc6nLe8uCSTtxKKHF0Go9kU+VgABAKTAn5oiixsRxfEb\n" + + "k1I6WQbIGk/XNPZ241k65WRdg1qODvXCwMUEGBYKATcFgmMHSnIFiQWfpgAJEK0A\n" + + "g4StCgZMRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ9el\n" + + "oLUj9wUjbwI91PYFiLbcIMYw+G2w85rw5nLaXzHEApsCvqAEGRYKAG8FgmMHSnIJ\n" + + "EDWWq2wLl0UaRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z622EwAZW4CP32L2ysCphw7DasyPOdBpDsiMv2LB7dz8FiEEZQkoZquL7+v7NBoA\n" + + "NZarbAuXRRoAAOu3AP0X6dEVabI84d7t4AwRpmEiShSum9CJiODSs580lzkjlAD/\n" + + "QOGSIE5iO155kPudwi8ubif+v4tXe4Ro++tIP85bTQ8WIQSyGqu/Fd8P2jdCTemt\n" + + "AIOErQoGTAAAScwBAJ2A8vuK0wEMQHhVJR1lcjUaUm7EoBVuplF85dFipXjuAP40\n" + + "bwvHajjg8wFLo8pwAATBd2gnNYXXDK7J2WwiZPAKAcddBGMHSnISCisGAQQBl1UB\n" + + "BQEBB0DG0ue4giLkEecm/qz1wPQQBIl5v18/9M0SrMUk/M16agMBCAcAAP9Z4R+9\n" + + "tmG3aUKOB9nwN1t4N1GnbYCmaZzTn3uJXLetYBFTwsAGBBgWCgB4BYJjB0pyBYkF\n" + + "n6YACRCtAIOErQoGTEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn\n" + + "cC5vcmfZNJZA/uU2jCW1zQzCT9oK5hj0sL2taNvDFlLtkMCNqwKbDBYhBLIaq78V\n" + + "3w/aN0JN6a0Ag4StCgZMAAAlKwD/eUEbC8MoIitKulDZawtlC0rSITXtQJqUkGNc\n" + + "ujTPgIAA/1p/Y3sHn4nhmYcVX902BRXBp8YMD/cHQWZkWPhvM9YF\n" + + "=3nhb\n" + + "-----END PGP PRIVATE KEY BLOCK-----\n"; + + @Test + public void test() { + + } +} From 6995879e05c9808f3fdcbbbc2d2db3aaf38fe468 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 25 Aug 2022 12:56:25 +0200 Subject: [PATCH 2/5] Bump cert-d-java to 0.2.0 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index 45efb59..982e05b 100644 --- a/version.gradle +++ b/version.gradle @@ -13,7 +13,7 @@ allprojects { junitVersion = '5.8.2' mockitoVersion = '4.5.1' pgpainlessVersion = '1.3.5' - pgpCertDJavaVersion = '0.1.2-SNAPSHOT' + pgpCertDJavaVersion = '0.2.0' picocliVersion = '4.6.3' } } From 80600c86ce2ee90fdb27318ff02f5407aef8bb09 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 25 Aug 2022 12:56:56 +0200 Subject: [PATCH 3/5] Refactor MergeCallbacks --- .../java/pgp/cert_d/cli/commands/Import.java | 2 +- .../java/pgp/cert_d/cli/commands/Insert.java | 2 +- .../java/pgp/cert_d/cli/commands/Setup.java | 2 +- .../certificate_store/MergeCallbacks.java | 104 +++++++++--------- .../KeyMaterialReaderTest.java | 93 +++++++++++++++- 5 files changed, 145 insertions(+), 58 deletions(-) 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 d7d29fb..3dfcfb4 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 @@ -32,7 +32,7 @@ public class Import implements Runnable { for (PGPPublicKeyRing cert : certificates) { ByteArrayInputStream certIn = new ByteArrayInputStream(cert.getEncoded()); Certificate certificate = PGPCertDCli.getCertificateDirectory() - .insert(certIn, MergeCallbacks.mergeCertificates()); + .insert(certIn, MergeCallbacks.mergeWithExisting()); } } 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 c88ead0..25987d0 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 @@ -24,7 +24,7 @@ public class Insert implements Runnable { public void run() { try { Certificate certificate = PGPCertDCli.getCertificateDirectory() - .insert(System.in, MergeCallbacks.mergeCertificates()); + .insert(System.in, MergeCallbacks.mergeWithExisting()); } 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 index 0178e0a..2cdc5c4 100644 --- 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 @@ -61,7 +61,7 @@ public class Setup implements Runnable { try { InputStream inputStream = new ByteArrayInputStream(trustRoot.getEncoded()); - PGPCertDCli.getCertificateDirectory().insertTrustRoot(inputStream, MergeCallbacks.overrideKey()); + PGPCertDCli.getCertificateDirectory().insertTrustRoot(inputStream, MergeCallbacks.overrideExisting()); } catch (BadDataException e) { throw new RuntimeException(e); diff --git a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/MergeCallbacks.java b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/MergeCallbacks.java index 6e84f26..cbd3a44 100644 --- a/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/MergeCallbacks.java +++ b/pgpainless-cert-d/src/main/java/org/pgpainless/certificate_store/MergeCallbacks.java @@ -16,7 +16,6 @@ import pgp.certificate_store.certificate.KeyMaterialMerger; import pgp.certificate_store.exception.BadDataException; import java.io.IOException; -import java.util.Arrays; import java.util.Iterator; public class MergeCallbacks { @@ -27,11 +26,13 @@ public class MergeCallbacks { * * @return merging callback */ - public static KeyMaterialMerger mergeCertificates() { + public static KeyMaterialMerger mergeWithExisting() { return new KeyMaterialMerger() { @Override - public KeyMaterial merge(KeyMaterial data, KeyMaterial existing) throws IOException { + public KeyMaterial merge(KeyMaterial data, KeyMaterial existing) + throws IOException { + // Simple cases: one is null -> return other if (data == null) { return existing; } @@ -46,51 +47,66 @@ public class MergeCallbacks { PGPKeyRing mergedKeyRing; if (existingKeyRing instanceof PGPPublicKeyRing) { - PGPPublicKeyRing existingCert = (PGPPublicKeyRing) existingKeyRing; - if (updatedKeyRing instanceof PGPPublicKeyRing) { - mergedKeyRing = PGPPublicKeyRing.join(existingCert, (PGPPublicKeyRing) updatedKeyRing); - } else if (updatedKeyRing instanceof PGPSecretKeyRing) { - PGPPublicKeyRing updatedPublicKeys = PGPainless.extractCertificate((PGPSecretKeyRing) updatedKeyRing); - PGPPublicKeyRing mergedPublicKeys = PGPPublicKeyRing.join(existingCert, updatedPublicKeys); - updatedKeyRing = PGPSecretKeyRing.replacePublicKeys((PGPSecretKeyRing) updatedKeyRing, mergedPublicKeys); - mergedKeyRing = updatedKeyRing; - } else { - throw new IOException(new BadDataException()); - } + mergedKeyRing = mergeWithCert((PGPPublicKeyRing) existingKeyRing, updatedKeyRing); } else if (existingKeyRing instanceof PGPSecretKeyRing) { - PGPSecretKeyRing existingKey = (PGPSecretKeyRing) existingKeyRing; - PGPPublicKeyRing existingCert = PGPainless.extractCertificate(existingKey); - if (updatedKeyRing instanceof PGPPublicKeyRing) { - PGPPublicKeyRing updatedCert = (PGPPublicKeyRing) updatedKeyRing; - PGPPublicKeyRing mergedCert = PGPPublicKeyRing.join(existingCert, updatedCert); - mergedKeyRing = PGPSecretKeyRing.replacePublicKeys(existingKey, mergedCert); - } else if (updatedKeyRing instanceof PGPSecretKeyRing) { - PGPSecretKeyRing updatedKey = (PGPSecretKeyRing) updatedKeyRing; - if (!Arrays.equals(existingKey.getEncoded(), updatedKey.getEncoded())) { - // Merging secret keys is not supported. - return existing; - } - mergedKeyRing = existingKeyRing; - } else { - throw new IOException(new BadDataException()); - } + mergedKeyRing = mergeWithKey(existingKeyRing, updatedKeyRing); } else { throw new IOException(new BadDataException()); } printOutDifferences(existingKeyRing, mergedKeyRing); - if (mergedKeyRing instanceof PGPPublicKeyRing) { - return CertificateFactory.certificateFromPublicKeyRing((PGPPublicKeyRing) mergedKeyRing, null); - } else { - return KeyFactory.keyFromSecretKeyRing((PGPSecretKeyRing) mergedKeyRing, null); - } + return toKeyMaterial(mergedKeyRing); } catch (PGPException e) { throw new RuntimeException(e); } } + private PGPKeyRing mergeWithCert(PGPPublicKeyRing existingKeyRing, PGPKeyRing updatedKeyRing) + throws PGPException, IOException { + PGPKeyRing mergedKeyRing; + PGPPublicKeyRing existingCert = existingKeyRing; + if (updatedKeyRing instanceof PGPPublicKeyRing) { + mergedKeyRing = PGPPublicKeyRing.join(existingCert, (PGPPublicKeyRing) updatedKeyRing); + } else if (updatedKeyRing instanceof PGPSecretKeyRing) { + PGPPublicKeyRing updatedPublicKeys = PGPainless.extractCertificate((PGPSecretKeyRing) updatedKeyRing); + PGPPublicKeyRing mergedPublicKeys = PGPPublicKeyRing.join(existingCert, updatedPublicKeys); + updatedKeyRing = PGPSecretKeyRing.replacePublicKeys((PGPSecretKeyRing) updatedKeyRing, mergedPublicKeys); + mergedKeyRing = updatedKeyRing; + } else { + throw new IOException(new BadDataException()); + } + return mergedKeyRing; + } + + private PGPKeyRing mergeWithKey(PGPKeyRing existingKeyRing, PGPKeyRing updatedKeyRing) + throws PGPException, IOException { + PGPKeyRing mergedKeyRing; + PGPSecretKeyRing existingKey = (PGPSecretKeyRing) existingKeyRing; + PGPPublicKeyRing existingCert = PGPainless.extractCertificate(existingKey); + if (updatedKeyRing instanceof PGPPublicKeyRing) { + PGPPublicKeyRing updatedCert = (PGPPublicKeyRing) updatedKeyRing; + PGPPublicKeyRing mergedCert = PGPPublicKeyRing.join(existingCert, updatedCert); + mergedKeyRing = PGPSecretKeyRing.replacePublicKeys(existingKey, mergedCert); + } else if (updatedKeyRing instanceof PGPSecretKeyRing) { + // Merging keys is not supported + mergedKeyRing = existingKeyRing; + } else { + throw new IOException(new BadDataException()); + } + return mergedKeyRing; + } + + private KeyMaterial toKeyMaterial(PGPKeyRing mergedKeyRing) + throws IOException { + if (mergedKeyRing instanceof PGPPublicKeyRing) { + return CertificateFactory.certificateFromPublicKeyRing((PGPPublicKeyRing) mergedKeyRing, null); + } else { + return KeyFactory.keyFromSecretKeyRing((PGPSecretKeyRing) mergedKeyRing, null); + } + } + private void printOutDifferences(PGPKeyRing existingCert, PGPKeyRing mergedCert) { int numSigsBefore = countSigs(existingCert); int numSigsAfter = countSigs(mergedCert); @@ -145,23 +161,7 @@ public class MergeCallbacks { }; } - /** - * Return an implementation of {@link KeyMaterialMerger} that ignores the existing certificate and instead - * returns the first instance. - * - * @return overriding callback - */ - public static KeyMaterialMerger overrideCertificate() { - // noinspection Convert2Lambda - return new KeyMaterialMerger() { - @Override - public KeyMaterial merge(KeyMaterial data, KeyMaterial existing) { - return data; - } - }; - } - - public static KeyMaterialMerger overrideKey() { + public static KeyMaterialMerger overrideExisting() { // noinspection Convert2Lambda return new KeyMaterialMerger() { @Override diff --git a/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java index 40d8048..327abdc 100644 --- a/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java +++ b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/KeyMaterialReaderTest.java @@ -1,8 +1,27 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package org.pgpainless.certificate_store; import org.junit.jupiter.api.Test; +import pgp.certificate_store.certificate.Certificate; +import pgp.certificate_store.certificate.Key; +import pgp.certificate_store.certificate.KeyMaterial; +import pgp.certificate_store.exception.BadDataException; -public class CertificateAndKeyFactoryTest { +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class KeyMaterialReaderTest { private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + "Comment: B21A ABBF 15DF 0FDA 3742 4DE9 AD00 8384 AD0A 064C\n" + @@ -39,8 +58,76 @@ public class CertificateAndKeyFactoryTest { "=3nhb\n" + "-----END PGP PRIVATE KEY BLOCK-----\n"; + private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: B21A ABBF 15DF 0FDA 3742 4DE9 AD00 8384 AD0A 064C\n" + + "Comment: Volodymyr Zelenskyy \n" + + "\n" + + "xjMEYwdKchYJKwYBBAHaRw8BAQdARSvx9BDpV0AoNYTmN/wrZXQAB7VzOV0rKEQc\n" + + "OkhbP5zCwBEEHxYKAIMFgmMHSnIFiQWfpgADCwkHCRCtAIOErQoGTEcUAAAAAAAe\n" + + "ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmemokUGLbEtrRXgP2TiuWaJ\n" + + "uEiwxGVy8r8wrzceN+HYIgMVCggCmwECHgEWIQSyGqu/Fd8P2jdCTemtAIOErQoG\n" + + "TAAAjA0A/jW7LADP6ppGFaiPSy1iaZ5+iThYdeTfiN9qZ5Oik7UsAQDovr9ys9jM\n" + + "hwBmtprPSzkAz/Y68llHwtoFKP3a4ENsDM0mVm9sb2R5bXlyIFplbGVuc2t5eSA8\n" + + "emVsZW5za3l5QGdvdi51YT7CwBQEExYKAIYFgmMHSnIFiQWfpgADCwkHCRCtAIOE\n" + + "rQoGTEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcme4P+qt\n" + + "J59gcsFRDvSpCFtwKXNeQxAKgY7hLdYnOJv8gAMVCggCmQECmwECHgEWIQSyGqu/\n" + + "Fd8P2jdCTemtAIOErQoGTAAAvwMA/jb7PWwMmW/rg9biKrY4Nhx1jU1Km2PN+JDa\n" + + "HKh00EWmAP4ySK/17ZsW+VGPZDkGvKnLdnAq8a2KRR7ez42Uow+WBM4zBGMHSnIW\n" + + "CSsGAQQB2kcPAQEHQE2MxvbzS9/zM0n9gC1xzqct7y4JJO3EoocXQaj2RT5WwsDF\n" + + "BBgWCgE3BYJjB0pyBYkFn6YACRCtAIOErQoGTEcUAAAAAAAeACBzYWx0QG5vdGF0\n" + + "aW9ucy5zZXF1b2lhLXBncC5vcmfXpaC1I/cFI28CPdT2BYi23CDGMPhtsPOa8OZy\n" + + "2l8xxAKbAr6gBBkWCgBvBYJjB0pyCRA1lqtsC5dFGkcUAAAAAAAeACBzYWx0QG5v\n" + + "dGF0aW9ucy5zZXF1b2lhLXBncC5vcmetthMAGVuAj99i9srAqYcOw2rMjznQaQ7I\n" + + "jL9iwe3c/BYhBGUJKGari+/r+zQaADWWq2wLl0UaAADrtwD9F+nRFWmyPOHe7eAM\n" + + "EaZhIkoUrpvQiYjg0rOfNJc5I5QA/0DhkiBOYjteeZD7ncIvLm4n/r+LV3uEaPvr\n" + + "SD/OW00PFiEEshqrvxXfD9o3Qk3prQCDhK0KBkwAAEnMAQCdgPL7itMBDEB4VSUd\n" + + "ZXI1GlJuxKAVbqZRfOXRYqV47gD+NG8Lx2o44PMBS6PKcAAEwXdoJzWF1wyuydls\n" + + "ImTwCgHOOARjB0pyEgorBgEEAZdVAQUBAQdAxtLnuIIi5BHnJv6s9cD0EASJeb9f\n" + + "P/TNEqzFJPzNemoDAQgHwsAGBBgWCgB4BYJjB0pyBYkFn6YACRCtAIOErQoGTEcU\n" + + "AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfZNJZA/uU2jCW1\n" + + "zQzCT9oK5hj0sL2taNvDFlLtkMCNqwKbDBYhBLIaq78V3w/aN0JN6a0Ag4StCgZM\n" + + "AAAlKwD/eUEbC8MoIitKulDZawtlC0rSITXtQJqUkGNcujTPgIAA/1p/Y3sHn4nh\n" + + "mYcVX902BRXBp8YMD/cHQWZkWPhvM9YF\n" + + "=o64m\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + private final KeyMaterialReader reader = new KeyMaterialReader(); + private final Charset UTF8 = Charset.forName("UTF8"); + @Test - public void test() { - + public void readBadDataTest() { + assertThrows(BadDataException.class, () -> reader.read( + new ByteArrayInputStream(CERT.substring(0, CERT.length() - 100).getBytes(UTF8)), null)); + } + + @Test + public void readIncompleteDataTest() { + assertThrows(BadDataException.class, () -> reader.read( + new ByteArrayInputStream("ThisIsNotOpenPGPDataAtAllLol".getBytes(UTF8)), null)); + } + + @Test + public void readKeyTest() throws BadDataException, IOException { + KeyMaterial keyMaterial = reader.read(new ByteArrayInputStream(KEY.getBytes(UTF8)), 12L); + assertNotNull(keyMaterial); + assertTrue(keyMaterial instanceof Key); + Key key = (Key) keyMaterial; + assertEquals("b21aabbf15df0fda37424de9ad008384ad0a064c", key.getFingerprint()); + assertEquals(12L, key.getTag()); + Certificate certificate = key.getCertificate(); + assertEquals(key.getFingerprint(), certificate.getFingerprint()); + assertEquals(key.getTag(), certificate.getTag()); + assertEquals(key.getSubkeyIds(), certificate.getSubkeyIds()); + } + + @Test + public void readCertTest() throws BadDataException, IOException { + KeyMaterial keyMaterial = reader.read(new ByteArrayInputStream(CERT.getBytes(UTF8)), null); + assertNotNull(keyMaterial); + assertTrue(keyMaterial instanceof Certificate); + Certificate certificate = (Certificate) keyMaterial; + assertEquals("b21aabbf15df0fda37424de9ad008384ad0a064c", certificate.getFingerprint()); + assertNull(certificate.getTag()); + assertSame(certificate, certificate.asCertificate()); } } From a7a89a31ed251b584c2f3e1fdce4ba4c34910768 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 25 Aug 2022 13:54:39 +0200 Subject: [PATCH 4/5] Add test for MergeCallbacks --- .../certificate_store/MergeCallbacksTest.java | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/MergeCallbacksTest.java diff --git a/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/MergeCallbacksTest.java b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/MergeCallbacksTest.java new file mode 100644 index 0000000..6101154 --- /dev/null +++ b/pgpainless-cert-d/src/test/java/org/pgpainless/certificate_store/MergeCallbacksTest.java @@ -0,0 +1,244 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.certificate_store; + +import org.bouncycastle.util.io.Streams; +import org.junit.jupiter.api.Test; +import pgp.certificate_store.certificate.KeyMaterial; +import pgp.certificate_store.certificate.KeyMaterialMerger; +import pgp.certificate_store.exception.BadDataException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class MergeCallbacksTest { + + private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 8E0F C503 D081 002A 2BC8 60A1 1CFC 3439 106D 1DD1\n" + + "Comment: Marge Simpson \n" + + "\n" + + "lFgEYwdeTxYJKwYBBAHaRw8BAQdA/culAZNfjpo8NyfJv9ggwUJBY/9Ps27wRzj1\n" + + "3i5Y/akAAQCel3XRH2ERU2+6C4kJEb3YXNtbH3CHJhkP+co3JQBJygz0tCBNYXJn\n" + + "ZSBTaW1wc29uIDxtYXJnZUBzaW1wc29uLnR2PoiPBBMWCgBBBQJjB15QCRAc/DQ5\n" + + "EG0d0RYhBI4PxQPQgQAqK8hgoRz8NDkQbR3RAp4BApsBBRYCAwEABAsJCAcFFQoJ\n" + + "CAsCmQEAAC34AP9jqFThNA0FeNxEEh+BKA/diGkxsAZaI0HscLeuoECOoAD9FjcO\n" + + "1TtI0UjF1wvRAGuoL6PrgNQ/kAE++zyzaXlbDAKcXQRjB15QEgorBgEEAZdVAQUB\n" + + "AQdAHQPnqtZwENOdLiD19wgjUpo/U0pJ4s/HCjgUQrFro38DAQgHAAD/VrXgi8fE\n" + + "UUAVLn+C3GXCJV0CBnCvLvMn6QwDUIbi1sgQF4h1BBgWCgAdBQJjB15QAp4BApsM\n" + + "BRYCAwEABAsJCAcFFQoJCAsACgkQHPw0ORBtHdGDdgD8DS1IyA0j4mnKPw93BLLn\n" + + "Wkt6Tc8tEc1Yy3fddhaGXXMBAIMu6ww43TM2EdQM/2orh8MhDZaBdDnD4egQ1ES4\n" + + "zxYJnFgEYwdeUBYJKwYBBAHaRw8BAQdAw/Pfecs1QEMAuTY8wGqEgpigYFx6GLHS\n" + + "qpgJkVds4hsAAP9JZ3XgkUguI4tUO9CyGCwxfBoUv1+F+XlYoxlyZV0M2A4qiNUE\n" + + "GBYKAH0FAmMHXlACngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJjB15Q\n" + + "AAoJEO8Ou9qRn4/Lk4ABAOTUMLOTPL9svmyoHmeVKYh4pL92/+zrsNL2Kh8BX7/F\n" + + "APsE3/N3J5MB2ZEyzNSU84STG3Aqa+2I2u4w58CeL8eRCAAKCRAc/DQ5EG0d0QBm\n" + + "AQDvHR1I/B4VBqMu44wcw1czqqFojv1KQMETnLCfU5Q4cwD+Mt6mNoXADACcnw2P\n" + + "3u5u3NoFQ0v2vFSaCoBxVzUQrgo=\n" + + "=OKv0\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + private static final String KEY_WITH_SIG = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 8E0F C503 D081 002A 2BC8 60A1 1CFC 3439 106D 1DD1\n" + + "Comment: Marge Simpson \n" + + "\n" + + "lFgEYwdeTxYJKwYBBAHaRw8BAQdA/culAZNfjpo8NyfJv9ggwUJBY/9Ps27wRzj1\n" + + "3i5Y/akAAQCel3XRH2ERU2+6C4kJEb3YXNtbH3CHJhkP+co3JQBJygz0iHUEHxYK\n" + + "ACcFAmMHXlAJEBz8NDkQbR3RFiEEjg/FA9CBACoryGChHPw0ORBtHdEAAEI4AP4w\n" + + "H667enh2czzfH8n4NeluivHQIavx6THn40MELAiBQQD/T3IdrTn0YDVmfdAGmCPL\n" + + "lNjOxPDus5SESpLuS6A7IAi0IE1hcmdlIFNpbXBzb24gPG1hcmdlQHNpbXBzb24u\n" + + "dHY+iI8EExYKAEEFAmMHXlAJEBz8NDkQbR3RFiEEjg/FA9CBACoryGChHPw0ORBt\n" + + "HdECngECmwEFFgIDAQAECwkIBwUVCgkICwKZAQAALfgA/2OoVOE0DQV43EQSH4Eo\n" + + "D92IaTGwBlojQexwt66gQI6gAP0WNw7VO0jRSMXXC9EAa6gvo+uA1D+QAT77PLNp\n" + + "eVsMApxdBGMHXlASCisGAQQBl1UBBQEBB0AdA+eq1nAQ050uIPX3CCNSmj9TSkni\n" + + "z8cKOBRCsWujfwMBCAcAAP9WteCLx8RRQBUuf4LcZcIlXQIGcK8u8yfpDANQhuLW\n" + + "yBAXiHUEGBYKAB0FAmMHXlACngECmwwFFgIDAQAECwkIBwUVCgkICwAKCRAc/DQ5\n" + + "EG0d0YN2APwNLUjIDSPiaco/D3cEsudaS3pNzy0RzVjLd912FoZdcwEAgy7rDDjd\n" + + "MzYR1Az/aiuHwyENloF0OcPh6BDURLjPFgmcWARjB15QFgkrBgEEAdpHDwEBB0DD\n" + + "8995yzVAQwC5NjzAaoSCmKBgXHoYsdKqmAmRV2ziGwAA/0lndeCRSC4ji1Q70LIY\n" + + "LDF8GhS/X4X5eVijGXJlXQzYDiqI1QQYFgoAfQUCYwdeUAKeAQKbAgUWAgMBAAQL\n" + + "CQgHBRUKCQgLXyAEGRYKAAYFAmMHXlAACgkQ7w672pGfj8uTgAEA5NQws5M8v2y+\n" + + "bKgeZ5UpiHikv3b/7Ouw0vYqHwFfv8UA+wTf83cnkwHZkTLM1JTzhJMbcCpr7Yja\n" + + "7jDnwJ4vx5EIAAoJEBz8NDkQbR3RAGYBAO8dHUj8HhUGoy7jjBzDVzOqoWiO/UpA\n" + + "wROcsJ9TlDhzAP4y3qY2hcAMAJyfDY/e7m7c2gVDS/a8VJoKgHFXNRCuCg==\n" + + "=WrKH\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 8E0F C503 D081 002A 2BC8 60A1 1CFC 3439 106D 1DD1\n" + + "Comment: Marge Simpson \n" + + "\n" + + "mDMEYwdeTxYJKwYBBAHaRw8BAQdA/culAZNfjpo8NyfJv9ggwUJBY/9Ps27wRzj1\n" + + "3i5Y/am0IE1hcmdlIFNpbXBzb24gPG1hcmdlQHNpbXBzb24udHY+iI8EExYKAEEF\n" + + "AmMHXlAJEBz8NDkQbR3RFiEEjg/FA9CBACoryGChHPw0ORBtHdECngECmwEFFgID\n" + + "AQAECwkIBwUVCgkICwKZAQAALfgA/2OoVOE0DQV43EQSH4EoD92IaTGwBlojQexw\n" + + "t66gQI6gAP0WNw7VO0jRSMXXC9EAa6gvo+uA1D+QAT77PLNpeVsMArg4BGMHXlAS\n" + + "CisGAQQBl1UBBQEBB0AdA+eq1nAQ050uIPX3CCNSmj9TSkniz8cKOBRCsWujfwMB\n" + + "CAeIdQQYFgoAHQUCYwdeUAKeAQKbDAUWAgMBAAQLCQgHBRUKCQgLAAoJEBz8NDkQ\n" + + "bR3Rg3YA/A0tSMgNI+Jpyj8PdwSy51pLek3PLRHNWMt33XYWhl1zAQCDLusMON0z\n" + + "NhHUDP9qK4fDIQ2WgXQ5w+HoENREuM8WCbgzBGMHXlAWCSsGAQQB2kcPAQEHQMPz\n" + + "33nLNUBDALk2PMBqhIKYoGBcehix0qqYCZFXbOIbiNUEGBYKAH0FAmMHXlACngEC\n" + + "mwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJjB15QAAoJEO8Ou9qRn4/Lk4AB\n" + + "AOTUMLOTPL9svmyoHmeVKYh4pL92/+zrsNL2Kh8BX7/FAPsE3/N3J5MB2ZEyzNSU\n" + + "84STG3Aqa+2I2u4w58CeL8eRCAAKCRAc/DQ5EG0d0QBmAQDvHR1I/B4VBqMu44wc\n" + + "w1czqqFojv1KQMETnLCfU5Q4cwD+Mt6mNoXADACcnw2P3u5u3NoFQ0v2vFSaCoBx\n" + + "VzUQrgo=\n" + + "=mKjW\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + private static final String CERT_WITH_SIG = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 8E0F C503 D081 002A 2BC8 60A1 1CFC 3439 106D 1DD1\n" + + "Comment: Marge Simpson \n" + + "\n" + + "mDMEYwdeTxYJKwYBBAHaRw8BAQdA/culAZNfjpo8NyfJv9ggwUJBY/9Ps27wRzj1\n" + + "3i5Y/amIdQQfFgoAJwUCYwdeUAkQHPw0ORBtHdEWIQSOD8UD0IEAKivIYKEc/DQ5\n" + + "EG0d0QAAQjgA/jAfrrt6eHZzPN8fyfg16W6K8dAhq/HpMefjQwQsCIFBAP9Pch2t\n" + + "OfRgNWZ90AaYI8uU2M7E8O6zlIRKku5LoDsgCLQgTWFyZ2UgU2ltcHNvbiA8bWFy\n" + + "Z2VAc2ltcHNvbi50dj6IjwQTFgoAQQUCYwdeUAkQHPw0ORBtHdEWIQSOD8UD0IEA\n" + + "KivIYKEc/DQ5EG0d0QKeAQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAAAt+AD/Y6hU\n" + + "4TQNBXjcRBIfgSgP3YhpMbAGWiNB7HC3rqBAjqAA/RY3DtU7SNFIxdcL0QBrqC+j\n" + + "64DUP5ABPvs8s2l5WwwCuDgEYwdeUBIKKwYBBAGXVQEFAQEHQB0D56rWcBDTnS4g\n" + + "9fcII1KaP1NKSeLPxwo4FEKxa6N/AwEIB4h1BBgWCgAdBQJjB15QAp4BApsMBRYC\n" + + "AwEABAsJCAcFFQoJCAsACgkQHPw0ORBtHdGDdgD8DS1IyA0j4mnKPw93BLLnWkt6\n" + + "Tc8tEc1Yy3fddhaGXXMBAIMu6ww43TM2EdQM/2orh8MhDZaBdDnD4egQ1ES4zxYJ\n" + + "uDMEYwdeUBYJKwYBBAHaRw8BAQdAw/Pfecs1QEMAuTY8wGqEgpigYFx6GLHSqpgJ\n" + + "kVds4huI1QQYFgoAfQUCYwdeUAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYK\n" + + "AAYFAmMHXlAACgkQ7w672pGfj8uTgAEA5NQws5M8v2y+bKgeZ5UpiHikv3b/7Ouw\n" + + "0vYqHwFfv8UA+wTf83cnkwHZkTLM1JTzhJMbcCpr7Yja7jDnwJ4vx5EIAAoJEBz8\n" + + "NDkQbR3RAGYBAO8dHUj8HhUGoy7jjBzDVzOqoWiO/UpAwROcsJ9TlDhzAP4y3qY2\n" + + "hcAMAJyfDY/e7m7c2gVDS/a8VJoKgHFXNRCuCg==\n" + + "=H6OY\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + private static final KeyMaterialReader reader = new KeyMaterialReader(); + + @Test + public void testOverrideExisting() throws IOException, BadDataException { + KeyMaterialMerger merger = MergeCallbacks.overrideExisting(); + KeyMaterial existing = parse(CERT); + KeyMaterial update = parse(KEY); + + assertSame(update, merger.merge(update, existing)); + } + + @Test + public void testOverrideExistingNull() throws IOException, BadDataException { + KeyMaterialMerger merger = MergeCallbacks.overrideExisting(); + KeyMaterial existing = null; + KeyMaterial update = parse(KEY); + + assertSame(update, merger.merge(update, existing)); + } + + @Test + public void testOverrideExistingWithNull() throws IOException, BadDataException { + KeyMaterialMerger merger = MergeCallbacks.overrideExisting(); + KeyMaterial existing = parse(CERT); + KeyMaterial update = null; + + assertNull(merger.merge(update, existing)); + } + + @Test + public void testMergeExistingCertWithSelf() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT); + KeyMaterial update = parse(CERT); + + assertEncodingEquals(existing, merger.merge(update, existing)); + } + + @Test + public void testMergeExistingCertWithNull() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT); + + assertEncodingEquals(existing, merger.merge(null, existing)); + } + + + @Test + public void testMergeNullWithCert() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial update = parse(CERT); + + assertEncodingEquals(update, merger.merge(update, null)); + } + + @Test + public void testMergeCertWithUpdate() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT); + KeyMaterial update = parse(CERT_WITH_SIG); + + assertEncodingEquals(update, merger.merge(update, existing)); + } + + @Test + public void testMergeUpdateWithCert() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT_WITH_SIG); + KeyMaterial update = parse(CERT); + + assertEncodingEquals(existing, merger.merge(update, existing)); + } + + @Test + public void testMergeKeyWithCert() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(KEY); + KeyMaterial update = parse(CERT); + + assertEncodingEquals(existing, merger.merge(update, existing)); + } + + @Test + public void testMergeCertWithKey() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT); + KeyMaterial update = parse(KEY); + + assertEncodingEquals(update, merger.merge(update, existing)); + } + + @Test + public void testMergeKeyWithUpdateCert() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(KEY); + KeyMaterial update = parse(CERT_WITH_SIG); + KeyMaterial expected = parse(KEY_WITH_SIG); + assertEncodingEquals(expected, merger.merge(update, existing)); + } + + @Test + public void testMergeUpdateCertWithKey() throws BadDataException, IOException { + KeyMaterialMerger merger = MergeCallbacks.mergeWithExisting(); + KeyMaterial existing = parse(CERT_WITH_SIG); + KeyMaterial update = parse(KEY); + KeyMaterial expected = parse(KEY_WITH_SIG); + + assertEncodingEquals(expected, merger.merge(update, existing)); + } + + private static KeyMaterial parse(String encoding) throws BadDataException, IOException { + return reader.read(new ByteArrayInputStream(encoding.getBytes(Charset.forName("UTF8"))), null); + } + + private static void assertEncodingEquals(KeyMaterial one, KeyMaterial two) throws IOException { + ByteArrayOutputStream oneOut = new ByteArrayOutputStream(); + ByteArrayOutputStream twoOut = new ByteArrayOutputStream(); + + Streams.pipeAll(one.getInputStream(), oneOut); + Streams.pipeAll(two.getInputStream(), twoOut); + + assertArrayEquals(oneOut.toByteArray(), twoOut.toByteArray()); + } +} From f21802523acb2c56b41d2926cfc5158a126cc9f2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 25 Aug 2022 14:26:36 +0200 Subject: [PATCH 5/5] CLI: Add test for Setup command --- .../main/java/pgp/cert_d/cli/PGPCertDCli.java | 6 +- .../java/pgp/cert_d/cli/InstantiateCLI.java | 18 +++ .../pgp/cert_d/cli/commands/SetupTest.java | 125 ++++++++++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/InstantiateCLI.java create mode 100644 pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/commands/SetupTest.java 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 501d6ca..7fbac8f 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 @@ -40,7 +40,7 @@ public class PGPCertDCli { scope = CommandLine.ScopeType.INHERIT) File baseDirectory; - private static PGPainlessCertD certificateDirectory; + static PGPainlessCertD certificateDirectory; private int executionStrategy(CommandLine.ParseResult parseResult) { try { @@ -52,6 +52,10 @@ public class PGPCertDCli { } private void initStore() throws NotAStoreException, SQLException { + if (certificateDirectory != null) { + return; + } + if (baseDirectory == null) { baseDirectory = BaseDirectoryProvider.getDefaultBaseDir(); } diff --git a/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/InstantiateCLI.java b/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/InstantiateCLI.java new file mode 100644 index 0000000..6a20ff6 --- /dev/null +++ b/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/InstantiateCLI.java @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.cert_d.cli; + +import org.pgpainless.certificate_store.PGPainlessCertD; + +public class InstantiateCLI { + + public static void resetStore() { + PGPCertDCli.certificateDirectory = null; + } + + public static void setInMemoryStore() { + PGPCertDCli.certificateDirectory = PGPainlessCertD.inMemory(); + } +} diff --git a/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/commands/SetupTest.java b/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/commands/SetupTest.java new file mode 100644 index 0000000..b4664c0 --- /dev/null +++ b/pgpainless-cert-d-cli/src/test/java/pgp/cert_d/cli/commands/SetupTest.java @@ -0,0 +1,125 @@ +// 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.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.certificate_store.PGPainlessCertD; +import org.pgpainless.key.OpenPgpFingerprint; +import org.pgpainless.key.info.KeyInfo; +import org.pgpainless.key.protection.UnlockSecretKey; +import org.pgpainless.util.Passphrase; +import pgp.cert_d.cli.InstantiateCLI; +import pgp.cert_d.cli.PGPCertDCli; +import pgp.certificate_store.certificate.Key; +import pgp.certificate_store.certificate.KeyMaterial; +import pgp.certificate_store.exception.BadDataException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SetupTest { + + private PGPainlessCertD store; + + @BeforeEach + public void setup() { + InstantiateCLI.setInMemoryStore(); + store = PGPCertDCli.getCertificateDirectory(); + } + + @AfterEach + public void teardown() { + InstantiateCLI.resetStore(); + store = null; + } + + @Test + public void testSetupGeneratesTrustRoot() + throws BadDataException, IOException { + assertNull(store.getTrustRoot()); + + PGPCertDCli.main(new String[] {"setup"}); + KeyMaterial trustRoot = store.getTrustRoot(); + assertNotNull(trustRoot); + assertTrue(trustRoot instanceof Key); + + // Check that key has no password + PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(trustRoot.getInputStream()); + assertTrue(KeyInfo.isDecrypted(secretKeys.getSecretKey())); + } + + @Test + public void testSetupWithPassword() + throws BadDataException, IOException, PGPException { + assertNull(store.getTrustRoot()); + + PGPCertDCli.main(new String[] {"setup", "--with-password", "sw0rdf1sh"}); + KeyMaterial trustRoot = store.getTrustRoot(); + assertNotNull(trustRoot); + assertTrue(trustRoot instanceof Key); + + // Check that key is encrypted + PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(trustRoot.getInputStream()); + assertTrue(KeyInfo.isEncrypted(secretKeys.getSecretKey())); + // Check that password matches + assertNotNull(UnlockSecretKey.unlockSecretKey( + secretKeys.getSecretKey(), Passphrase.fromPassword("sw0rdf1sh"))); + } + + @Test + public void testSetupImportFromStdin() + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + BadDataException, IOException { + assertNull(store.getTrustRoot()); + + PGPSecretKeyRing trustRoot = PGPainless.generateKeyRing() + .modernKeyRing("trust-root"); + OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(trustRoot); + String armored = PGPainless.asciiArmor(trustRoot); + ByteArrayInputStream trustRootIn = new ByteArrayInputStream( + armored.getBytes(Charset.forName("UTF8"))); + + InputStream originalStdin = System.in; + System.setIn(trustRootIn); + PGPCertDCli.main(new String[] {"setup", "--import-from-stdin"}); + System.setIn(originalStdin); + + KeyMaterial importedTrustRoot = store.getTrustRoot(); + assertEquals(fingerprint.toString().toLowerCase(), importedTrustRoot.getFingerprint()); + } + + @Test + public void testSetupOverridesExistingTrustRoot() + throws BadDataException, IOException { + assertNull(store.getTrustRoot()); + + PGPCertDCli.main(new String[] {"setup"}); + KeyMaterial trustRoot = store.getTrustRoot(); + assertNotNull(trustRoot); + String fingerprint = trustRoot.getFingerprint(); + + // Override trust-root by calling setup again + PGPCertDCli.main(new String[] {"setup"}); + trustRoot = store.getTrustRoot(); + assertNotNull(trustRoot); + + assertNotEquals(fingerprint, trustRoot.getFingerprint()); + } +}