cert-d-java/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateDirectoryTest.java

226 lines
9.9 KiB
Java
Raw Normal View History

2022-08-23 15:19:01 +02:00
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package pgp.cert_d;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
import pgp.cert_d.dummy.TestKeyMaterialMerger;
import pgp.cert_d.dummy.TestKeyMaterialReaderBackend;
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
2022-08-23 15:19:01 +02:00
import pgp.certificate_store.certificate.Certificate;
import pgp.certificate_store.certificate.Key;
import pgp.certificate_store.certificate.KeyMaterial;
import pgp.certificate_store.certificate.KeyMaterialMerger;
2022-08-23 15:19:01 +02:00
import pgp.certificate_store.exception.BadDataException;
import pgp.certificate_store.exception.BadNameException;
import pgp.certificate_store.exception.NotAStoreException;
2022-08-23 15:19:01 +02:00
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
2022-08-23 15:19:01 +02:00
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashSet;
2022-08-23 15:19:01 +02:00
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Stream;
2022-08-23 15:19:01 +02:00
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
2022-08-23 15:19:01 +02:00
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static pgp.cert_d.TestKeys.CEDRIC_FP;
import static pgp.cert_d.TestKeys.HARRY_FP;
import static pgp.cert_d.TestKeys.RON_FP;
2022-08-23 15:19:01 +02:00
public class PGPCertificateDirectoryTest {
private static final KeyMaterialMerger merger = new TestKeyMaterialMerger();
private static Stream<Arguments> provideTestSubjects()
throws IOException, NotAStoreException {
PGPCertificateDirectory inMemory = PGPCertificateDirectories.inMemoryCertificateDirectory(
2022-08-23 15:19:01 +02:00
new TestKeyMaterialReaderBackend());
File tempDir = Files.createTempDirectory("pgp-cert-d-test").toFile();
tempDir.deleteOnExit();
PGPCertificateDirectory fileBased = PGPCertificateDirectories.fileBasedCertificateDirectory(
new TestKeyMaterialReaderBackend(),
tempDir,
new InMemorySubkeyLookup());
return Stream.of(
Arguments.of(Named.of("InMemoryCertificateDirectory", inMemory)),
Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased)));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory)
throws IOException, InterruptedException, BadDataException {
2022-08-23 15:19:01 +02:00
// Manually lock the dir
assertFalse(directory.backend.getLock().isLocked());
directory.backend.getLock().lockDirectory();
assertTrue(directory.backend.getLock().isLocked());
assertFalse(directory.backend.getLock().tryLockDirectory());
Certificate inserted = directory.tryInsert(TestKeys.getCedricCert(), merger);
2022-08-23 15:19:01 +02:00
assertNull(inserted);
directory.backend.getLock().releaseDirectory();
inserted = directory.tryInsert(TestKeys.getCedricCert(), merger);
2022-08-23 15:19:01 +02:00
assertNotNull(inserted);
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getByInvalidNameFails(PGPCertificateDirectory directory) {
2022-08-23 15:19:01 +02:00
assertThrows(BadNameException.class, () -> directory.getBySpecialName("invalid"));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void testInsertAndGetSingleCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException, BadNameException {
2022-08-23 15:19:01 +02:00
assertNull(directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
2022-08-23 15:19:01 +02:00
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
Certificate get = directory.getByFingerprint(CEDRIC_FP);
assertEquals(CEDRIC_FP, get.getFingerprint(), "Fingerprint of retrieved cert MUST match");
byte[] expected = TestKeys.CEDRIC_CERT.getBytes(Charset.forName("UTF8"));
2022-08-23 15:19:01 +02:00
ByteArrayOutputStream actual = new ByteArrayOutputStream();
Streams.pipeAll(get.getInputStream(), actual);
assertArrayEquals(expected, actual.toByteArray(), "InputStream of cert MUST match what we gave in");
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException {
2022-08-23 15:19:01 +02:00
assertNull(directory.getTrustRoot());
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger);
2022-08-23 15:19:01 +02:00
assertNotNull(trustRootMaterial);
assertTrue(trustRootMaterial instanceof Key);
assertEquals(HARRY_FP, trustRootMaterial.getFingerprint());
Key trustRoot = (Key) directory.getTrustRoot();
assertEquals(HARRY_FP, trustRoot.getFingerprint());
Certificate trustRootCert = directory.getTrustRootCertificate();
assertEquals(HARRY_FP, trustRootCert.getFingerprint());
directory.tryInsert(TestKeys.getRonCert(), merger);
directory.insert(TestKeys.getCedricCert(), merger);
2022-08-23 15:19:01 +02:00
Set<String> expected = new HashSet<>(Arrays.asList(RON_FP, CEDRIC_FP));
Set<String> actual = new HashSet<>();
2022-08-23 15:19:01 +02:00
Iterator<String> fingerprints = directory.fingerprints();
actual.add(fingerprints.next());
actual.add(fingerprints.next());
2022-08-23 15:19:01 +02:00
assertFalse(fingerprints.hasNext());
assertEquals(expected, actual);
2022-08-23 15:19:01 +02:00
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void testGetTrustRootIfChanged(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException {
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger);
assertNotNull(trustRootMaterial.getTag());
Long tag = trustRootMaterial.getTag();
assertNull(directory.getTrustRootCertificateIfChanged(tag));
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
Long oldTag = tag;
// "update" key
trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger);
tag = trustRootMaterial.getTag();
assertNotEquals(oldTag, tag);
assertNotNull(directory.getTrustRootCertificateIfChanged(oldTag));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void testGetBySpecialNameIfChanged(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException, BadNameException {
KeyMaterial specialName = directory.insertWithSpecialName(SpecialNames.TRUST_ROOT,
TestKeys.getHarryKey(), merger);
assertNotNull(specialName.getTag());
Long tag = specialName.getTag();
assertNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag));
assertNotNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag + 1));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void testGetByFingerprintIfChanged(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException, BadNameException {
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
Long tag = certificate.getTag();
assertNotNull(tag);
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
}
@Test
public void testFileBasedCertificateDirectoryTagChangesWhenFileChanges() throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
File tempDir = Files.createTempDirectory("file-based-changes").toFile();
tempDir.deleteOnExit();
PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
new TestKeyMaterialReaderBackend(),
tempDir,
new InMemorySubkeyLookup());
FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
// Insert certificate
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
Long tag = certificate.getTag();
assertNotNull(tag);
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
Long oldTag = tag;
// Change the file on disk directly, this invalidates the tag due to changed modification date
File certFile = resolver.getCertFileByFingerprint(certificate.getFingerprint());
FileOutputStream fileOut = new FileOutputStream(certFile);
Streams.pipeAll(certificate.getInputStream(), fileOut);
fileOut.close();
// Old invalidated tag indicates a change, so the modified certificate is returned
certificate = directory.getByFingerprintIfChanged(certificate.getFingerprint(), oldTag);
assertNotNull(certificate);
// new tag is valid
tag = certificate.getTag();
assertNotEquals(oldTag, tag);
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
}
2022-08-23 15:19:01 +02:00
}