mirror of
https://codeberg.org/PGPainless/cert-d-java.git
synced 2025-09-10 11:49:39 +02:00
Add back support for getXIfChanged(Y, tag)
This commit is contained in:
parent
d050cb5516
commit
27f4598437
15 changed files with 423 additions and 101 deletions
|
@ -5,12 +5,17 @@
|
|||
package pgp.cert_d;
|
||||
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
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;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.Key;
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
import pgp.certificate_store.exception.NotAStoreException;
|
||||
|
@ -18,6 +23,7 @@ import pgp.certificate_store.exception.NotAStoreException;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
|
@ -30,6 +36,7 @@ import java.util.stream.Stream;
|
|||
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;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
@ -37,6 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
|
||||
public class PGPCertificateDirectoryTest {
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
private static final Charset UTF8 = Charset.forName("UTF8");
|
||||
|
||||
private static final String HARRY_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||
|
@ -143,7 +151,10 @@ public class PGPCertificateDirectoryTest {
|
|||
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||
private static final String CEDRIC_FP = "5e75bf20646bc1a98d3b1bc2fe9cd472987c4021";
|
||||
|
||||
private static Stream<PGPCertificateDirectory> provideTestSubjects() throws IOException, NotAStoreException {
|
||||
private static final KeyMaterialMerger merger = new TestKeyMaterialMerger();
|
||||
|
||||
private static Stream<PGPCertificateDirectory> provideTestSubjects()
|
||||
throws IOException, NotAStoreException {
|
||||
PGPCertificateDirectory inMemory = PGPCertificateDirectories.inMemoryCertificateDirectory(
|
||||
new TestKeyMaterialReaderBackend());
|
||||
|
||||
|
@ -159,18 +170,19 @@ public class PGPCertificateDirectoryTest {
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestSubjects")
|
||||
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory) throws IOException, InterruptedException, BadDataException {
|
||||
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory)
|
||||
throws IOException, InterruptedException, BadDataException {
|
||||
// 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(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
||||
Certificate inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||
assertNull(inserted);
|
||||
|
||||
directory.backend.getLock().releaseDirectory();
|
||||
inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
||||
inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||
assertNotNull(inserted);
|
||||
}
|
||||
|
||||
|
@ -188,7 +200,7 @@ public class PGPCertificateDirectoryTest {
|
|||
|
||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8));
|
||||
|
||||
Certificate certificate = directory.insert(bytesIn, new TestKeyMaterialMerger());
|
||||
Certificate certificate = directory.insert(bytesIn, merger);
|
||||
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
|
||||
|
||||
Certificate get = directory.getByFingerprint(CEDRIC_FP);
|
||||
|
@ -207,7 +219,7 @@ public class PGPCertificateDirectoryTest {
|
|||
assertNull(directory.getTrustRoot());
|
||||
|
||||
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), new TestKeyMaterialMerger());
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||
assertNotNull(trustRootMaterial);
|
||||
assertTrue(trustRootMaterial instanceof Key);
|
||||
assertEquals(HARRY_FP, trustRootMaterial.getFingerprint());
|
||||
|
@ -217,8 +229,8 @@ public class PGPCertificateDirectoryTest {
|
|||
Certificate trustRootCert = directory.getTrustRootCertificate();
|
||||
assertEquals(HARRY_FP, trustRootCert.getFingerprint());
|
||||
|
||||
directory.tryInsert(new ByteArrayInputStream(RON_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
||||
directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
||||
directory.tryInsert(new ByteArrayInputStream(RON_CERT.getBytes(UTF8)), merger);
|
||||
directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||
|
||||
Set<String> expected = new HashSet<>(Arrays.asList(RON_FP, CEDRIC_FP));
|
||||
|
||||
|
@ -230,4 +242,104 @@ public class PGPCertificateDirectoryTest {
|
|||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestSubjects")
|
||||
public void testGetTrustRootIfChanged(PGPCertificateDirectory directory)
|
||||
throws BadDataException, IOException, InterruptedException {
|
||||
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), 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(
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), 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,
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||
|
||||
assertNotNull(specialName.getTag());
|
||||
Long tag = specialName.getTag();
|
||||
assertNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag));
|
||||
assertNotNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag + 1));
|
||||
|
||||
Long oldTag = tag;
|
||||
// "update" key
|
||||
specialName = directory.insertWithSpecialName(SpecialNames.TRUST_ROOT,
|
||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||
tag = specialName.getTag();
|
||||
|
||||
assertNotEquals(oldTag, tag);
|
||||
assertNotNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, oldTag));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestSubjects")
|
||||
public void testGetByFingerprintIfChanged(PGPCertificateDirectory directory)
|
||||
throws BadDataException, IOException, InterruptedException, BadNameException {
|
||||
Certificate certificate = directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||
Long tag = certificate.getTag();
|
||||
assertNotNull(tag);
|
||||
|
||||
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
|
||||
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
|
||||
|
||||
Long oldTag = tag;
|
||||
// "update" cert
|
||||
certificate = directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||
tag = certificate.getTag();
|
||||
|
||||
assertNotEquals(oldTag, tag);
|
||||
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
|
||||
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), oldTag));
|
||||
}
|
||||
|
||||
@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(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d;
|
||||
package pgp.cert_d.dummy;
|
||||
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d;
|
||||
package pgp.cert_d.dummy;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
|
@ -33,24 +33,22 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
|||
KeyFingerPrintCalculator fpCalc = new BcKeyFingerprintCalculator();
|
||||
|
||||
@Override
|
||||
public KeyMaterial read(InputStream data) throws IOException, BadDataException {
|
||||
public KeyMaterial read(InputStream data, Long tag) throws IOException, BadDataException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(data, out);
|
||||
|
||||
try {
|
||||
Key key = readKey(new ByteArrayInputStream(out.toByteArray()));
|
||||
return key;
|
||||
return readKey(new ByteArrayInputStream(out.toByteArray()), tag);
|
||||
} catch (IOException | PGPException e) {
|
||||
try {
|
||||
Certificate certificate = readCertificate(new ByteArrayInputStream(out.toByteArray()));
|
||||
return certificate;
|
||||
return readCertificate(new ByteArrayInputStream(out.toByteArray()), tag);
|
||||
} catch (IOException e1) {
|
||||
throw new BadDataException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Key readKey(InputStream inputStream) throws IOException, PGPException {
|
||||
private Key readKey(InputStream inputStream, Long tag) throws IOException, PGPException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(inputStream, buffer);
|
||||
inputStream.close();
|
||||
|
@ -60,64 +58,21 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
|||
PGPSecretKeyRing secretKeys = new PGPSecretKeyRing(decoderStream, fpCalc);
|
||||
PGPPublicKeyRing cert = extractCert(secretKeys);
|
||||
ByteArrayInputStream encoded = new ByteArrayInputStream(cert.getEncoded());
|
||||
Certificate certificate = readCertificate(encoded);
|
||||
Certificate certificate = readCertificate(encoded, tag);
|
||||
|
||||
return new Key() {
|
||||
@Override
|
||||
public Certificate getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() {
|
||||
return certificate.getFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(buffer.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getSubkeyIds() throws IOException {
|
||||
return certificate.getSubkeyIds();
|
||||
}
|
||||
};
|
||||
return new Key(buffer.toByteArray(), certificate, tag);
|
||||
}
|
||||
|
||||
private Certificate readCertificate(InputStream inputStream) throws IOException {
|
||||
private Certificate readCertificate(InputStream inputStream, Long tag) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(inputStream, buffer);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
|
||||
InputStream decoderStream = PGPUtil.getDecoderStream(in);
|
||||
|
||||
PGPPublicKeyRing cert = new PGPPublicKeyRing(decoderStream, fpCalc);
|
||||
return new Certificate() {
|
||||
@Override
|
||||
public String getFingerprint() {
|
||||
return Hex.toHexString(cert.getPublicKey().getFingerprint()).toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(buffer.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getSubkeyIds() throws IOException {
|
||||
return TestKeyMaterialReaderBackend.getSubkeyIds(cert);
|
||||
}
|
||||
};
|
||||
String fingerprint = Hex.toHexString(cert.getPublicKey().getFingerprint()).toLowerCase();
|
||||
List<Long> subKeyIds = getSubkeyIds(cert);
|
||||
return new Certificate(buffer.toByteArray(), fingerprint, subKeyIds, tag);
|
||||
}
|
||||
|
||||
private PGPPublicKeyRing extractCert(PGPSecretKeyRing secretKeys) {
|
||||
|
@ -126,8 +81,7 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
|||
while (publicKeyIterator.hasNext()) {
|
||||
publicKeyList.add(publicKeyIterator.next());
|
||||
}
|
||||
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(publicKeyList);
|
||||
return publicKeyRing;
|
||||
return new PGPPublicKeyRing(publicKeyList);
|
||||
}
|
||||
|
||||
private static List<Long> getSubkeyIds(PGPKeyRing keyRing) {
|
Loading…
Add table
Add a link
Reference in a new issue