mirror of
https://codeberg.org/PGPainless/cert-d-java.git
synced 2024-11-16 21:02:05 +01:00
Compare commits
No commits in common. "main" and "0.2.0" have entirely different histories.
12 changed files with 60 additions and 269 deletions
|
@ -5,13 +5,6 @@ SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
# Cert-D-Java Changelog
|
# Cert-D-Java Changelog
|
||||||
|
|
||||||
## 0.2.2
|
|
||||||
- Bump Bouncy Castle to `1.75`
|
|
||||||
- Bump `sqlite-jdbc` to `3.42.0.0`
|
|
||||||
|
|
||||||
## 0.2.1
|
|
||||||
- Throw `NoSuchElementException` when querying non-existent certificates
|
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
- `pgp-certificate-store`:
|
- `pgp-certificate-store`:
|
||||||
- Rework `Certificate`, `Key` to inherit from `KeyMaterial`
|
- Rework `Certificate`, `Key` to inherit from `KeyMaterial`
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
This repository contains a number of modules defining OpenPGP certificate storage for Java and Android applications.
|
This repository contains a number of modules defining OpenPGP certificate storage for Java and Android applications.
|
||||||
|
|
||||||
The module [pgp-certificate-store](pgp-certificate-store) defines generalized
|
The module [pgp-certificate-store](pgp-certificate-store] defines generalized
|
||||||
interfaces for OpenPGP Certificate storage.
|
interfaces for OpenPGP Certificate storage.
|
||||||
It can be used by applications and libraries such as
|
It can be used by applications and libraries such as
|
||||||
[PGPainless](https://pgpainless.org/) for certificate management.
|
[PGPainless](https://pgpainless.org/) for certificate management.
|
||||||
|
|
|
@ -32,7 +32,7 @@ dependencies {
|
||||||
testImplementation project(":pgp-cert-d-java-jdbc-sqlite-lookup")
|
testImplementation project(":pgp-cert-d-java-jdbc-sqlite-lookup")
|
||||||
|
|
||||||
testImplementation "org.bouncycastle:bcprov-jdk15to18:$bouncycastleVersion"
|
testImplementation "org.bouncycastle:bcprov-jdk15to18:$bouncycastleVersion"
|
||||||
testImplementation "org.bouncycastle:bcpg-jdk15to18:$bouncyPgVersion"
|
testImplementation "org.bouncycastle:bcpg-jdk15to18:$bouncycastleVersion"
|
||||||
}
|
}
|
||||||
|
|
||||||
animalsniffer {
|
animalsniffer {
|
||||||
|
|
|
@ -15,8 +15,6 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -48,17 +46,13 @@ public class PGPCertificateDirectory
|
||||||
if (!openPgpV4FingerprintPattern.matcher(fingerprint).matches()) {
|
if (!openPgpV4FingerprintPattern.matcher(fingerprint).matches()) {
|
||||||
throw new BadNameException();
|
throw new BadNameException();
|
||||||
}
|
}
|
||||||
Certificate certificate = backend.readByFingerprint(fingerprint);
|
return backend.readByFingerprint(fingerprint);
|
||||||
if (certificate == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
return certificate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
public Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
||||||
throws IOException, BadNameException, BadDataException {
|
throws IOException, BadNameException, BadDataException {
|
||||||
if (!Objects.equals(tag, backend.getTagForFingerprint(fingerprint))) {
|
if (tag != backend.getTagForFingerprint(fingerprint)) {
|
||||||
return getByFingerprint(fingerprint);
|
return getByFingerprint(fingerprint);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -72,13 +66,13 @@ public class PGPCertificateDirectory
|
||||||
if (keyMaterial != null) {
|
if (keyMaterial != null) {
|
||||||
return keyMaterial.asCertificate();
|
return keyMaterial.asCertificate();
|
||||||
}
|
}
|
||||||
throw new NoSuchElementException();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
public Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
||||||
throws IOException, BadNameException, BadDataException {
|
throws IOException, BadNameException, BadDataException {
|
||||||
if (!Objects.equals(tag, backend.getTagForSpecialName(specialName))) {
|
if (tag != backend.getTagForSpecialName(specialName)) {
|
||||||
return getBySpecialName(specialName);
|
return getBySpecialName(specialName);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -127,11 +121,7 @@ public class PGPCertificateDirectory
|
||||||
@Override
|
@Override
|
||||||
public KeyMaterial getTrustRoot() throws IOException, BadDataException {
|
public KeyMaterial getTrustRoot() throws IOException, BadDataException {
|
||||||
try {
|
try {
|
||||||
KeyMaterial keyMaterial = backend.readBySpecialName(SpecialNames.TRUST_ROOT);
|
return backend.readBySpecialName(SpecialNames.TRUST_ROOT);
|
||||||
if (keyMaterial == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
return keyMaterial;
|
|
||||||
} catch (BadNameException e) {
|
} catch (BadNameException e) {
|
||||||
throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is implementation MUST");
|
throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is implementation MUST");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import pgp.certificate_store.exception.BadNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for a read-only OpenPGP certificate directory.
|
* Interface for a read-only OpenPGP certificate directory.
|
||||||
|
@ -20,12 +19,12 @@ public interface ReadOnlyPGPCertificateDirectory {
|
||||||
/**
|
/**
|
||||||
* Get the trust-root certificate. This is a certificate which is stored under the special name
|
* Get the trust-root certificate. This is a certificate which is stored under the special name
|
||||||
* <pre>trust-root</pre>.
|
* <pre>trust-root</pre>.
|
||||||
|
* If no such certificate is found, <pre>null</pre> is returned.
|
||||||
*
|
*
|
||||||
* @return trust-root certificate
|
* @return trust-root certificate
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getTrustRootCertificate()
|
Certificate getTrustRootCertificate()
|
||||||
throws IOException, BadDataException;
|
throws IOException, BadDataException;
|
||||||
|
@ -37,25 +36,24 @@ public interface ReadOnlyPGPCertificateDirectory {
|
||||||
* Otherwise. the changed certificate is returned.
|
* Otherwise. the changed certificate is returned.
|
||||||
*
|
*
|
||||||
* @param tag tag
|
* @param tag tag
|
||||||
* @return changed certificate, or null if the certificate is unchanged.
|
* @return changed certificate, or null if the certificate is unchanged or not found.
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getTrustRootCertificateIfChanged(long tag)
|
Certificate getTrustRootCertificateIfChanged(long tag)
|
||||||
throws IOException, BadDataException;
|
throws IOException, BadDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the certificate identified by the given fingerprint.
|
* Get the certificate identified by the given fingerprint.
|
||||||
|
* If no such certificate is found, return <pre>null</pre>.
|
||||||
*
|
*
|
||||||
* @param fingerprint lower-case fingerprint of the certificate
|
* @param fingerprint lower-case fingerprint of the certificate
|
||||||
* @return certificate
|
* @return certificate or null if no such certificate has been found
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadNameException if the fingerprint is malformed
|
* @throws BadNameException if the fingerprint is malformed
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getByFingerprint(String fingerprint)
|
Certificate getByFingerprint(String fingerprint)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
@ -68,26 +66,25 @@ public interface ReadOnlyPGPCertificateDirectory {
|
||||||
*
|
*
|
||||||
* @param fingerprint lower-case fingerprint of the certificate
|
* @param fingerprint lower-case fingerprint of the certificate
|
||||||
* @param tag tag
|
* @param tag tag
|
||||||
* @return certificate or null if the certificate has not been changed
|
* @return certificate or null if the certificate has not been changed or has not been found
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadNameException if the fingerprint is malformed
|
* @throws BadNameException if the fingerprint is malformed
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the certificate identified by the given special name.
|
* Get the certificate identified by the given special name.
|
||||||
|
* If no such certificate is found, <pre>null</pre> is returned.
|
||||||
*
|
*
|
||||||
* @param specialName special name
|
* @param specialName special name
|
||||||
* @return certificate
|
* @return certificate or null
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadNameException if the special name is not known
|
* @throws BadNameException if the special name is not known
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getBySpecialName(String specialName)
|
Certificate getBySpecialName(String specialName)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
@ -105,7 +102,6 @@ public interface ReadOnlyPGPCertificateDirectory {
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadNameException if the special name is not known
|
* @throws BadNameException if the special name is not known
|
||||||
* @throws BadDataException if the certificate contains bad data
|
* @throws BadDataException if the certificate contains bad data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
|
@ -33,7 +33,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -349,7 +348,7 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
|
|
||||||
private Long getTag(File file) throws IOException {
|
private Long getTag(File file) throws IOException {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
throw new NoSuchElementException();
|
throw new IllegalArgumentException("File MUST exist.");
|
||||||
}
|
}
|
||||||
Path path = file.toPath();
|
Path path = file.toPath();
|
||||||
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class BaseDirectoryProviderTest {
|
||||||
public void testGetDefaultBaseDir_Linux() {
|
public void testGetDefaultBaseDir_Linux() {
|
||||||
assumeTrue(System.getProperty("os.name").equalsIgnoreCase("linux"));
|
assumeTrue(System.getProperty("os.name").equalsIgnoreCase("linux"));
|
||||||
File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("linux");
|
File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("linux");
|
||||||
assertTrue(baseDir.getAbsolutePath().endsWith("pgp.cert.d"));
|
assertTrue(baseDir.getAbsolutePath().endsWith("/.local/share/pgp.cert.d"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
// 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.Test;
|
|
||||||
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;
|
|
||||||
import pgp.cert_d.subkey_lookup.SubkeyLookup;
|
|
||||||
import pgp.certificate_store.certificate.Certificate;
|
|
||||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
|
||||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
|
||||||
import pgp.certificate_store.exception.BadDataException;
|
|
||||||
import pgp.certificate_store.exception.BadNameException;
|
|
||||||
import pgp.certificate_store.exception.NotAStoreException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
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.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
|
||||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
|
||||||
|
|
||||||
public class FileBasedPGPCertificateDirectoryTest {
|
|
||||||
|
|
||||||
private static final KeyMaterialMerger merger = new TestKeyMaterialMerger();
|
|
||||||
@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;
|
|
||||||
|
|
||||||
Thread.sleep(10);
|
|
||||||
// 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.write("\n".getBytes());
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fileBasedStoreInWriteProtectedAreaThrows() {
|
|
||||||
File root = new File("/");
|
|
||||||
assumeTrue(root.exists(), "This test only runs on unix-like systems");
|
|
||||||
File baseDirectory = new File(root, "pgp.cert.d");
|
|
||||||
assumeFalse(baseDirectory.mkdirs(), "This test assumes that we cannot create dirs in /");
|
|
||||||
|
|
||||||
KeyMaterialReaderBackend reader = new TestKeyMaterialReaderBackend();
|
|
||||||
SubkeyLookup lookup = new InMemorySubkeyLookup();
|
|
||||||
assertThrows(NotAStoreException.class, () -> PGPCertificateDirectories.fileBasedCertificateDirectory(
|
|
||||||
reader, baseDirectory, lookup));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fileBasedStoreOnFileThrows()
|
|
||||||
throws IOException {
|
|
||||||
File tempDir = Files.createTempDirectory("containsAFile").toFile();
|
|
||||||
tempDir.deleteOnExit();
|
|
||||||
File baseDir = new File(tempDir, "pgp.cert.d");
|
|
||||||
baseDir.createNewFile(); // this is a file, not a dir
|
|
||||||
|
|
||||||
KeyMaterialReaderBackend reader = new TestKeyMaterialReaderBackend();
|
|
||||||
SubkeyLookup lookup = new InMemorySubkeyLookup();
|
|
||||||
assertThrows(NotAStoreException.class, () -> PGPCertificateDirectories.fileBasedCertificateDirectory(
|
|
||||||
reader, baseDir, lookup));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCertificateStoredUnderWrongFingerprintThrowsBadData()
|
|
||||||
throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
|
|
||||||
File tempDir = Files.createTempDirectory("wrong-fingerprint").toFile();
|
|
||||||
tempDir.deleteOnExit();
|
|
||||||
PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
|
|
||||||
new TestKeyMaterialReaderBackend(),
|
|
||||||
tempDir,
|
|
||||||
new InMemorySubkeyLookup());
|
|
||||||
FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
|
|
||||||
new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
|
|
||||||
|
|
||||||
// Insert Rons certificate
|
|
||||||
directory.insert(TestKeys.getRonCert(), merger);
|
|
||||||
|
|
||||||
// Copy Rons cert to Cedrics cert file
|
|
||||||
File ronCert = resolver.getCertFileByFingerprint(TestKeys.RON_FP);
|
|
||||||
FileInputStream inputStream = new FileInputStream(ronCert);
|
|
||||||
File cedricCert = resolver.getCertFileByFingerprint(TestKeys.CEDRIC_FP);
|
|
||||||
cedricCert.getParentFile().mkdirs();
|
|
||||||
cedricCert.createNewFile();
|
|
||||||
FileOutputStream outputStream = new FileOutputStream(cedricCert);
|
|
||||||
Streams.pipeAll(inputStream, outputStream);
|
|
||||||
inputStream.close();
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
// Reading cedrics cert will fail, as it has Rons fingerprint
|
|
||||||
assertThrows(BadDataException.class, () -> directory.getByFingerprint(TestKeys.CEDRIC_FP));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,9 +6,11 @@ package pgp.cert_d;
|
||||||
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.junit.jupiter.api.Named;
|
import org.junit.jupiter.api.Named;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
|
||||||
import pgp.cert_d.dummy.TestKeyMaterialMerger;
|
import pgp.cert_d.dummy.TestKeyMaterialMerger;
|
||||||
import pgp.cert_d.dummy.TestKeyMaterialReaderBackend;
|
import pgp.cert_d.dummy.TestKeyMaterialReaderBackend;
|
||||||
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
|
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
|
||||||
|
@ -22,13 +24,13 @@ import pgp.certificate_store.exception.NotAStoreException;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -66,55 +68,6 @@ public class PGPCertificateDirectoryTest {
|
||||||
Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased)));
|
Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentCertByFingerprintThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getByFingerprint("0000000000000000000000000000000000000000"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentCertByFingerprintIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getByFingerprintIfChanged("0000000000000000000000000000000000000000", 12));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentCertBySpecialNameThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getBySpecialName(SpecialNames.TRUST_ROOT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentCertBySpecialNameIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, 12));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentTrustRootThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getTrustRoot());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentTrustRootIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getTrustRootCertificateIfChanged(12));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void getNonExistentTrustRootCertificateThrowsNoSuchElementException(PGPCertificateDirectory directory) {
|
|
||||||
assertThrows(NoSuchElementException.class, () ->
|
|
||||||
directory.getTrustRootCertificate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory)
|
public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory)
|
||||||
|
@ -177,7 +130,7 @@ public class PGPCertificateDirectoryTest {
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void testInsertAndGetSingleCert(PGPCertificateDirectory directory)
|
public void testInsertAndGetSingleCert(PGPCertificateDirectory directory)
|
||||||
throws BadDataException, IOException, InterruptedException, BadNameException {
|
throws BadDataException, IOException, InterruptedException, BadNameException {
|
||||||
assertThrows(NoSuchElementException.class, () -> directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
|
assertNull(directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
|
||||||
|
|
||||||
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
|
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
|
||||||
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
|
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
|
||||||
|
@ -195,7 +148,7 @@ public class PGPCertificateDirectoryTest {
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory)
|
public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory)
|
||||||
throws BadDataException, IOException, InterruptedException {
|
throws BadDataException, IOException, InterruptedException {
|
||||||
assertThrows(NoSuchElementException.class, () -> directory.getTrustRoot());
|
assertNull(directory.getTrustRoot());
|
||||||
|
|
||||||
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
||||||
TestKeys.getHarryKey(), merger);
|
TestKeys.getHarryKey(), merger);
|
||||||
|
@ -235,7 +188,6 @@ public class PGPCertificateDirectoryTest {
|
||||||
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
|
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
|
||||||
|
|
||||||
Long oldTag = tag;
|
Long oldTag = tag;
|
||||||
Thread.sleep(10);
|
|
||||||
// "update" key
|
// "update" key
|
||||||
trustRootMaterial = directory.insertTrustRoot(
|
trustRootMaterial = directory.insertTrustRoot(
|
||||||
TestKeys.getHarryKey(), merger);
|
TestKeys.getHarryKey(), merger);
|
||||||
|
@ -270,42 +222,38 @@ public class PGPCertificateDirectoryTest {
|
||||||
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
|
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@Test
|
||||||
@MethodSource("provideTestSubjects")
|
public void testFileBasedCertificateDirectoryTagChangesWhenFileChanges() throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
|
||||||
public void testOverwriteTrustRoot(PGPCertificateDirectory directory)
|
File tempDir = Files.createTempDirectory("file-based-changes").toFile();
|
||||||
throws BadDataException, IOException, InterruptedException {
|
tempDir.deleteOnExit();
|
||||||
directory.insertTrustRoot(TestKeys.getHarryKey(), merger);
|
PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
|
||||||
assertEquals(HARRY_FP, directory.getTrustRoot().getFingerprint());
|
new TestKeyMaterialReaderBackend(),
|
||||||
assertTrue(directory.getTrustRoot() instanceof Key);
|
tempDir,
|
||||||
|
new InMemorySubkeyLookup());
|
||||||
|
FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
|
||||||
|
new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
|
||||||
|
|
||||||
directory.insertTrustRoot(TestKeys.getCedricCert(), merger);
|
// Insert certificate
|
||||||
assertEquals(CEDRIC_FP, directory.getTrustRoot().getFingerprint());
|
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
|
||||||
assertTrue(directory.getTrustRoot() instanceof Certificate);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void testOverwriteSpecialName(PGPCertificateDirectory directory)
|
|
||||||
throws BadDataException, IOException, InterruptedException, BadNameException {
|
|
||||||
directory.insertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getRonCert(), merger);
|
|
||||||
KeyMaterial bySpecialName = directory.getBySpecialName(SpecialNames.TRUST_ROOT);
|
|
||||||
assertEquals(RON_FP, bySpecialName.getFingerprint());
|
|
||||||
|
|
||||||
directory.insertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
|
|
||||||
assertEquals(HARRY_FP, directory.getBySpecialName(SpecialNames.TRUST_ROOT).getFingerprint());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestSubjects")
|
|
||||||
public void testOverwriteByFingerprint(PGPCertificateDirectory directory)
|
|
||||||
throws BadDataException, IOException, InterruptedException, BadNameException {
|
|
||||||
directory.insert(TestKeys.getRonCert(), merger);
|
|
||||||
Certificate extracted = directory.getByFingerprint(RON_FP);
|
|
||||||
assertEquals(RON_FP, extracted.getFingerprint());
|
|
||||||
|
|
||||||
directory.insert(TestKeys.getRonCert(), merger);
|
|
||||||
extracted = directory.getByFingerprint(RON_FP);
|
|
||||||
assertEquals(RON_FP, extracted.getFingerprint());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ import pgp.certificate_store.exception.BadNameException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
@ -53,7 +52,7 @@ public class PGPCertificateStoreAdapterTest {
|
||||||
@Test
|
@Test
|
||||||
public void testInsertGetCertificate()
|
public void testInsertGetCertificate()
|
||||||
throws BadDataException, IOException, InterruptedException, BadNameException {
|
throws BadDataException, IOException, InterruptedException, BadNameException {
|
||||||
assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(TestKeys.CEDRIC_FP));
|
assertNull(adapter.getCertificate(TestKeys.CEDRIC_FP));
|
||||||
assertFalse(adapter.getCertificates().hasNext());
|
assertFalse(adapter.getCertificates().hasNext());
|
||||||
|
|
||||||
Certificate certificate = adapter.insertCertificate(TestKeys.getCedricCert(), merger);
|
Certificate certificate = adapter.insertCertificate(TestKeys.getCedricCert(), merger);
|
||||||
|
@ -71,7 +70,7 @@ public class PGPCertificateStoreAdapterTest {
|
||||||
@Test
|
@Test
|
||||||
public void testInsertGetTrustRoot()
|
public void testInsertGetTrustRoot()
|
||||||
throws BadDataException, BadNameException, IOException, InterruptedException {
|
throws BadDataException, BadNameException, IOException, InterruptedException {
|
||||||
assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(SpecialNames.TRUST_ROOT));
|
assertNull(adapter.getCertificate(SpecialNames.TRUST_ROOT));
|
||||||
|
|
||||||
Certificate certificate = adapter.insertCertificateBySpecialName(
|
Certificate certificate = adapter.insertCertificateBySpecialName(
|
||||||
SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
|
SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import pgp.certificate_store.exception.BadNameException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for an OpenPGP certificate (public key) store.
|
* Interface for an OpenPGP certificate (public key) store.
|
||||||
|
@ -21,6 +20,7 @@ public interface PGPCertificateStore {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the certificate that matches the given identifier.
|
* Return the certificate that matches the given identifier.
|
||||||
|
* If no matching certificate can be found, return null.
|
||||||
*
|
*
|
||||||
* @param identifier identifier for a certificate.
|
* @param identifier identifier for a certificate.
|
||||||
* @return certificate or null
|
* @return certificate or null
|
||||||
|
@ -28,7 +28,6 @@ public interface PGPCertificateStore {
|
||||||
* @throws IOException in case of an IO-error
|
* @throws IOException in case of an IO-error
|
||||||
* @throws BadNameException if the identifier is invalid
|
* @throws BadNameException if the identifier is invalid
|
||||||
* @throws BadDataException if the certificate file contains invalid data
|
* @throws BadDataException if the certificate file contains invalid data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getCertificate(String identifier)
|
Certificate getCertificate(String identifier)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
@ -46,7 +45,6 @@ public interface PGPCertificateStore {
|
||||||
* @throws IOException in case of an IO-error
|
* @throws IOException in case of an IO-error
|
||||||
* @throws BadNameException if the identifier is invalid
|
* @throws BadNameException if the identifier is invalid
|
||||||
* @throws BadDataException if the certificate file contains invalid data
|
* @throws BadDataException if the certificate file contains invalid data
|
||||||
* @throws NoSuchElementException if no such certificate is found
|
|
||||||
*/
|
*/
|
||||||
Certificate getCertificateIfChanged(String identifier, Long tag)
|
Certificate getCertificateIfChanged(String identifier, Long tag)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
|
@ -4,16 +4,15 @@
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '0.2.3'
|
shortVersion = '0.2.0'
|
||||||
isSnapshot = true
|
isSnapshot = false
|
||||||
minAndroidSdk = 26
|
minAndroidSdk = 26
|
||||||
animalsnifferSignatureVersion = "$minAndroidSdk:8.0.0_r2"
|
animalsnifferSignatureVersion = "$minAndroidSdk:8.0.0_r2"
|
||||||
javaSourceCompatibility = 1.8
|
javaSourceCompatibility = 1.8
|
||||||
bouncycastleVersion = '1.75'
|
bouncycastleVersion = '1.71'
|
||||||
bouncyPgVersion = "$bouncycastleVersion"
|
|
||||||
slf4jVersion = '1.7.36'
|
slf4jVersion = '1.7.36'
|
||||||
logbackVersion = '1.2.11'
|
logbackVersion = '1.2.11'
|
||||||
junitVersion = '5.8.2'
|
junitVersion = '5.8.2'
|
||||||
sqliteJdbcVersion = '3.42.0.0'
|
sqliteJdbcVersion = '3.36.0.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue