diff --git a/pgp-cert-d-java/src/main/java/pgp/cert_d/InMemorySubkeyLookup.java b/pgp-cert-d-java/src/main/java/pgp/cert_d/InMemorySubkeyLookup.java index e8e79698..c7fa2faf 100644 --- a/pgp-cert-d-java/src/main/java/pgp/cert_d/InMemorySubkeyLookup.java +++ b/pgp-cert-d-java/src/main/java/pgp/cert_d/InMemorySubkeyLookup.java @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package pgp.cert_d; import java.util.HashMap; diff --git a/pgp-cert-d-java/src/main/java/pgp/cert_d/SharedPGPCertificateDirectoryImpl.java b/pgp-cert-d-java/src/main/java/pgp/cert_d/SharedPGPCertificateDirectoryImpl.java index 05a25fdc..0cd835aa 100644 --- a/pgp-cert-d-java/src/main/java/pgp/cert_d/SharedPGPCertificateDirectoryImpl.java +++ b/pgp-cert-d-java/src/main/java/pgp/cert_d/SharedPGPCertificateDirectoryImpl.java @@ -11,16 +11,17 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; -import java.util.Queue; -import java.util.concurrent.SynchronousQueue; +import java.util.List; import pgp.cert_d.exception.BadDataException; import pgp.cert_d.exception.BadNameException; import pgp.cert_d.exception.NotAStoreException; import pgp.certificate_store.Certificate; -import pgp.certificate_store.MergeCallback; import pgp.certificate_store.CertificateReaderBackend; +import pgp.certificate_store.MergeCallback; public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDirectory { @@ -237,7 +238,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi public Iterator items() { return new Iterator() { - private final Queue> certificateQueue = new SynchronousQueue<>(); + private final List> certificateQueue = Collections.synchronizedList(new ArrayList<>()); // Constructor... wtf. { @@ -283,7 +284,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi @Override public Certificate next() { try { - return certificateQueue.poll().get(); + return certificateQueue.remove(0).get(); } catch (BadDataException e) { throw new AssertionError("Could not retrieve item: " + e.getMessage()); } diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java index 9aaed423..357ba3ff 100644 --- a/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java +++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import java.io.File; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -19,4 +20,24 @@ public class BaseDirectoryProviderTest { File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("linux"); assertTrue(baseDir.getAbsolutePath().endsWith("/.local/share/pgp.cert.d")); } + + @Test + public void testGetDefaultBaseDir_Windows() { + assumeTrue(System.getProperty("os.name").toLowerCase().contains("win")); + File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("Windows"); + assertTrue(baseDir.getAbsolutePath().endsWith("\\Roaming\\pgp.cert.d")); + } + + @Test + public void testGetDefaultBaseDir_Mac() { + assumeTrue(System.getProperty("os.name").toLowerCase().contains("mac")); + File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("Mac"); + assertTrue(baseDir.getAbsolutePath().endsWith("/Library/Application Support/pgp.cert.d")); + } + + @Test + public void testGetDefaultBaseDirNotNull() { + File baseDir = BaseDirectoryProvider.getDefaultBaseDir(); + assertNotNull(baseDir); + } } diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/FilenameResolverTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/FilenameResolverTest.java index 8f00c47a..600b9027 100644 --- a/pgp-cert-d-java/src/test/java/pgp/cert_d/FilenameResolverTest.java +++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/FilenameResolverTest.java @@ -34,7 +34,7 @@ public class FilenameResolverTest { File subDir = new File(baseDir, "d1"); File expected = new File(subDir, "a66e1a23b182c9980f788cfbfcc82a015e7330"); - assertEquals(resolver.getCertFileByFingerprint(fingerprint).getAbsolutePath(), expected.getAbsolutePath()); + assertEquals(expected.getAbsolutePath(), resolver.getCertFileByFingerprint(fingerprint).getAbsolutePath()); } @Test @@ -44,7 +44,7 @@ public class FilenameResolverTest { File subDir = new File(baseDir, "eb"); File expected = new File(subDir, "85bb5fa33a75e15e944e63f231550c4f47e38e"); - assertEquals(resolver.getCertFileByFingerprint(fingerprint).getAbsolutePath(), expected.getAbsolutePath()); + assertEquals(expected.getAbsolutePath(), resolver.getCertFileByFingerprint(fingerprint).getAbsolutePath()); } @Test @@ -63,4 +63,18 @@ public class FilenameResolverTest { public void testGetFileForNullFingerprint() { assertThrows(NullPointerException.class, () -> resolver.getCertFileByFingerprint(null)); } + + @Test + public void testGetFileForSpecialName() throws BadNameException { + String specialName = "trust-root"; + File expected = new File(baseDir, "trust-root"); + + assertEquals(expected, resolver.getCertFileBySpecialName(specialName)); + } + + @Test + public void testGetFileForInvalidSpecialName() { + String invalidSpecialName = "invalid"; + assertThrows(BadNameException.class, () -> resolver.getCertFileBySpecialName(invalidSpecialName)); + } } 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 d17f5419..cff690c7 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 @@ -4,27 +4,39 @@ package org.pgpainless.cert_d; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; 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.Assumptions.assumeTrue; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; import java.util.stream.Stream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.util.io.Streams; import org.junit.jupiter.params.ParameterizedTest; 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.key.OpenPgpFingerprint; +import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.type.KeyType; +import org.pgpainless.key.generation.type.eddsa.EdDSACurve; import pgp.cert_d.CachingSharedPGPCertificateDirectoryWrapper; import pgp.cert_d.FileLockingMechanism; import pgp.cert_d.SharedPGPCertificateDirectory; @@ -84,6 +96,35 @@ public class SharedPGPCertificateDirectoryTest { assertNotNull(directory.tryInsert(certIn, dummyMerge)); } + @ParameterizedTest + @MethodSource("provideTestSubjects") + public void simpleInsertGetBySpecialName(SharedPGPCertificateDirectory directory) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, + BadDataException, InterruptedException, BadNameException { + PGPSecretKeyRing key = PGPainless.buildKeyRing() + .addUserId("trust-root") + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) + .build(); + PGPPublicKeyRing trustRoot = PGPainless.extractCertificate(key); + OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(trustRoot); + ByteArrayInputStream certIn = new ByteArrayInputStream(trustRoot.getEncoded()); + + // standard case: get() is null + assertNull(directory.getBySpecialName("trust-root")); + + // insert and check returned certs fingerprint + Certificate certificate = directory.insertWithSpecialName("trust-root", certIn, dummyMerge); + assertEquals(fingerprint.toString().toLowerCase(), certificate.getFingerprint()); + + // getIfChanged + assertNull(directory.getBySpecialNameIfChanged("trust-root", certificate.getTag())); + assertNotNull(directory.getBySpecialNameIfChanged("trust-root", "invalidTag")); + + // tryInsert + certIn = new ByteArrayInputStream(trustRoot.getEncoded()); + assertNotNull(directory.tryInsertWithSpecialName("trust-root", certIn, dummyMerge)); + } + @ParameterizedTest @MethodSource("provideTestSubjects") public void tryInsertFailsWithLockedStore(SharedPGPCertificateDirectory directory) @@ -101,4 +142,53 @@ public class SharedPGPCertificateDirectoryTest { directory.getLock().releaseDirectory(); assertNotNull(directory.tryInsert(certIn, dummyMerge)); } + + @ParameterizedTest + @MethodSource("provideTestSubjects") + public void testGetItemsAndFingerprints(SharedPGPCertificateDirectory directory) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, + BadDataException, InterruptedException, BadNameException { + + PGPSecretKeyRing trustRootKey = PGPainless.generateKeyRing().modernKeyRing("Alice", null); + PGPPublicKeyRing trustRootCert = PGPainless.extractCertificate(trustRootKey); + OpenPgpFingerprint trustRootFingerprint = OpenPgpFingerprint.of(trustRootCert); + ByteArrayInputStream trustRootCertIn = new ByteArrayInputStream(trustRootCert.getEncoded()); + directory.insertWithSpecialName("trust-root", trustRootCertIn, dummyMerge); + + final int certificateCount = 3; + Map certificateMap = new HashMap<>(); + for (int i = 0; i < certificateCount; i++) { + PGPSecretKeyRing key = PGPainless.generateKeyRing().modernKeyRing("Alice", null); + PGPPublicKeyRing cert = PGPainless.extractCertificate(key); + OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(cert); + certificateMap.put(fingerprint.toString().toLowerCase(), cert); + + ByteArrayInputStream certIn = new ByteArrayInputStream(cert.getEncoded()); + directory.insert(certIn, dummyMerge); + } + + Iterator certificates = directory.items(); + int count = 0; + while (certificates.hasNext()) { + count++; + Certificate certificate = certificates.next(); + String fingerprint = certificate.getFingerprint(); + assertNotNull(certificateMap.get(fingerprint)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.pipeAll(certificate.getInputStream(), out); + assertArrayEquals(certificateMap.get(fingerprint).getEncoded(), out.toByteArray()); + } + + assertEquals(certificateCount, count); + + Iterator fingerprints = directory.fingerprints(); + Set fingerprintSet = new HashSet<>(); + while (fingerprints.hasNext()) { + String fingerprint = fingerprints.next(); + fingerprintSet.add(fingerprint); + assertNotNull(certificateMap.get(fingerprint)); + } + + assertEquals(certificateCount, fingerprintSet.size()); + } }