TMCH
@@ -1146,20 +1161,6 @@ public final class RegistryConfig {
return Duration.standardDays(30);
}
- /**
- * Returns {@code true} if TMCH certificate authority should be in testing mode.
- *
- * @see google.registry.tmch.TmchCertificateAuthority
- */
- public static boolean getTmchCaTestingMode() {
- switch (RegistryEnvironment.get()) {
- case PRODUCTION:
- return false;
- default:
- return true;
- }
- }
-
/**
* Returns the address of the Nomulus app HTTP server.
*
diff --git a/java/google/registry/tmch/TmchCertificateAuthority.java b/java/google/registry/tmch/TmchCertificateAuthority.java
index 56c881fc0..d5a2c2fdb 100644
--- a/java/google/registry/tmch/TmchCertificateAuthority.java
+++ b/java/google/registry/tmch/TmchCertificateAuthority.java
@@ -14,6 +14,7 @@
package google.registry.tmch;
+import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILOT;
import static google.registry.config.RegistryConfig.getSingletonCachePersistDuration;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.util.ResourceUtils.readResourceUtf8;
@@ -23,6 +24,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import google.registry.config.RegistryConfig.Config;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.model.tmch.TmchCrl;
import google.registry.util.Clock;
import google.registry.util.NonFinalForTesting;
@@ -39,23 +41,26 @@ import javax.inject.Inject;
/**
* 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
+ *
There are two CRLs, a real one for the production environment and a pilot 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.
+ *
+ *
The CRTs do not change and are included as files in the codebase that are not refreshed. They
+ * were downloaded from https://ca.icann.org/tmch.crt and https://ca.icann.org/tmch_pilot.crt
*/
@Immutable
@ThreadSafe
public final class TmchCertificateAuthority {
private static final String ROOT_CRT_FILE = "icann-tmch.crt";
- private static final String TEST_ROOT_CRT_FILE = "icann-tmch-test.crt";
+ private static final String ROOT_CRT_PILOT_FILE = "icann-tmch-pilot.crt";
private static final String CRL_FILE = "icann-tmch.crl";
- private static final String TEST_CRL_FILE = "icann-tmch-test.crl";
+ private static final String CRL_PILOT_FILE = "icann-tmch-pilot.crl";
- private boolean tmchCaTestingMode;
+ private final TmchCaMode tmchCaMode;
- public @Inject TmchCertificateAuthority(@Config("tmchCaTestingMode") boolean tmchCaTestingMode) {
- this.tmchCaTestingMode = tmchCaTestingMode;
+ public @Inject TmchCertificateAuthority(@Config("tmchCaMode") TmchCaMode tmchCaMode) {
+ this.tmchCaMode = tmchCaMode;
}
/**
@@ -65,28 +70,27 @@ public final class TmchCertificateAuthority {
* 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
+ * correspond to the correct CRL for the given TMCH CA mode because {@link TmchCrlAction} can
* only persist the correct one for this given environment.
*/
- private static final LoadingCache CRL_CACHE =
+ private static final LoadingCache CRL_CACHE =
CacheBuilder.newBuilder()
.expireAfterWrite(getSingletonCacheRefreshDuration().getMillis(), MILLISECONDS)
.build(
- new CacheLoader() {
+ new CacheLoader() {
@Override
- public X509CRL load(final Boolean tmchCaTestingMode)
- throws GeneralSecurityException {
+ public X509CRL load(final TmchCaMode tmchCaMode) throws GeneralSecurityException {
TmchCrl storedCrl = TmchCrl.get();
+ String crlContents;
+ if (storedCrl == null) {
+ String file = (tmchCaMode == PILOT) ? CRL_PILOT_FILE : CRL_FILE;
+ crlContents = readResourceUtf8(TmchCertificateAuthority.class, file);
+ } else {
+ crlContents = storedCrl.getCrl();
+ }
+ X509CRL crl = X509Utils.loadCrl(crlContents);
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());
+ crl.verify(ROOT_CACHE.get(tmchCaMode).getPublicKey());
return crl;
} catch (ExecutionException e) {
if (e.getCause() instanceof GeneralSecurityException) {
@@ -98,16 +102,15 @@ public final class TmchCertificateAuthority {
}});
/** A cached function that loads the CRT from a jar resource. */
- private static final LoadingCache ROOT_CACHE =
+ private static final LoadingCache ROOT_CACHE =
CacheBuilder.newBuilder()
.expireAfterWrite(getSingletonCachePersistDuration().getMillis(), MILLISECONDS)
.build(
- new CacheLoader() {
+ new CacheLoader() {
@Override
- public X509Certificate load(final Boolean tmchCaTestingMode)
+ public X509Certificate load(final TmchCaMode tmchCaMode)
throws GeneralSecurityException {
- String file =
- tmchCaTestingMode.booleanValue() ? TEST_ROOT_CRT_FILE : ROOT_CRT_FILE;
+ String file = (tmchCaMode == PILOT) ? ROOT_CRT_PILOT_FILE : ROOT_CRT_FILE;
X509Certificate root =
X509Utils.loadCertificate(
readResourceUtf8(TmchCertificateAuthority.class, file));
@@ -153,7 +156,7 @@ public final class TmchCertificateAuthority {
public X509Certificate getRoot() throws GeneralSecurityException {
try {
- return ROOT_CACHE.get(tmchCaTestingMode);
+ return ROOT_CACHE.get(tmchCaMode);
} catch (Exception e) {
if (e.getCause() instanceof GeneralSecurityException) {
throw (GeneralSecurityException) e.getCause();
@@ -166,7 +169,7 @@ public final class TmchCertificateAuthority {
public X509CRL getCrl() throws GeneralSecurityException {
try {
- return CRL_CACHE.get(tmchCaTestingMode);
+ return CRL_CACHE.get(tmchCaMode);
} catch (Exception e) {
if (e.getCause() instanceof GeneralSecurityException) {
throw (GeneralSecurityException) e.getCause();
diff --git a/java/google/registry/tmch/icann-tmch-test.crl b/java/google/registry/tmch/icann-tmch-pilot.crl
similarity index 100%
rename from java/google/registry/tmch/icann-tmch-test.crl
rename to java/google/registry/tmch/icann-tmch-pilot.crl
diff --git a/java/google/registry/tmch/icann-tmch-test.crt b/java/google/registry/tmch/icann-tmch-pilot.crt
similarity index 100%
rename from java/google/registry/tmch/icann-tmch-test.crt
rename to java/google/registry/tmch/icann-tmch-pilot.crt
diff --git a/javatests/google/registry/flows/EppCommitLogsTest.java b/javatests/google/registry/flows/EppCommitLogsTest.java
index 6988a3e03..b220b10be 100644
--- a/javatests/google/registry/flows/EppCommitLogsTest.java
+++ b/javatests/google/registry/flows/EppCommitLogsTest.java
@@ -25,6 +25,7 @@ import static org.joda.time.DateTimeZone.UTC;
import static org.joda.time.Duration.standardDays;
import com.googlecode.objectify.Key;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.domain.DomainResource;
import google.registry.model.ofy.Ofy;
@@ -71,7 +72,7 @@ public class EppCommitLogsTest extends ShardableTestCase {
SessionMetadata sessionMetadata = new HttpSessionMetadata(new FakeHttpSession());
sessionMetadata.setClientId("TheRegistrar");
DaggerEppTestComponent.builder()
- .fakesAndMocksModule(new FakesAndMocksModule(clock, true))
+ .fakesAndMocksModule(new FakesAndMocksModule(clock, TmchCaMode.PILOT))
.build()
.startRequest()
.flowComponentBuilder()
diff --git a/javatests/google/registry/flows/EppTestCase.java b/javatests/google/registry/flows/EppTestCase.java
index 573c393b5..2ba803eab 100644
--- a/javatests/google/registry/flows/EppTestCase.java
+++ b/javatests/google/registry/flows/EppTestCase.java
@@ -23,6 +23,7 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.net.MediaType;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.ofy.Ofy;
import google.registry.testing.FakeClock;
@@ -114,7 +115,7 @@ public class EppTestCase extends ShardableTestCase {
FakeResponse response = new FakeResponse();
handler.response = response;
handler.eppController = DaggerEppTestComponent.builder()
- .fakesAndMocksModule(new FakesAndMocksModule(clock, true))
+ .fakesAndMocksModule(new FakesAndMocksModule(clock, TmchCaMode.PILOT))
.build()
.startRequest()
.eppController();
diff --git a/javatests/google/registry/flows/EppTestComponent.java b/javatests/google/registry/flows/EppTestComponent.java
index 331c04bf3..6a9d9c62e 100644
--- a/javatests/google/registry/flows/EppTestComponent.java
+++ b/javatests/google/registry/flows/EppTestComponent.java
@@ -22,6 +22,7 @@ import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import google.registry.config.RegistryConfig.ConfigModule;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.dns.DnsQueue;
import google.registry.flows.custom.CustomLogicFactory;
import google.registry.flows.custom.TestCustomLogicFactory;
@@ -61,14 +62,13 @@ interface EppTestComponent {
final Sleeper sleeper;
FakesAndMocksModule() {
- this(new FakeClock(), true);
+ this(new FakeClock(), TmchCaMode.PILOT);
}
- FakesAndMocksModule(FakeClock clock, boolean tmchCaTestingMode) {
+ FakesAndMocksModule(FakeClock clock, TmchCaMode tmchCaMode) {
this.clock = clock;
this.domainFlowTmchUtils =
- new DomainFlowTmchUtils(
- new TmchXmlSignature(new TmchCertificateAuthority(tmchCaTestingMode)));
+ new DomainFlowTmchUtils(new TmchXmlSignature(new TmchCertificateAuthority(tmchCaMode)));
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 7397c6e02..5d42720a5 100644
--- a/javatests/google/registry/flows/FlowTestCase.java
+++ b/javatests/google/registry/flows/FlowTestCase.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.flows.picker.FlowPicker;
import google.registry.model.billing.BillingEvent;
@@ -277,7 +278,7 @@ public abstract class FlowTestCase extends ShardableTestCase {
.isEqualTo(new TypeInstantiator(getClass()){}.getExactType());
// Run the flow.
return DaggerEppTestComponent.builder()
- .fakesAndMocksModule(new FakesAndMocksModule(clock, tmchCaTestingMode))
+ .fakesAndMocksModule(new FakesAndMocksModule(clock, tmchCaMode))
.build()
.startRequest()
.flowComponentBuilder()
@@ -339,10 +340,10 @@ public abstract class FlowTestCase extends ShardableTestCase {
return output;
}
- private boolean tmchCaTestingMode = true;
+ private TmchCaMode tmchCaMode = TmchCaMode.PILOT;
protected void useTmchProdCert() {
- tmchCaTestingMode = false;
+ tmchCaMode = TmchCaMode.PRODUCTION;
}
public EppOutput dryRunFlowAssertResponse(String xml, String... ignoredPaths) throws Exception {
diff --git a/javatests/google/registry/tmch/BUILD b/javatests/google/registry/tmch/BUILD
index 712294655..b5365e053 100644
--- a/javatests/google/registry/tmch/BUILD
+++ b/javatests/google/registry/tmch/BUILD
@@ -17,6 +17,7 @@ java_library(
"testdata/*/*/*",
]),
deps = [
+ "//java/google/registry/config",
"//java/google/registry/model",
"//java/google/registry/request",
"//java/google/registry/tmch",
diff --git a/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java b/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java
index 9b309a3a9..61f45ee14 100644
--- a/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java
+++ b/javatests/google/registry/tmch/TmchCertificateAuthorityTest.java
@@ -14,6 +14,8 @@
package google.registry.tmch;
+import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILOT;
+import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PRODUCTION;
import static google.registry.tmch.TmchTestData.loadString;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import static google.registry.util.X509Utils.loadCertificate;
@@ -61,7 +63,7 @@ public class TmchCertificateAuthorityTest {
@Test
public void testFailure_prodRootExpired() throws Exception {
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PRODUCTION);
clock.setTo(DateTime.parse("2024-01-01T00:00:00Z"));
thrown.expectRootCause(
CertificateExpiredException.class, "NotAfter: Sun Jul 23 23:59:59 UTC 2023");
@@ -70,7 +72,7 @@ public class TmchCertificateAuthorityTest {
@Test
public void testFailure_prodRootNotYetValid() throws Exception {
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PRODUCTION);
clock.setTo(DateTime.parse("2000-01-01T00:00:00Z"));
thrown.expectRootCause(CertificateNotYetValidException.class,
"NotBefore: Wed Jul 24 00:00:00 UTC 2013");
@@ -80,7 +82,7 @@ public class TmchCertificateAuthorityTest {
@Test
public void testFailure_crlDoesntMatchCerts() throws Exception {
// Use the prod cl, which won't match our test certificate.
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PILOT);
TmchCrl.set(
readResourceUtf8(TmchCertificateAuthority.class, "icann-tmch.crl"), "http://cert.crl");
thrown.expectRootCause(SignatureException.class, "Signature does not match");
@@ -89,20 +91,20 @@ public class TmchCertificateAuthorityTest {
@Test
public void testSuccess_verify() throws Exception {
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PILOT);
tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE));
}
@Test
public void testFailure_verifySignatureDoesntMatch() throws Exception {
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(false);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PRODUCTION);
thrown.expectRootCause(SignatureException.class, "Signature does not match");
tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE));
}
@Test
public void testFailure_verifyRevoked() throws Exception {
- TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(true);
+ TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PILOT);
thrown.expect(CertificateRevokedException.class, "revoked, reason: KEY_COMPROMISE");
tmchCertificateAuthority.verify(loadCertificate(REVOKED_TEST_CERTIFICATE));
}
diff --git a/javatests/google/registry/tmch/TmchCrlActionTest.java b/javatests/google/registry/tmch/TmchCrlActionTest.java
index 566dfcac9..c053085b8 100644
--- a/javatests/google/registry/tmch/TmchCrlActionTest.java
+++ b/javatests/google/registry/tmch/TmchCrlActionTest.java
@@ -19,6 +19,7 @@ import static google.registry.util.ResourceUtils.readResourceBytes;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SignatureException;
@@ -30,10 +31,10 @@ import org.junit.Test;
/** Unit tests for {@link TmchCrlAction}. */
public class TmchCrlActionTest extends TmchActionTestCase {
- private TmchCrlAction newTmchCrlAction(boolean tmchCaTestingMode) throws MalformedURLException {
+ private TmchCrlAction newTmchCrlAction(TmchCaMode tmchCaMode) throws MalformedURLException {
TmchCrlAction action = new TmchCrlAction();
action.marksdb = marksdb;
- action.tmchCertificateAuthority = new TmchCertificateAuthority(tmchCaTestingMode);
+ action.tmchCertificateAuthority = new TmchCertificateAuthority(tmchCaMode);
action.tmchCrlUrl = new URL("http://sloth.lol/tmch.crl");
return action;
}
@@ -43,7 +44,7 @@ public class TmchCrlActionTest extends TmchActionTestCase {
clock.setTo(DateTime.parse("2013-07-24TZ"));
when(httpResponse.getContent()).thenReturn(
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch.crl").read());
- newTmchCrlAction(false).run();
+ newTmchCrlAction(TmchCaMode.PRODUCTION).run();
verify(httpResponse).getContent();
verify(fetchService).fetch(httpRequest.capture());
assertThat(httpRequest.getValue().getURL().toString()).isEqualTo("http://sloth.lol/tmch.crl");
@@ -53,8 +54,12 @@ public class TmchCrlActionTest extends TmchActionTestCase {
public void testFailure_crlTooOld() throws Exception {
clock.setTo(DateTime.parse("2020-01-01TZ"));
when(httpResponse.getContent()).thenReturn(
- readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-test.crl").read());
- TmchCrlAction action = newTmchCrlAction(false);
+ readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-pilot.crl").read());
+ // We use the pilot CRL here only because we know that it was generated more recently than the
+ // production CRL, and thus attempting to replace it with the production CRL will fail. It
+ // doesn't matter that the wrong CRT would be used to verify it because that check happens after
+ // the age check.
+ TmchCrlAction action = newTmchCrlAction(TmchCaMode.PRODUCTION);
thrown.expectRootCause(CRLException.class, "New CRL is more out of date than our current CRL.");
action.run();
}
@@ -65,15 +70,15 @@ 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(true).run();
+ newTmchCrlAction(TmchCaMode.PILOT).run();
}
@Test
public void testFailure_crlNotYetValid() throws Exception {
clock.setTo(DateTime.parse("1984-01-01TZ"));
when(httpResponse.getContent()).thenReturn(
- readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-test.crl").read());
+ readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-pilot.crl").read());
thrown.expectRootCause(CertificateNotYetValidException.class);
- newTmchCrlAction(true).run();
+ newTmchCrlAction(TmchCaMode.PILOT).run();
}
}
diff --git a/javatests/google/registry/tmch/TmchXmlSignatureTest.java b/javatests/google/registry/tmch/TmchXmlSignatureTest.java
index b5c99e059..26b7448cc 100644
--- a/javatests/google/registry/tmch/TmchXmlSignatureTest.java
+++ b/javatests/google/registry/tmch/TmchXmlSignatureTest.java
@@ -16,6 +16,7 @@ package google.registry.tmch;
import static google.registry.tmch.TmchTestData.loadSmd;
+import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.testing.AppEngineRule;
import google.registry.testing.ExceptionRule;
import google.registry.testing.FakeClock;
@@ -58,11 +59,11 @@ public class TmchXmlSignatureTest {
@Before
public void before() throws Exception {
inject.setStaticField(TmchCertificateAuthority.class, "clock", clock);
- tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(true));
+ tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(TmchCaMode.PILOT));
}
public void wrongCertificateAuthority() throws Exception {
- tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(false));
+ tmchXmlSignature = new TmchXmlSignature(new TmchCertificateAuthority(TmchCaMode.PRODUCTION));
smdData = loadSmd("active/Court-Agent-Arabic-Active.smd");
thrown.expectRootCause(SignatureException.class, "Signature does not match");
tmchXmlSignature.verify(smdData);
diff --git a/javatests/google/registry/tools/UpdateSmdCommandTest.java b/javatests/google/registry/tools/UpdateSmdCommandTest.java
index 3ef66d595..f00ae1782 100644
--- a/javatests/google/registry/tools/UpdateSmdCommandTest.java
+++ b/javatests/google/registry/tools/UpdateSmdCommandTest.java
@@ -15,6 +15,7 @@
package google.registry.tools;
import static com.google.common.io.BaseEncoding.base64;
+import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILOT;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newDomainApplication;
@@ -69,7 +70,7 @@ public class UpdateSmdCommandTest extends CommandTestCase {
.setEncodedSignedMarks(ImmutableList.of(EncodedSignedMark.create("base64", "garbage")))
.build());
command.tmchUtils =
- new DomainFlowTmchUtils(new TmchXmlSignature(new TmchCertificateAuthority(true)));
+ new DomainFlowTmchUtils(new TmchXmlSignature(new TmchCertificateAuthority(PILOT)));
}
private DomainApplication reloadDomainApplication() {