From c05424b947f07cd622ebd75ce54571d2ad51f829 Mon Sep 17 00:00:00 2001 From: mcilwain Date: Thu, 5 Jan 2017 14:26:18 -0800 Subject: [PATCH] Daggerize TMCH/signed mark util classes This allows them to support injectable configuration. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143709052 --- .../flows/domain/DomainFlowTmchUtils.java | 9 +- java/google/registry/model/tmch/TmchCrl.java | 11 +- .../tmch/TmchCertificateAuthority.java | 157 +++++++++++------- java/google/registry/tmch/TmchCrlAction.java | 6 +- .../registry/tmch/TmchXmlSignature.java | 36 ++-- .../GenerateApplicationsReportCommand.java | 6 +- .../registry/flows/CheckApiActionTest.java | 3 +- .../registry/flows/EppCommitLogsTest.java | 2 +- .../google/registry/flows/EppTestCase.java | 2 +- .../registry/flows/EppTestComponent.java | 12 +- .../google/registry/flows/FlowTestCase.java | 8 +- .../DomainApplicationCreateFlowTest.java | 5 +- javatests/google/registry/model/schema.txt | 1 + .../registry/model/tmch/TmchCrlTest.java | 2 +- .../registry/testing/RegistryConfigRule.java | 10 -- .../tmch/TmchCertificateAuthorityTest.java | 28 ++-- .../registry/tmch/TmchCrlActionTest.java | 14 +- .../registry/tmch/TmchXmlSignatureTest.java | 90 +++++----- .../registry/tools/UpdateSmdCommandTest.java | 5 +- 19 files changed, 242 insertions(+), 165 deletions(-) diff --git a/java/google/registry/flows/domain/DomainFlowTmchUtils.java b/java/google/registry/flows/domain/DomainFlowTmchUtils.java index 0aa43fc36..c895072df 100644 --- a/java/google/registry/flows/domain/DomainFlowTmchUtils.java +++ b/java/google/registry/flows/domain/DomainFlowTmchUtils.java @@ -48,7 +48,12 @@ import org.xml.sax.SAXException; /** TMCH utility functions for domain flows. */ public final class DomainFlowTmchUtils { - @Inject public DomainFlowTmchUtils() {} + private final TmchXmlSignature tmchXmlSignature; + + @Inject + public DomainFlowTmchUtils(TmchXmlSignature tmchXmlSignature) { + this.tmchXmlSignature = tmchXmlSignature; + } public SignedMark verifySignedMarks( ImmutableList signedMarks, String domainLabel, DateTime now) @@ -86,7 +91,7 @@ public final class DomainFlowTmchUtils { } try { - TmchXmlSignature.verify(signedMarkData); + tmchXmlSignature.verify(signedMarkData); } catch (CertificateExpiredException e) { throw new SignedMarkCertificateExpiredException(); } catch (CertificateNotYetValidException e) { diff --git a/java/google/registry/model/tmch/TmchCrl.java b/java/google/registry/model/tmch/TmchCrl.java index 5884e6374..dfabc9179 100644 --- a/java/google/registry/model/tmch/TmchCrl.java +++ b/java/google/registry/model/tmch/TmchCrl.java @@ -37,6 +37,7 @@ public final class TmchCrl extends CrossTldSingleton { String crl; DateTime updated; + String url; /** Returns the singleton instance of this entity, without memoization. */ @Nullable @@ -50,13 +51,14 @@ public final class TmchCrl extends CrossTldSingleton { *

Please do not call this function unless your CRL is properly formatted, signed by the root, * and actually newer than the one currently in the datastore. */ - public static void set(final String crl) { + public static void set(final String crl, final String url) { ofy().transactNew(new VoidWork() { @Override public void vrun() { TmchCrl tmchCrl = new TmchCrl(); tmchCrl.updated = ofy().getTransactionTime(); tmchCrl.crl = checkNotNull(crl, "crl"); + tmchCrl.url = checkNotNull(url, "url"); ofy().saveWithoutBackup().entity(tmchCrl); }}); } @@ -66,7 +68,12 @@ public final class TmchCrl extends CrossTldSingleton { return crl; } - /** Time we last updated the datastore with a newer ICANN CRL. */ + /** Returns the URL that the CRL was downloaded from. */ + public final String getUrl() { + return crl; + } + + /** Time we last updated the Datastore with a newer ICANN CRL. */ public final DateTime getUpdated() { return updated; } diff --git a/java/google/registry/tmch/TmchCertificateAuthority.java b/java/google/registry/tmch/TmchCertificateAuthority.java index 1c7ea6a41..76600712b 100644 --- a/java/google/registry/tmch/TmchCertificateAuthority.java +++ b/java/google/registry/tmch/TmchCertificateAuthority.java @@ -14,13 +14,13 @@ package google.registry.tmch; -import static com.google.common.base.Throwables.propagateIfInstanceOf; -import static google.registry.util.CacheUtils.memoizeWithLongExpiration; -import static google.registry.util.CacheUtils.memoizeWithShortExpiration; import static google.registry.util.ResourceUtils.readResourceUtf8; -import static google.registry.util.X509Utils.loadCrl; +import static java.util.concurrent.TimeUnit.MILLISECONDS; -import com.google.common.base.Supplier; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import google.registry.config.ConfigModule.Config; import google.registry.config.RegistryEnvironment; import google.registry.model.tmch.TmchCrl; import google.registry.util.Clock; @@ -30,10 +30,18 @@ import google.registry.util.X509Utils; import java.security.GeneralSecurityException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; +import java.util.concurrent.ExecutionException; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; -/** Datastore singleton for ICANN's TMCH root certificate and revocation list. */ +/** + * Helper methods for accessing ICANN's TMCH root certificate and revocation list. + * + *

There are two CRLs, a real one for the production environment and a testing one for + * non-production environments. The Datastore singleton {@link TmchCrl} entity is used to cache this + * CRL once loaded and will always contain the proper one corresponding to the environment. + */ @Immutable @ThreadSafe public final class TmchCertificateAuthority { @@ -45,45 +53,70 @@ public final class TmchCertificateAuthority { private static final String CRL_FILE = "icann-tmch.crl"; private static final String TEST_CRL_FILE = "icann-tmch-test.crl"; - /** - * A cached supplier that loads the crl from datastore or chooses a default value. - * - *

We keep the cache here rather than caching TmchCrl in the model, because loading the crl - * string into an X509CRL instance is expensive and should itself be cached. - */ - private static final Supplier CRL_CACHE = - memoizeWithShortExpiration(new Supplier() { - @Override - public X509CRL get() { - TmchCrl storedCrl = TmchCrl.get(); - try { - X509CRL crl = loadCrl((storedCrl == null) - ? readResourceUtf8( - TmchCertificateAuthority.class, - ENVIRONMENT.config().getTmchCaTestingMode() ? TEST_CRL_FILE : CRL_FILE) - : storedCrl.getCrl()); - crl.verify(getRoot().getPublicKey()); - return crl; - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - }}); + private boolean tmchCaTestingMode; - /** A cached function that loads the crt from a jar resource. */ - private static final Supplier ROOT_CACHE = - memoizeWithLongExpiration(new Supplier() { - @Override - public X509Certificate get() { - try { - X509Certificate root = X509Utils.loadCertificate(readResourceUtf8( - TmchCertificateAuthority.class, - ENVIRONMENT.config().getTmchCaTestingMode() ? TEST_ROOT_CRT_FILE : ROOT_CRT_FILE)); - root.checkValidity(clock.nowUtc().toDate()); - return root; - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - }}); + public @Inject TmchCertificateAuthority(@Config("tmchCaTestingMode") boolean tmchCaTestingMode) { + this.tmchCaTestingMode = tmchCaTestingMode; + } + + /** + * A cached supplier that loads the CRL from Datastore or chooses a default value. + * + *

We keep the cache here rather than caching TmchCrl in the model, because loading the CRL + * string into an X509CRL instance is expensive and should itself be cached. + * + *

Note that the stored CRL won't exist for tests, and on deployed environments will always + * correspond to the correct CRL for the given testing mode because {@link TmchCrlAction} can + * only persist the correct one for this given environment. + */ + private static final LoadingCache CRL_CACHE = + CacheBuilder.newBuilder() + .expireAfterWrite( + ENVIRONMENT.config().getSingletonCacheRefreshDuration().getMillis(), MILLISECONDS) + .build( + new CacheLoader() { + @Override + public X509CRL load(final Boolean tmchCaTestingMode) + throws GeneralSecurityException { + TmchCrl storedCrl = TmchCrl.get(); + try { + String crlContents; + if (storedCrl == null) { + String file = tmchCaTestingMode.booleanValue() ? TEST_CRL_FILE : CRL_FILE; + crlContents = readResourceUtf8(TmchCertificateAuthority.class, file); + } else { + crlContents = storedCrl.getCrl(); + } + X509CRL crl = X509Utils.loadCrl(crlContents); + crl.verify(ROOT_CACHE.get(tmchCaTestingMode).getPublicKey()); + return crl; + } catch (ExecutionException e) { + if (e.getCause() instanceof GeneralSecurityException) { + throw (GeneralSecurityException) e.getCause(); + } else { + throw new RuntimeException("Unexpected exception while loading CRL", e); + } + } + }}); + + /** A cached function that loads the CRT from a jar resource. */ + private static final LoadingCache ROOT_CACHE = + CacheBuilder.newBuilder() + .expireAfterWrite( + ENVIRONMENT.config().getSingletonCachePersistDuration().getMillis(), MILLISECONDS) + .build( + new CacheLoader() { + @Override + public X509Certificate load(final Boolean tmchCaTestingMode) + throws GeneralSecurityException { + String file = + tmchCaTestingMode.booleanValue() ? TEST_ROOT_CRT_FILE : ROOT_CRT_FILE; + X509Certificate root = + X509Utils.loadCertificate( + readResourceUtf8(TmchCertificateAuthority.class, file)); + root.checkValidity(clock.nowUtc().toDate()); + return root; + }}); @NonFinalForTesting private static Clock clock = new SystemClock(); @@ -97,14 +130,14 @@ public final class TmchCertificateAuthority { * incorrect keys, and for invalid, old, not-yet-valid or revoked certificates. * @see X509Utils#verifyCertificate */ - public static void verify(X509Certificate cert) throws GeneralSecurityException { + public void verify(X509Certificate cert) throws GeneralSecurityException { synchronized (TmchCertificateAuthority.class) { X509Utils.verifyCertificate(getRoot(), getCrl(), cert, clock.nowUtc().toDate()); } } /** - * Update to the latest TMCH X.509 certificate revocation list and save to the datastore. + * Update to the latest TMCH X.509 certificate revocation list and save it to Datastore. * *

Your ASCII-armored CRL must be signed by the current ICANN root certificate. * @@ -115,27 +148,35 @@ public final class TmchCertificateAuthority { * incorrect keys, and for invalid, old, not-yet-valid or revoked certificates. * @see X509Utils#verifyCrl */ - public static void updateCrl(String asciiCrl) throws GeneralSecurityException { + public void updateCrl(String asciiCrl, String url) throws GeneralSecurityException { X509CRL crl = X509Utils.loadCrl(asciiCrl); X509Utils.verifyCrl(getRoot(), getCrl(), crl, clock.nowUtc().toDate()); - TmchCrl.set(asciiCrl); + TmchCrl.set(asciiCrl, url); } - public static X509Certificate getRoot() throws GeneralSecurityException { + public X509Certificate getRoot() throws GeneralSecurityException { try { - return ROOT_CACHE.get(); - } catch (RuntimeException e) { - propagateIfInstanceOf(e.getCause(), GeneralSecurityException.class); - throw e; + return ROOT_CACHE.get(tmchCaTestingMode); + } catch (Exception e) { + if (e.getCause() instanceof GeneralSecurityException) { + throw (GeneralSecurityException) e.getCause(); + } else if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e); } } - public static X509CRL getCrl() throws GeneralSecurityException { + public X509CRL getCrl() throws GeneralSecurityException { try { - return CRL_CACHE.get(); - } catch (RuntimeException e) { - propagateIfInstanceOf(e.getCause(), GeneralSecurityException.class); - throw e; + return CRL_CACHE.get(tmchCaTestingMode); + } catch (Exception e) { + if (e.getCause() instanceof GeneralSecurityException) { + throw (GeneralSecurityException) e.getCause(); + } else if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e); } } } diff --git a/java/google/registry/tmch/TmchCrlAction.java b/java/google/registry/tmch/TmchCrlAction.java index 8d3554e15..95fdb971f 100644 --- a/java/google/registry/tmch/TmchCrlAction.java +++ b/java/google/registry/tmch/TmchCrlAction.java @@ -31,14 +31,16 @@ public final class TmchCrlAction implements Runnable { @Inject Marksdb marksdb; @Inject @Config("tmchCrlUrl") URL tmchCrlUrl; + @Inject TmchCertificateAuthority tmchCertificateAuthority; @Inject TmchCrlAction() {} /** Synchronously fetches latest ICANN TMCH CRL and saves it to datastore. */ @Override public void run() { try { - TmchCertificateAuthority - .updateCrl(new String(marksdb.fetch(tmchCrlUrl, Optional.absent()), UTF_8)); + tmchCertificateAuthority.updateCrl( + new String(marksdb.fetch(tmchCrlUrl, Optional.absent()), UTF_8), + tmchCrlUrl.toString()); } catch (IOException | GeneralSecurityException e) { throw new RuntimeException("Failed to update ICANN TMCH CRL.", e); } diff --git a/java/google/registry/tmch/TmchXmlSignature.java b/java/google/registry/tmch/TmchXmlSignature.java index e50218a00..34386f04a 100644 --- a/java/google/registry/tmch/TmchXmlSignature.java +++ b/java/google/registry/tmch/TmchXmlSignature.java @@ -20,6 +20,7 @@ import static com.google.common.base.Throwables.getRootCause; import static com.google.common.base.Throwables.propagateIfInstanceOf; import static google.registry.xml.XmlTransformer.loadXmlSchemas; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -32,6 +33,7 @@ import java.security.cert.X509Certificate; import java.util.List; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; import javax.xml.crypto.AlgorithmMethod; import javax.xml.crypto.KeySelector; import javax.xml.crypto.KeySelectorException; @@ -56,6 +58,14 @@ import org.xml.sax.SAXException; @ThreadSafe public final class TmchXmlSignature { + @VisibleForTesting + final TmchCertificateAuthority tmchCertificateAuthority; + + @Inject + public TmchXmlSignature(TmchCertificateAuthority tmchCertificateAuthority) { + this.tmchCertificateAuthority = tmchCertificateAuthority; + } + private static final Schema SCHEMA = loadXmlSchemas(ImmutableList.of("mark.xsd", "dsig.xsd", "smd.xsd")); @@ -66,19 +76,15 @@ public final class TmchXmlSignature { * cryptographic stuff. * * @throws GeneralSecurityException for unsupported protocols, certs not signed by the TMCH, - * incorrect keys, and for invalid, old, not-yet-valid or revoked certificates. + * incorrect keys, and for invalid, old, not-yet-valid or revoked certificates. * @throws IOException * @throws MarshalException * @throws ParserConfigurationException * @throws SAXException */ - public static void verify(byte[] smdXml) - throws GeneralSecurityException, - IOException, - MarshalException, - ParserConfigurationException, - SAXException, - XMLSignatureException { + public void verify(byte[] smdXml) + throws GeneralSecurityException, IOException, MarshalException, ParserConfigurationException, + SAXException, XMLSignatureException { checkArgument(smdXml.length > 0); Document doc = parseSmdDocument(new ByteArrayInputStream(smdXml)); @@ -87,7 +93,7 @@ public final class TmchXmlSignature { throw new XMLSignatureException("Expected exactly one element."); } XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM"); - KeyValueKeySelector selector = new KeyValueKeySelector(); + KeyValueKeySelector selector = new KeyValueKeySelector(tmchCertificateAuthority); DOMValidateContext context = new DOMValidateContext(selector, signatureNodes.item(0)); XMLSignature signature = factory.unmarshalXMLSignature(context); @@ -134,13 +140,21 @@ public final class TmchXmlSignature { /** Callback class for DOM validator checks validity of {@code } elements. */ private static final class KeyValueKeySelector extends KeySelector { + + private final TmchCertificateAuthority tmchCertificateAuthority; + + KeyValueKeySelector(TmchCertificateAuthority tmchCertificateAuthority) { + this.tmchCertificateAuthority = tmchCertificateAuthority; + } + @Nullable @Override public KeySelectorResult select( @Nullable KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, - XMLCryptoContext context) throws KeySelectorException { + XMLCryptoContext context) + throws KeySelectorException { if (keyInfo == null) { return null; } @@ -151,7 +165,7 @@ public final class TmchXmlSignature { if (x509DataChild instanceof X509Certificate) { X509Certificate cert = (X509Certificate) x509DataChild; try { - TmchCertificateAuthority.verify(cert); + tmchCertificateAuthority.verify(cert); } catch (SignatureException e) { throw new KeySelectorException(new CertificateSignatureException(e.getMessage())); } catch (GeneralSecurityException e) { diff --git a/java/google/registry/tools/GenerateApplicationsReportCommand.java b/java/google/registry/tools/GenerateApplicationsReportCommand.java index 76cedf277..aed4c8031 100644 --- a/java/google/registry/tools/GenerateApplicationsReportCommand.java +++ b/java/google/registry/tools/GenerateApplicationsReportCommand.java @@ -71,8 +71,8 @@ final class GenerateApplicationsReportCommand implements RemoteApiCommand { validateWith = PathParameter.OutputFile.class) private Path output = Paths.get("/dev/stdout"); - @Inject - Clock clock; + @Inject Clock clock; + @Inject TmchXmlSignature tmchXmlSignature; @Override public void run() throws Exception { @@ -143,7 +143,7 @@ final class GenerateApplicationsReportCommand implements RemoteApiCommand { } try { - TmchXmlSignature.verify(signedMarkData); + tmchXmlSignature.verify(signedMarkData); } catch (Exception e) { return Optional.of( makeLine(domainApplication, String.format("Invalid SMD (%s)", e.getMessage()))); diff --git a/javatests/google/registry/flows/CheckApiActionTest.java b/javatests/google/registry/flows/CheckApiActionTest.java index 7ce13537a..1045a7e25 100644 --- a/javatests/google/registry/flows/CheckApiActionTest.java +++ b/javatests/google/registry/flows/CheckApiActionTest.java @@ -26,7 +26,6 @@ import google.registry.flows.EppTestComponent.FakesAndMocksModule; import google.registry.model.registrar.Registrar; import google.registry.model.registry.Registry; import google.registry.testing.AppEngineRule; -import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; import java.util.Map; import org.json.simple.JSONValue; @@ -63,7 +62,7 @@ public class CheckApiActionTest { action.response = new FakeResponse(); action.config = RegistryEnvironment.UNITTEST.config(); action.eppController = DaggerEppTestComponent.builder() - .fakesAndMocksModule(new FakesAndMocksModule(new FakeClock())) + .fakesAndMocksModule(new FakesAndMocksModule()) .build() .startRequest() .eppController(); diff --git a/javatests/google/registry/flows/EppCommitLogsTest.java b/javatests/google/registry/flows/EppCommitLogsTest.java index 37bf322bd..6988a3e03 100644 --- a/javatests/google/registry/flows/EppCommitLogsTest.java +++ b/javatests/google/registry/flows/EppCommitLogsTest.java @@ -71,7 +71,7 @@ public class EppCommitLogsTest extends ShardableTestCase { SessionMetadata sessionMetadata = new HttpSessionMetadata(new FakeHttpSession()); sessionMetadata.setClientId("TheRegistrar"); DaggerEppTestComponent.builder() - .fakesAndMocksModule(new FakesAndMocksModule(clock)) + .fakesAndMocksModule(new FakesAndMocksModule(clock, true)) .build() .startRequest() .flowComponentBuilder() diff --git a/javatests/google/registry/flows/EppTestCase.java b/javatests/google/registry/flows/EppTestCase.java index 6d985f8b1..573c393b5 100644 --- a/javatests/google/registry/flows/EppTestCase.java +++ b/javatests/google/registry/flows/EppTestCase.java @@ -114,7 +114,7 @@ public class EppTestCase extends ShardableTestCase { FakeResponse response = new FakeResponse(); handler.response = response; handler.eppController = DaggerEppTestComponent.builder() - .fakesAndMocksModule(new FakesAndMocksModule(clock)) + .fakesAndMocksModule(new FakesAndMocksModule(clock, true)) .build() .startRequest() .eppController(); diff --git a/javatests/google/registry/flows/EppTestComponent.java b/javatests/google/registry/flows/EppTestComponent.java index 3a0f0c4bd..5dc00a463 100644 --- a/javatests/google/registry/flows/EppTestComponent.java +++ b/javatests/google/registry/flows/EppTestComponent.java @@ -31,6 +31,8 @@ import google.registry.monitoring.whitebox.EppMetric; import google.registry.request.RequestScope; import google.registry.testing.FakeClock; import google.registry.testing.FakeSleeper; +import google.registry.tmch.TmchCertificateAuthority; +import google.registry.tmch.TmchXmlSignature; import google.registry.util.Clock; import google.registry.util.Sleeper; import javax.inject.Singleton; @@ -58,9 +60,15 @@ interface EppTestComponent { final ModulesService modulesService; final Sleeper sleeper; - FakesAndMocksModule(FakeClock clock) { + FakesAndMocksModule() { + this(new FakeClock(), true); + } + + FakesAndMocksModule(FakeClock clock, boolean tmchCaTestingMode) { this.clock = clock; - this.domainFlowTmchUtils = new DomainFlowTmchUtils(); + this.domainFlowTmchUtils = + new DomainFlowTmchUtils( + new TmchXmlSignature(new TmchCertificateAuthority(tmchCaTestingMode))); this.sleeper = new FakeSleeper(clock); this.dnsQueue = DnsQueue.create(); this.metricBuilder = EppMetric.builderForRequest("request-id-1", clock); diff --git a/javatests/google/registry/flows/FlowTestCase.java b/javatests/google/registry/flows/FlowTestCase.java index ef6e048a9..7397c6e02 100644 --- a/javatests/google/registry/flows/FlowTestCase.java +++ b/javatests/google/registry/flows/FlowTestCase.java @@ -277,7 +277,7 @@ public abstract class FlowTestCase extends ShardableTestCase { .isEqualTo(new TypeInstantiator(getClass()){}.getExactType()); // Run the flow. return DaggerEppTestComponent.builder() - .fakesAndMocksModule(new FakesAndMocksModule(clock)) + .fakesAndMocksModule(new FakesAndMocksModule(clock, tmchCaTestingMode)) .build() .startRequest() .flowComponentBuilder() @@ -339,6 +339,12 @@ public abstract class FlowTestCase extends ShardableTestCase { return output; } + private boolean tmchCaTestingMode = true; + + protected void useTmchProdCert() { + tmchCaTestingMode = false; + } + public EppOutput dryRunFlowAssertResponse(String xml, String... ignoredPaths) throws Exception { List beforeEntities = ofy().load().list(); EppOutput output = diff --git a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java index a2c88d988..ffaca4f2c 100644 --- a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java @@ -282,7 +282,6 @@ public class DomainApplicationCreateFlowTest setEppInput("domain_create_sunrush_encoded_signed_mark.xml"); persistContactsAndHosts(); clock.advanceOneMilli(); - clock.setTo(DateTime.parse("2012-07-26T00:01:00Z")); clock.setTo(DateTime.parse("2012-07-22T00:01:00Z")); thrown.expect(SignedMarkCertificateNotYetValidException.class); runFlow(); @@ -291,7 +290,7 @@ public class DomainApplicationCreateFlowTest @Test @Ignore("I'm not sure how to get this to throw without creating a custom CA / certs") public void testFailure_signedMarkCertificateCorrupt() throws Exception { - configRule.useTmchProdCert(); + useTmchProdCert(); createTld("tld", TldState.SUNRUSH); setEppInput("domain_create_sunrush_encoded_signed_mark_certificate_corrupt.xml"); persistContactsAndHosts(); @@ -302,7 +301,7 @@ public class DomainApplicationCreateFlowTest @Test public void testFailure_signedMarkCertificateSignature() throws Exception { - configRule.useTmchProdCert(); + useTmchProdCert(); createTld("tld", TldState.SUNRUSH); setEppInput("domain_create_sunrush_encoded_signed_mark.xml"); persistContactsAndHosts(); diff --git a/javatests/google/registry/model/schema.txt b/javatests/google/registry/model/schema.txt index 3e7506a9a..4e42eab36 100644 --- a/javatests/google/registry/model/schema.txt +++ b/javatests/google/registry/model/schema.txt @@ -851,6 +851,7 @@ class google.registry.model.tmch.TmchCrl { @Id long id; @Parent com.googlecode.objectify.Key parent; java.lang.String crl; + java.lang.String url; org.joda.time.DateTime updated; } class google.registry.model.transfer.TransferData { diff --git a/javatests/google/registry/model/tmch/TmchCrlTest.java b/javatests/google/registry/model/tmch/TmchCrlTest.java index 109cb4003..f80d3121b 100644 --- a/javatests/google/registry/model/tmch/TmchCrlTest.java +++ b/javatests/google/registry/model/tmch/TmchCrlTest.java @@ -32,7 +32,7 @@ public class TmchCrlTest { @SuppressWarnings("null") public void testSuccess() throws Exception { assertThat(TmchCrl.get()).isNull(); - TmchCrl.set("lolcat"); + TmchCrl.set("lolcat", "http://lol.cat"); assertThat(TmchCrl.get().getCrl()).isEqualTo("lolcat"); } } diff --git a/javatests/google/registry/testing/RegistryConfigRule.java b/javatests/google/registry/testing/RegistryConfigRule.java index 7095e4dde..9e7e3b4c0 100644 --- a/javatests/google/registry/testing/RegistryConfigRule.java +++ b/javatests/google/registry/testing/RegistryConfigRule.java @@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Optional; import google.registry.config.RegistryConfig; import google.registry.config.RegistryEnvironment; -import google.registry.config.TestRegistryConfig; import org.junit.rules.ExternalResource; /** JUnit Rule for overriding Nomulus configuration values. */ @@ -42,15 +41,6 @@ public final class RegistryConfigRule extends ExternalResource { RegistryEnvironment.overrideConfigurationForTesting(checkNotNull(override)); } - /** Override registry configuration to use TMCH production CA. */ - public void useTmchProdCert() { - override(new TestRegistryConfig() { - @Override - public boolean getTmchCaTestingMode() { - return false; - }}); - } - @Override protected void before() throws Exception { if (override.isPresent()) { diff --git a/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java b/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java index 3438707a6..9b309a3a9 100644 --- a/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java +++ b/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java @@ -23,7 +23,6 @@ import google.registry.testing.AppEngineRule; import google.registry.testing.ExceptionRule; import google.registry.testing.FakeClock; import google.registry.testing.InjectRule; -import google.registry.testing.RegistryConfigRule; import java.security.SignatureException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; @@ -53,9 +52,6 @@ public class TmchCertificateAuthorityTest { @Rule public final InjectRule inject = new InjectRule(); - @Rule - public final RegistryConfigRule configRule = new RegistryConfigRule(); - private FakeClock clock = new FakeClock(DateTime.parse("2014-01-01T00:00:00Z")); @Before @@ -65,45 +61,49 @@ public class TmchCertificateAuthorityTest { @Test public void testFailure_prodRootExpired() throws Exception { - configRule.useTmchProdCert(); + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false); clock.setTo(DateTime.parse("2024-01-01T00:00:00Z")); thrown.expectRootCause( CertificateExpiredException.class, "NotAfter: Sun Jul 23 23:59:59 UTC 2023"); - TmchCertificateAuthority.getRoot(); + tmchCertificateAuthority.getRoot(); } @Test public void testFailure_prodRootNotYetValid() throws Exception { - configRule.useTmchProdCert(); + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false); clock.setTo(DateTime.parse("2000-01-01T00:00:00Z")); thrown.expectRootCause(CertificateNotYetValidException.class, "NotBefore: Wed Jul 24 00:00:00 UTC 2013"); - TmchCertificateAuthority.getRoot(); + tmchCertificateAuthority.getRoot(); } @Test public void testFailure_crlDoesntMatchCerts() throws Exception { // Use the prod cl, which won't match our test certificate. - TmchCrl.set(readResourceUtf8(TmchCertificateAuthority.class, "icann-tmch.crl")); + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true); + TmchCrl.set( + readResourceUtf8(TmchCertificateAuthority.class, "icann-tmch.crl"), "http://cert.crl"); thrown.expectRootCause(SignatureException.class, "Signature does not match"); - TmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); + tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); } @Test public void testSuccess_verify() throws Exception { - TmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true); + tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); } @Test public void testFailure_verifySignatureDoesntMatch() throws Exception { - configRule.useTmchProdCert(); + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false); thrown.expectRootCause(SignatureException.class, "Signature does not match"); - TmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); + tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)); } @Test public void testFailure_verifyRevoked() throws Exception { + TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true); thrown.expect(CertificateRevokedException.class, "revoked, reason: KEY_COMPROMISE"); - TmchCertificateAuthority.verify(loadCertificate(REVOKED_TEST_CERTIFICATE)); + tmchCertificateAuthority.verify(loadCertificate(REVOKED_TEST_CERTIFICATE)); } } diff --git a/javatests/google/registry/tmch/TmchCrlActionTest.java b/javatests/google/registry/tmch/TmchCrlActionTest.java index 0686340f7..566dfcac9 100644 --- a/javatests/google/registry/tmch/TmchCrlActionTest.java +++ b/javatests/google/registry/tmch/TmchCrlActionTest.java @@ -30,9 +30,10 @@ import org.junit.Test; /** Unit tests for {@link TmchCrlAction}. */ public class TmchCrlActionTest extends TmchActionTestCase { - private TmchCrlAction newTmchCrlAction() throws MalformedURLException { + private TmchCrlAction newTmchCrlAction(boolean tmchCaTestingMode) throws MalformedURLException { TmchCrlAction action = new TmchCrlAction(); action.marksdb = marksdb; + action.tmchCertificateAuthority = new TmchCertificateAuthority(tmchCaTestingMode); action.tmchCrlUrl = new URL("http://sloth.lol/tmch.crl"); return action; } @@ -40,10 +41,9 @@ public class TmchCrlActionTest extends TmchActionTestCase { @Test public void testSuccess() throws Exception { clock.setTo(DateTime.parse("2013-07-24TZ")); - configRule.useTmchProdCert(); when(httpResponse.getContent()).thenReturn( readResourceBytes(TmchCertificateAuthority.class, "icann-tmch.crl").read()); - newTmchCrlAction().run(); + newTmchCrlAction(false).run(); verify(httpResponse).getContent(); verify(fetchService).fetch(httpRequest.capture()); assertThat(httpRequest.getValue().getURL().toString()).isEqualTo("http://sloth.lol/tmch.crl"); @@ -52,11 +52,11 @@ public class TmchCrlActionTest extends TmchActionTestCase { @Test public void testFailure_crlTooOld() throws Exception { clock.setTo(DateTime.parse("2020-01-01TZ")); - configRule.useTmchProdCert(); when(httpResponse.getContent()).thenReturn( readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-test.crl").read()); + TmchCrlAction action = newTmchCrlAction(false); thrown.expectRootCause(CRLException.class, "New CRL is more out of date than our current CRL."); - newTmchCrlAction().run(); + action.run(); } @Test @@ -65,7 +65,7 @@ public class TmchCrlActionTest extends TmchActionTestCase { when(httpResponse.getContent()).thenReturn( readResourceBytes(TmchCertificateAuthority.class, "icann-tmch.crl").read()); thrown.expectRootCause(SignatureException.class, "Signature does not match."); - newTmchCrlAction().run(); + newTmchCrlAction(true).run(); } @Test @@ -74,6 +74,6 @@ public class TmchCrlActionTest extends TmchActionTestCase { when(httpResponse.getContent()).thenReturn( readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-test.crl").read()); thrown.expectRootCause(CertificateNotYetValidException.class); - newTmchCrlAction().run(); + newTmchCrlAction(true).run(); } } diff --git a/javatests/google/registry/tmch/TmchXmlSignatureTest.java b/javatests/google/registry/tmch/TmchXmlSignatureTest.java index 17ad45a7d..7137b2729 100644 --- a/javatests/google/registry/tmch/TmchXmlSignatureTest.java +++ b/javatests/google/registry/tmch/TmchXmlSignatureTest.java @@ -57,17 +57,19 @@ public class TmchXmlSignatureTest { private final FakeClock clock = new FakeClock(DateTime.parse("2013-11-24T23:15:37.4Z")); private byte[] smdData; + private TmchXmlSignature tmchXmlSignature; @Before public void before() throws Exception { inject.setStaticField(TmchCertificateAuthority.class, "clock", clock); + tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(true)); } public void wrongCertificateAuthority() throws Exception { - configRule.useTmchProdCert(); + tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(false)); smdData = loadSmd("active/Court-Agent-Arabic-Active.smd"); thrown.expectRootCause(SignatureException.class, "Signature does not match"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test @@ -75,7 +77,7 @@ public class TmchXmlSignatureTest { smdData = loadSmd("active/Court-Agent-Arabic-Active.smd"); clock.setTo(DateTime.parse("2013-05-01T00:00:00Z")); thrown.expectRootCause(CertificateNotYetValidException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test @@ -83,256 +85,256 @@ public class TmchXmlSignatureTest { smdData = loadSmd("active/Court-Agent-Arabic-Active.smd"); clock.setTo(DateTime.parse("2023-06-01T00:00:00Z")); thrown.expectRootCause(CertificateExpiredException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtAgentArabicActive() throws Exception { smdData = loadSmd("active/Court-Agent-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtAgentChineseActive() throws Exception { smdData = loadSmd("active/Court-Agent-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtAgentEnglishActive() throws Exception { smdData = loadSmd("active/Court-Agent-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtAgentFrenchActive() throws Exception { smdData = loadSmd("active/Court-Agent-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtAgentRussianActive() throws Exception { smdData = loadSmd("active/Court-Agent-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtHolderArabicActive() throws Exception { smdData = loadSmd("active/Court-Holder-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtHolderChineseActive() throws Exception { smdData = loadSmd("active/Court-Holder-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtHolderEnglishActive() throws Exception { smdData = loadSmd("active/Court-Holder-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtHolderFrenchActive() throws Exception { smdData = loadSmd("active/Court-Holder-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveCourtHolderRussianActive() throws Exception { smdData = loadSmd("active/Court-Holder-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkAgentArabicActive() throws Exception { smdData = loadSmd("active/Trademark-Agent-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkAgentChineseActive() throws Exception { smdData = loadSmd("active/Trademark-Agent-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkAgentEnglishActive() throws Exception { smdData = loadSmd("active/Trademark-Agent-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkAgentFrenchActive() throws Exception { smdData = loadSmd("active/Trademark-Agent-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkAgentRussianActive() throws Exception { smdData = loadSmd("active/Trademark-Agent-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkHolderArabicActive() throws Exception { smdData = loadSmd("active/Trademark-Holder-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkHolderChineseActive() throws Exception { smdData = loadSmd("active/Trademark-Holder-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkHolderEnglishActive() throws Exception { smdData = loadSmd("active/Trademark-Holder-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkHolderFrenchActive() throws Exception { smdData = loadSmd("active/Trademark-Holder-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTrademarkHolderRussianActive() throws Exception { smdData = loadSmd("active/Trademark-Holder-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteAgentArabicActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Agent-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteAgentChineseActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Agent-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteAgentEnglishActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Agent-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteAgentFrenchActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Agent-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteAgentRussianActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Agent-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteHolderArabicActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Holder-Arabic-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteHolderChineseActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Holder-Chinese-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteHolderEnglishActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Holder-English-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteHolderFrenchActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Holder-French-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testActiveTreatystatuteHolderRussianActive() throws Exception { smdData = loadSmd("active/TreatyStatute-Holder-Russian-Active.smd"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testInvalidInvalidsignatureCourtAgentFrenchActive() throws Exception { smdData = loadSmd("invalid/InvalidSignature-Court-Agent-French-Active.smd"); thrown.expect(XMLSignatureException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testInvalidInvalidsignatureTrademarkAgentEnglishActive() throws Exception { smdData = loadSmd("invalid/InvalidSignature-Trademark-Agent-English-Active.smd"); thrown.expect(XMLSignatureException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testInvalidInvalidsignatureTrademarkAgentRussianActive() throws Exception { smdData = loadSmd("invalid/InvalidSignature-Trademark-Agent-Russian-Active.smd"); thrown.expect(XMLSignatureException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testInvalidInvalidsignatureTreatystatuteAgentChineseActive() throws Exception { smdData = loadSmd("invalid/InvalidSignature-TreatyStatute-Agent-Chinese-Active.smd"); thrown.expect(XMLSignatureException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testInvalidInvalidsignatureTreatystatuteAgentEnglishActive() throws Exception { smdData = loadSmd("invalid/InvalidSignature-TreatyStatute-Agent-English-Active.smd"); thrown.expect(XMLSignatureException.class); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testRevokedTmvTmvrevokedCourtAgentFrenchActive() throws Exception { smdData = loadSmd("revoked/tmv/TMVRevoked-Court-Agent-French-Active.smd"); thrown.expectRootCause(CertificateRevokedException.class, "KEY_COMPROMISE"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testRevokedTmvTmvrevokedTrademarkAgentEnglishActive() throws Exception { smdData = loadSmd("revoked/tmv/TMVRevoked-Trademark-Agent-English-Active.smd"); thrown.expectRootCause(CertificateRevokedException.class, "KEY_COMPROMISE"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testRevokedTmvTmvrevokedTrademarkAgentRussianActive() throws Exception { smdData = loadSmd("revoked/tmv/TMVRevoked-Trademark-Agent-Russian-Active.smd"); thrown.expectRootCause(CertificateRevokedException.class, "KEY_COMPROMISE"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testRevokedTmvTmvrevokedTreatystatuteAgentChineseActive() throws Exception { smdData = loadSmd("revoked/tmv/TMVRevoked-TreatyStatute-Agent-Chinese-Active.smd"); thrown.expectRootCause(CertificateRevokedException.class, "KEY_COMPROMISE"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } @Test public void testRevokedTmvTmvrevokedTreatystatuteAgentEnglishActive() throws Throwable { smdData = loadSmd("revoked/tmv/TMVRevoked-TreatyStatute-Agent-English-Active.smd"); thrown.expectRootCause(CertificateRevokedException.class, "KEY_COMPROMISE"); - TmchXmlSignature.verify(smdData); + tmchXmlSignature.verify(smdData); } } diff --git a/javatests/google/registry/tools/UpdateSmdCommandTest.java b/javatests/google/registry/tools/UpdateSmdCommandTest.java index 8d8944010..3ef66d595 100644 --- a/javatests/google/registry/tools/UpdateSmdCommandTest.java +++ b/javatests/google/registry/tools/UpdateSmdCommandTest.java @@ -34,7 +34,9 @@ import google.registry.model.domain.DomainApplication; import google.registry.model.reporting.HistoryEntry; import google.registry.model.smd.EncodedSignedMark; import google.registry.model.smd.SignedMarkRevocationList; +import google.registry.tmch.TmchCertificateAuthority; import google.registry.tmch.TmchData; +import google.registry.tmch.TmchXmlSignature; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -66,7 +68,8 @@ public class UpdateSmdCommandTest extends CommandTestCase { .setCurrentSponsorClientId("TheRegistrar") .setEncodedSignedMarks(ImmutableList.of(EncodedSignedMark.create("base64", "garbage"))) .build()); - command.tmchUtils = new DomainFlowTmchUtils(); + command.tmchUtils = + new DomainFlowTmchUtils(new TmchXmlSignature(new TmchCertificateAuthority(true))); } private DomainApplication reloadDomainApplication() {