diff --git a/sigstore-java/src/main/java/dev/sigstore/encryption/certificates/Certificates.java b/sigstore-java/src/main/java/dev/sigstore/encryption/certificates/Certificates.java index df31ff32..49770a23 100644 --- a/sigstore-java/src/main/java/dev/sigstore/encryption/certificates/Certificates.java +++ b/sigstore-java/src/main/java/dev/sigstore/encryption/certificates/Certificates.java @@ -23,6 +23,8 @@ import java.nio.charset.StandardCharsets; import java.security.cert.*; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; public class Certificates { @@ -55,11 +57,22 @@ public static Certificate fromPem(byte[] cert) throws CertificateException { return fromPem(new String(cert, StandardCharsets.UTF_8)); } + /** Convert a single der encoded cert to Certificate. */ public static Certificate fromDer(byte[] cert) throws CertificateException { CertificateFactory cf = CertificateFactory.getInstance("X.509"); return cf.generateCertificate(new ByteArrayInputStream(cert)); } + /** Convert a lit of der encoded certs to CertPath. */ + public static CertPath fromDer(List certChain) throws CertificateException { + List certificates = new ArrayList<>(certChain.size()); + for (var cert : certChain) { + certificates.add(fromDer(cert)); + } + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertPath(certificates); + } + /** Convert a CertPath to a PEM encoded certificate chain. */ public static String toPemString(CertPath certs) throws IOException { var certWriter = new StringWriter(); @@ -116,4 +129,10 @@ public static CertPath fromPemChain(String certs) throws CertificateException { public static CertPath fromPemChain(byte[] certs) throws CertificateException { return fromPemChain(new String(certs, StandardCharsets.UTF_8)); } + + /** Converts a single X509Certificate to a {@link CertPath}. */ + public static CertPath toCertPath(Certificate certificate) throws CertificateException { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertPath(Collections.singletonList(certificate)); + } } diff --git a/sigstore-java/src/main/java/dev/sigstore/trustroot/SigstoreTrustedRoot.java b/sigstore-java/src/main/java/dev/sigstore/trustroot/SigstoreTrustedRoot.java index 3635a04a..0f90ba75 100644 --- a/sigstore-java/src/main/java/dev/sigstore/trustroot/SigstoreTrustedRoot.java +++ b/sigstore-java/src/main/java/dev/sigstore/trustroot/SigstoreTrustedRoot.java @@ -15,23 +15,31 @@ */ package dev.sigstore.trustroot; +import com.google.common.annotations.VisibleForTesting; import dev.sigstore.proto.trustroot.v1.TrustedRoot; import java.security.cert.CertificateException; +import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import org.immutables.value.Value.Immutable; @Immutable -public interface SigstoreTrustedRoot { +public abstract class SigstoreTrustedRoot { - List getCertificateAuthorities(); + /** A list of certificate authorities associated with this trustroot. */ + public abstract List getCertificateAuthorities(); - List getTLogs(); + /** A list of binary transparency logs associated with this trustroot. */ + public abstract List getTLogs(); - List getCTLogs(); + /** A list of certificate transparency logs associated with this trustroot. */ + public abstract List getCTLogs(); - static SigstoreTrustedRoot from(TrustedRoot proto) throws CertificateException { + /** Create an instance from a parsed proto definition of a trustroot. */ + public static SigstoreTrustedRoot from(TrustedRoot proto) throws CertificateException { List certificateAuthorities = new ArrayList<>(proto.getCertificateAuthoritiesCount()); for (var certAuthority : proto.getCertificateAuthoritiesList()) { @@ -48,4 +56,114 @@ static SigstoreTrustedRoot from(TrustedRoot proto) throws CertificateException { .cTLogs(ctlogs) .build(); } + + /** + * Find transparency log matching the logId and validity time. + * + * @param logId a log id + * @param time a point in time during the logs validity period + * @return the first log found matching logId and time, or empty if none match + */ + public Optional getTlog(byte[] logId, Instant time) { + return getTLog(getTLogs(), logId, time); + } + + /** + * Find certificate transparency log matching the logId and validity time. + * + * @param logId a log id + * @param time a point in time during the logs validity period + * @return the first log found matching logId and time, or empty if none match + */ + public Optional getCTLog(byte[] logId, Instant time) { + return getTLog(getCTLogs(), logId, time); + } + + private Optional getTLog( + List tlogs, byte[] logId, Instant time) { + return tlogs.stream() + .filter(tl -> Arrays.equals(tl.getLogId().getKeyId(), logId)) + .filter(tl -> tl.getPublicKey().getValidFor().getStart().compareTo(time) <= 0) + .filter( + tl -> + tl.getPublicKey().getValidFor().getEnd().orElse(Instant.now()).compareTo(time) >= 0) + .findAny(); + } + + /** + * Get the one an only current TLog + * + * @return the current active TLog + * @throws IllegalStateException if trust root does not contain exactly one transparency log + */ + public Optional getCurrentTLog() { + return getCurrentTLog(getTLogs()); + } + + /** + * Get the one an only current CTLog + * + * @return the current active CTLog + * @throws IllegalStateException if trust root does not contain exactly one active CT log + */ + public Optional getCurrentCTLog() { + return getCurrentTLog(getCTLogs()); + } + + @VisibleForTesting + static Optional getCurrentTLog(List tlogs) { + var current = + tlogs.stream() + .filter(tl -> tl.getPublicKey().getValidFor().getEnd().isEmpty()) + .collect(Collectors.toList()); + if (current.size() == 0) { + throw new IllegalStateException("Trust root contains no current transparency logs"); + } + if (current.size() > 1) { + throw new IllegalStateException( + "Trust root contains multiple current transparency logs (" + current.size() + ")"); + } + return Optional.of(current.get(0)); + } + + /** + * Find a CA by validity time, users of this method will need to then compare the key in the leaf + * to find the exact CA to validate against + * + * @param time the time the CA was expected to be valid (usually tlog entry time) + * @return a list of CAs that were valid at {@code time} + */ + public List getCAs(Instant time) { + return getCertificateAuthorities().stream() + .filter(ca -> ca.getValidFor().getStart().compareTo(time) <= 0) + .filter(ca -> ca.getValidFor().getEnd().orElse(Instant.now()).compareTo(time) >= 0) + .collect(Collectors.toList()); + } + + /** + * Get the one an only current Certificate Authority + * + * @return the current active CA + * @throws IllegalStateException if trust root does not contain exactly one active CA + */ + public Optional getCurrentCA() { + return getCurrentCA(getCertificateAuthorities()); + } + + @VisibleForTesting + static Optional getCurrentCA( + List certificateAuthorities) { + var current = + certificateAuthorities.stream() + .filter(ca -> ca.getValidFor().getEnd().isEmpty()) + .collect(Collectors.toList()); + if (current.size() == 0) { + throw new IllegalStateException("Trust root contains no current certificate authorities"); + } + if (current.size() > 1) { + throw new IllegalStateException( + "Trust root contains multiple current certificate authorities (" + current.size() + ")"); + } + return Optional.of(current.get(0)); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/encryption/certificates/CertificatesTest.java b/sigstore-java/src/test/java/dev/sigstore/encryption/certificates/CertificatesTest.java index 4921f6a0..f2df8ebe 100644 --- a/sigstore-java/src/test/java/dev/sigstore/encryption/certificates/CertificatesTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/encryption/certificates/CertificatesTest.java @@ -19,6 +19,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.List; +import org.bouncycastle.util.encoders.Base64; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -94,4 +97,37 @@ public void fromPemChain_garbage() throws IOException { var pemString = "garbage"; Assertions.assertThrows(CertificateException.class, () -> Certificates.fromPemChain(pemString)); } + + @Test + public void fromDer() throws Exception { + var derCert = + Base64.decode( + "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ=="); + Assertions.assertNotNull(Certificates.fromDer(derCert)); + } + + @Test + public void fromDer_certPath() throws Exception { + List certs = new ArrayList<>(2); + certs.add( + 0, + Base64.decode( + "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=")); + certs.add( + 1, + Base64.decode( + "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ")); + Assertions.assertEquals(2, Certificates.fromDer(certs).getCertificates().size()); + } + + @Test + public void toCertPath() throws Exception { + var cert = + Certificates.fromDer( + Base64.decode( + "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==")); + var certPath = Certificates.toCertPath(cert); + Assertions.assertEquals(1, certPath.getCertificates().size()); + Assertions.assertEquals(cert, certPath.getCertificates().get(0)); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/trustroot/SigstoreTrustedRootTest.java b/sigstore-java/src/test/java/dev/sigstore/trustroot/SigstoreTrustedRootTest.java index aff9dfb2..344da392 100644 --- a/sigstore-java/src/test/java/dev/sigstore/trustroot/SigstoreTrustedRootTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/trustroot/SigstoreTrustedRootTest.java @@ -15,22 +15,34 @@ */ package dev.sigstore.trustroot; +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.assertTrue; import com.google.common.io.Resources; import com.google.protobuf.util.JsonFormat; +import dev.sigstore.encryption.certificates.Certificates; import dev.sigstore.proto.trustroot.v1.TrustedRoot; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.bouncycastle.util.encoders.Base64; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; class SigstoreTrustedRootTest { - @Test - void from_prod() throws Exception { + private static SigstoreTrustedRoot trustRoot; + + @BeforeAll + public static void initTrustRoot() throws IOException, CertificateException { var json = Resources.toString( Resources.getResource("dev/sigstore/trustroot/trusted_root.json"), @@ -38,8 +50,11 @@ void from_prod() throws Exception { var builder = TrustedRoot.newBuilder(); JsonFormat.parser().merge(json, builder); - var trustRoot = SigstoreTrustedRoot.from(builder.build()); + trustRoot = SigstoreTrustedRoot.from(builder.build()); + } + @Test + void from_checkFields() throws Exception { assertEquals(2, trustRoot.getCertificateAuthorities().size()); assertEquals(1, trustRoot.getTLogs().size()); assertEquals(2, trustRoot.getCTLogs().size()); @@ -117,4 +132,155 @@ void from_prod() throws Exception { "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==", Base64.toBase64String(PublicKey.toJavaPublicKey(currCTLog.getPublicKey()).getEncoded())); } + + @Test + public void getTlog_isPresent() { + var key = Base64.decode("wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="); + + // exactly at start time + assertIsPresent( + trustRoot.getTlog(key, ZonedDateTime.parse("2021-01-12T11:53:27.000Z").toInstant())); + // other random time + assertIsPresent( + trustRoot.getTlog(key, ZonedDateTime.parse("2021-12-12T11:53:27.000Z").toInstant())); + } + + @Test + public void getTlog_isNotPresent() { + // before the start time, but valid key + assertIsEmpty( + trustRoot.getTlog( + Base64.decode("wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="), + ZonedDateTime.parse("2021-01-01T11:53:27.000Z").toInstant())); + + // valid time but bad key + assertIsEmpty( + trustRoot.getTlog( + Base64.decode("wNI8atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="), + ZonedDateTime.parse("2021-12-12T11:53:27.000Z").toInstant())); + } + + @Test + public void getCurrentTLog() { + var tlog = trustRoot.getCurrentTLog(); + assertIsPresent(tlog); + assertArrayEquals( + Base64.decode("wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="), + tlog.get().getLogId().getKeyId()); + } + + @Test + public void getCurrentCTLog() { + var ctlog = trustRoot.getCurrentCTLog(); + assertIsPresent(ctlog); + assertArrayEquals( + Base64.decode("3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4="), + ctlog.get().getLogId().getKeyId()); + } + + @Test + public void getCurrentTLog_missing() { + Assertions.assertThrows( + IllegalStateException.class, + () -> SigstoreTrustedRoot.getCurrentTLog(Collections.emptyList())); + } + + @Test + public void getCurrentTlog_tooMany() { + var tlog = trustRoot.getTLogs().get(0); + Assertions.assertThrows( + IllegalStateException.class, () -> SigstoreTrustedRoot.getCurrentTLog(List.of(tlog, tlog))); + } + + @Test + public void getCTlog_isPresent() { + var key = Base64.decode("CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I="); + + // exactly at start + assertIsPresent( + trustRoot.getCTLog(key, ZonedDateTime.parse("2021-03-14T00:00:00.000Z").toInstant())); + // in the middle + assertIsPresent( + trustRoot.getCTLog(key, ZonedDateTime.parse("2021-10-14T00:00:00.000Z").toInstant())); + // exactly at end time + assertIsPresent( + trustRoot.getCTLog(key, ZonedDateTime.parse("2022-10-31T23:59:59.999Z").toInstant())); + } + + @Test + public void getCTlog_isNotPresent() { + var key = Base64.decode("CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I="); + + // before the start time, but valid key + assertIsEmpty( + trustRoot.getCTLog(key, ZonedDateTime.parse("2019-01-01T11:53:27.000Z").toInstant())); + + // after end time, but valid key + assertIsEmpty( + trustRoot.getCTLog(key, ZonedDateTime.parse("2022-11-01T11:53:27.000Z").toInstant())); + + // garbage key + assertIsEmpty( + trustRoot.getCTLog( + Base64.decode("wNI8atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="), + ZonedDateTime.parse("2021-10-14T00:00:00.000Z").toInstant())); + } + + @Test + public void getCAs_isPresent() { + // exactly at start + assertEquals( + 1, trustRoot.getCAs(ZonedDateTime.parse("2021-03-07T03:20:29.000Z").toInstant()).size()); + // somewhere in the middle + assertEquals( + 1, trustRoot.getCAs(ZonedDateTime.parse("2021-04-28T03:59:59.999Z").toInstant()).size()); + // exactly at end but also overlapping with another CA + assertEquals( + 2, trustRoot.getCAs(ZonedDateTime.parse("2022-12-31T23:59:59.999Z").toInstant()).size()); + } + + @Test + public void getCAs_isNotPresent() { + assertEquals( + 0, trustRoot.getCAs(ZonedDateTime.parse("2015-03-14T00:00:00.000Z").toInstant()).size()); + } + + @Test + public void getCurrentCA() throws CertificateException { + var ca = trustRoot.getCurrentCA(); + assertIsPresent(ca); + List certs = new ArrayList<>(2); + certs.add( + 0, + Base64.decode( + "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=")); + certs.add( + 1, + Base64.decode( + "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ")); + var certPath = Certificates.fromDer(certs); + assertEquals(certPath, ca.get().getCertPath()); + } + + @Test + public void getCurrentCA_missing() { + Assertions.assertThrows( + IllegalStateException.class, + () -> SigstoreTrustedRoot.getCurrentCA(Collections.emptyList())); + } + + @Test + public void getCurrentCA_tooMany() { + var ca = trustRoot.getCertificateAuthorities().get(1); + Assertions.assertThrows( + IllegalStateException.class, () -> SigstoreTrustedRoot.getCurrentCA(List.of(ca, ca))); + } + + private void assertIsPresent(Optional optional) { + assertTrue(optional.isPresent()); + } + + private void assertIsEmpty(Optional optional) { + assertTrue(optional.isEmpty()); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/tuf/SigstoreTufClientTest.java b/sigstore-java/src/test/java/dev/sigstore/tuf/SigstoreTufClientTest.java index c7d1cf1a..1908f0c2 100644 --- a/sigstore-java/src/test/java/dev/sigstore/tuf/SigstoreTufClientTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/tuf/SigstoreTufClientTest.java @@ -39,6 +39,9 @@ public void testUpdate_publicGoodHasTrustedRootJson() throws Exception { .build(); client.forceUpdate(); Assertions.assertNotNull(client.getSigstoreTrustedRoot()); + Assertions.assertTrue(client.getSigstoreTrustedRoot().getCurrentTLog().isPresent()); + Assertions.assertTrue(client.getSigstoreTrustedRoot().getCurrentCTLog().isPresent()); + Assertions.assertTrue(client.getSigstoreTrustedRoot().getCurrentCA().isPresent()); } @Test