Compare commits
8 Commits
f34c6d7735
...
eab31b8c12
Author | SHA1 | Date |
---|---|---|
Paul Schaub | eab31b8c12 | |
Paul Schaub | 65a283c80b | |
Paul Schaub | 682b3d58fa | |
Paul Schaub | 142d1f6b16 | |
Paul Schaub | da39d41df3 | |
Paul Schaub | 3ec7b082be | |
Paul Schaub | 2758c3efb8 | |
Paul Schaub | 4ce9f46846 |
|
@ -5,15 +5,18 @@ SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
# Cert-D-Java Changelog
|
# Cert-D-Java Changelog
|
||||||
|
|
||||||
## 0.1.2-SNAPSHOT
|
## 0.2.0
|
||||||
- `pgp-certificate-store`:
|
- `pgp-certificate-store`:
|
||||||
- Rework `Certificate`, `Key` to inherit from `KeyMaterial`
|
- Rework `Certificate`, `Key` to inherit from `KeyMaterial`
|
||||||
- Rename `CertificateReaderBackend` to `KeyMaterialReaderBackend`
|
- Rename `CertificateReaderBackend` to `KeyMaterialReaderBackend`
|
||||||
- Rename `CertificateMerger` to `KeyMaterialMerger`
|
- Rename `CertificateMerger` to `KeyMaterialMerger`
|
||||||
- Rework `PGPCertificateStore` class
|
- Rework `PGPCertificateStore` class
|
||||||
- `pgp-cert-d-java`:
|
- `pgp-cert-d-java`:
|
||||||
|
- Increase minimum Android API level to 26
|
||||||
|
- Add `PGPCertificateDirectories` factory class
|
||||||
- Rework `PGPCertificateDirectory` class by separating out backend logic
|
- Rework `PGPCertificateDirectory` class by separating out backend logic
|
||||||
- Split interface into `ReadOnlyPGPCertificateDirectory` and `WritingPGPCertificateDirectory`
|
- Split interface into `ReadOnlyPGPCertificateDirectory` and `WritingPGPCertificateDirectory`
|
||||||
|
- `FileBasedCertificateDirectoryBackend`: Calculate tag based on file attributes (inode)
|
||||||
- `pgp-cert-d-java-jdbc-sqlite-lookup`:
|
- `pgp-cert-d-java-jdbc-sqlite-lookup`:
|
||||||
- Add `DatabaseSubkeyLookupFactory`
|
- Add `DatabaseSubkeyLookupFactory`
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,20 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link PGPCertificateDirectory.Backend} which stores certificates in a directory structure.
|
||||||
|
*
|
||||||
|
* @see <a href="https://sequoia-pgp.gitlab.io/pgp-cert-d/#name-implementation">Shared PGP Certificate Directory</a>
|
||||||
|
*/
|
||||||
public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirectory.Backend {
|
public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirectory.Backend {
|
||||||
|
|
||||||
private abstract static class Lazy<E> {
|
private abstract static class Lazy<E> {
|
||||||
abstract E get() throws BadDataException;
|
abstract E get() throws BadDataException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locking mechanism which uses a lock file to synchronize write-access to the store.
|
||||||
|
*/
|
||||||
private static class FileLockingMechanism implements PGPCertificateDirectory.LockingMechanism {
|
private static class FileLockingMechanism implements PGPCertificateDirectory.LockingMechanism {
|
||||||
|
|
||||||
private final File lockFile;
|
private final File lockFile;
|
||||||
|
@ -209,6 +217,10 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (subdirectories == null) {
|
||||||
|
subdirectories = new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
for (File subdirectory : subdirectories) {
|
for (File subdirectory : subdirectories) {
|
||||||
File[] files = subdirectory.listFiles(new FileFilter() {
|
File[] files = subdirectory.listFiles(new FileFilter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -217,6 +229,10 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (files == null) {
|
||||||
|
files = new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
for (File certFile : files) {
|
for (File certFile : files) {
|
||||||
certificateQueue.add(new Lazy<Certificate>() {
|
certificateQueue.add(new Lazy<Certificate>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -364,10 +380,14 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
return getTag(certFile);
|
return getTag(certFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to resolve file names from certificate fingerprints / special names.
|
||||||
|
*/
|
||||||
public static class FilenameResolver {
|
public static class FilenameResolver {
|
||||||
|
|
||||||
private final File baseDirectory;
|
private final File baseDirectory;
|
||||||
private final Pattern openPgpV4FingerprintPattern = Pattern.compile("^[a-f0-9]{40}$");
|
// matches v4 and v5 fingerprints (v4 = 40 hex chars, v5 = 64 hex chars)
|
||||||
|
private final Pattern openPgpFingerprint = Pattern.compile("^[a-f0-9]{40}([a-f0-9]{24})?$");
|
||||||
|
|
||||||
public FilenameResolver(File baseDirectory) {
|
public FilenameResolver(File baseDirectory) {
|
||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
|
@ -415,26 +435,8 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
return new File(getBaseDirectory(), specialName);
|
return new File(getBaseDirectory(), specialName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the file location for the key addressed using the given special name.
|
|
||||||
* For known special names, see {@link SpecialNames}.
|
|
||||||
*
|
|
||||||
* @param specialName special name (e.g. "trust-root")
|
|
||||||
* @return absolute key file location
|
|
||||||
*
|
|
||||||
* @throws BadNameException in case the given special name is not known
|
|
||||||
*/
|
|
||||||
public File getKeyFileBySpecialName(String specialName)
|
|
||||||
throws BadNameException {
|
|
||||||
if (!isSpecialName(specialName)) {
|
|
||||||
throw new BadNameException(String.format("%s is not a known special name", specialName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new File(getBaseDirectory(), specialName + ".key");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isFingerprint(String fingerprint) {
|
private boolean isFingerprint(String fingerprint) {
|
||||||
return openPgpV4FingerprintPattern.matcher(fingerprint).matches();
|
return openPgpFingerprint.matcher(fingerprint).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSpecialName(String specialName) {
|
private boolean isSpecialName(String specialName) {
|
||||||
|
|
|
@ -21,6 +21,10 @@ import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link PGPCertificateDirectory.Backend} which stores key material in-memory.
|
||||||
|
* It uses object locking with {@link #wait()} and {@link #notify()} to synchronize write-access.
|
||||||
|
*/
|
||||||
public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirectory.Backend {
|
public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirectory.Backend {
|
||||||
|
|
||||||
protected static class ObjectLockingMechanism implements PGPCertificateDirectory.LockingMechanism {
|
protected static class ObjectLockingMechanism implements PGPCertificateDirectory.LockingMechanism {
|
||||||
|
|
|
@ -6,6 +6,9 @@ package pgp.cert_d.subkey_lookup;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory class to instantiate {@link InMemorySubkeyLookup} objects.
|
||||||
|
*/
|
||||||
public class InMemorySubkeyLookupFactory implements SubkeyLookupFactory {
|
public class InMemorySubkeyLookupFactory implements SubkeyLookupFactory {
|
||||||
@Override
|
@Override
|
||||||
public SubkeyLookup createFileBasedInstance(File baseDirectory) {
|
public SubkeyLookup createFileBasedInstance(File baseDirectory) {
|
||||||
|
|
|
@ -6,6 +6,9 @@ package pgp.cert_d.subkey_lookup;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory class to instantiate different {@link SubkeyLookup} implementations.
|
||||||
|
*/
|
||||||
public interface SubkeyLookupFactory {
|
public interface SubkeyLookupFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class PGPCertificateDirectoryTest {
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory)
|
public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory)
|
||||||
throws IOException, InterruptedException, BadDataException {
|
throws IOException, InterruptedException, BadDataException {
|
||||||
// Manually lock the dir
|
// Manually lock the dir
|
||||||
assertFalse(directory.backend.getLock().isLocked());
|
assertFalse(directory.backend.getLock().isLocked());
|
||||||
|
@ -86,6 +86,40 @@ public class PGPCertificateDirectoryTest {
|
||||||
assertNotNull(inserted);
|
assertNotNull(inserted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestSubjects")
|
||||||
|
public void lockDirectoryAndTryInsertTrustRootWillFail(PGPCertificateDirectory directory)
|
||||||
|
throws IOException, InterruptedException, BadDataException {
|
||||||
|
// Manually lock the dir
|
||||||
|
assertFalse(directory.backend.getLock().isLocked());
|
||||||
|
directory.backend.getLock().lockDirectory();
|
||||||
|
assertTrue(directory.backend.getLock().isLocked());
|
||||||
|
|
||||||
|
KeyMaterial inserted = directory.tryInsertTrustRoot(TestKeys.getHarryKey(), merger);
|
||||||
|
assertNull(inserted);
|
||||||
|
|
||||||
|
directory.backend.getLock().releaseDirectory();
|
||||||
|
inserted = directory.tryInsertTrustRoot(TestKeys.getHarryKey(), merger);
|
||||||
|
assertNotNull(inserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestSubjects")
|
||||||
|
public void lockDirectoryAndTryInsertWithSpecialNameWillFail(PGPCertificateDirectory directory)
|
||||||
|
throws IOException, InterruptedException, BadDataException, BadNameException {
|
||||||
|
// Manually lock the dir
|
||||||
|
assertFalse(directory.backend.getLock().isLocked());
|
||||||
|
directory.backend.getLock().lockDirectory();
|
||||||
|
assertTrue(directory.backend.getLock().isLocked());
|
||||||
|
|
||||||
|
Certificate inserted = directory.tryInsertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
|
||||||
|
assertNull(inserted);
|
||||||
|
|
||||||
|
directory.backend.getLock().releaseDirectory();
|
||||||
|
inserted = directory.tryInsertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
|
||||||
|
assertNotNull(inserted);
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void getByInvalidNameFails(PGPCertificateDirectory directory) {
|
public void getByInvalidNameFails(PGPCertificateDirectory directory) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '0.1.2'
|
shortVersion = '0.2.1'
|
||||||
isSnapshot = true
|
isSnapshot = true
|
||||||
minAndroidSdk = 26
|
minAndroidSdk = 26
|
||||||
animalsnifferSignatureVersion = "$minAndroidSdk:8.0.0_r2"
|
animalsnifferSignatureVersion = "$minAndroidSdk:8.0.0_r2"
|
||||||
|
|
Loading…
Reference in New Issue