Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ public static boolean algorithmIsInvalid(int alg) {
if (alg > 255 || alg < 0) {
return true;
}
if (DomainDsData.FORBIDDEN_ALGORITHMS.contains(alg)
&& FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904)) {
return true;
}
// Algorithms that are reserved or unassigned will just return a string representation of their
// integer wire value.
String algorithm = Algorithm.string(alg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package google.registry.model.domain.secdns;

import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import jakarta.persistence.Access;
Expand All @@ -26,13 +27,24 @@
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.xbill.DNS.DNSSEC.Algorithm;

/** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */
@MappedSuperclass
@Access(AccessType.FIELD)
@XmlType(propOrder = {"keyTag", "algorithm", "digestType", "digest"})
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {

// A set of algorithms that we do not allow. See RFC 9904 for more details.
public static final ImmutableSet<Integer> FORBIDDEN_ALGORITHMS =
ImmutableSet.of(
Algorithm.RSAMD5,
Algorithm.RSASHA1,
Algorithm.RSA_NSEC3_SHA1,
Algorithm.DSA,
Algorithm.DSA_NSEC3_SHA1,
Algorithm.ECC_GOST);

@XmlTransient @Transient @Insignificant String domainRepoId;

/** The identifier for this particular key in the domain. */
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/google/registry/tools/DsRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package google.registry.tools;

import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;

import com.beust.jcommander.IStringConverter;
Expand Down Expand Up @@ -46,7 +47,7 @@ private static DsRecord create(int keyTag, int alg, int digestType, String diges
String.format("DS record has an invalid digest length: %s", digest));
}

if (DomainFlowUtils.algorithmIsInvalid(alg)) {
if (tm().reTransact(() -> DomainFlowUtils.algorithmIsInvalid(alg))) {
throw new IllegalArgumentException(
String.format("DS record uses an unrecognized algorithm: %d", alg));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ void testSuccess_secDns() throws Exception {
.hasExactlyDsData(
DomainDsData.create(
12345,
3,
8,
2,
base16()
.decode("D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A"))
Expand Down Expand Up @@ -992,7 +992,7 @@ void testSuccess_secDnsSha1_flagInactive() throws Exception {
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC49FD46E6C4B45C55D4AC"))
12345, 8, 1, base16().decode("49FD46E6C4B45C55D4AC49FD46E6C4B45C55D4AC"))
.cloneWithDomainRepoId(domain.getRepoId()));
}

Expand All @@ -1004,6 +1004,42 @@ void testFailure_secDnsInvalidAlgorithm() throws Exception {
assertAboutEppExceptions().that(thrown).marshalsToXml();
}

@Test
void testFailure_secDnsForbiddenAlgorithm() throws Exception {
setEppInput("domain_create_dsdata_forbidden_algorithm.xml");
persistHosts();
DatabaseHelper.persistResource(
new FeatureFlag.Builder()
.setFeatureName(FORBID_INSECURE_ALGORITHMS_RFC_9904)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.ACTIVE))
.build());
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}

@Test
void testSuccess_secDnsForbiddenAlgorithm_flagInactive() throws Exception {
setEppInput("domain_create_dsdata_forbidden_algorithm.xml");
persistHosts();
DatabaseHelper.persistResource(
new FeatureFlag.Builder()
.setFeatureName(FORBID_INSECURE_ALGORITHMS_RFC_9904)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.INACTIVE))
.build());
doSuccessfulTest("tld");
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345,
1,
2,
base16()
.decode("D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A"))
.cloneWithDomainRepoId(domain.getRepoId()));
}

@Test
void testFailure_wrongExtension() {
setEppInput("domain_create_wrong_extension.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
"UNIT", "y");

private static final Pattern OK_PATTERN = Pattern.compile("\"ok\"");
private static final String SHA_256_DIGEST =
"D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A";

private Host host1;
private Host host2;
Expand Down Expand Up @@ -314,8 +316,7 @@ void testSuccess_secDns() throws Exception {
domain
.asBuilder()
.setDsData(
ImmutableSet.of(
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
ImmutableSet.of(DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))
.setNameservers(ImmutableSet.of(host1.createVKey(), host3.createVKey()))
.build());
doSuccessfulTest("domain_info_response_dsdata.xml", false);
Expand Down Expand Up @@ -519,8 +520,7 @@ void testSuccess_secDnsAndAddGracePeriod() throws Exception {
"TheRegistrar",
null))
.setDsData(
ImmutableSet.of(
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
ImmutableSet.of(DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))
.build());
doSuccessfulTest("domain_info_response_dsdata_addperiod.xml", false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
private static final ImmutableMap<String, String> OTHER_DSDATA_TEMPLATE_MAP =
ImmutableMap.of(
"KEY_TAG", "12346",
"ALG", "3",
"ALG", "8",
"DIGEST_TYPE", "2",
"DIGEST", SHA_256_DIGEST);

Expand Down Expand Up @@ -459,9 +459,9 @@ void testSuccess_secDnsAdd() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
null,
ImmutableSet.of(DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableMap.of(
"KEY_TAG", "12346", "ALG", "3", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
"KEY_TAG", "12346", "ALG", "8", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
true);
}

Expand All @@ -471,9 +471,9 @@ void testSuccess_secDnsAddPreservesExisting() throws Exception {
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(
SOME_DSDATA, DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableMap.of(
"KEY_TAG", "12346", "ALG", "3", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
"KEY_TAG", "12346", "ALG", "8", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
true);
}

Expand Down Expand Up @@ -648,7 +648,7 @@ void testSuccess_secDnsAddToMaxRecords() throws Exception {
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))))),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))))),
true);
}

Expand All @@ -657,7 +657,7 @@ void testSuccess_secDnsRemove() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(
SOME_DSDATA, DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(SOME_DSDATA),
true);
}
Expand All @@ -668,7 +668,7 @@ void testSuccess_secDnsRemoveAll() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem_all.xml",
ImmutableSet.of(
SOME_DSDATA, DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(),
true);
}
Expand All @@ -678,9 +678,9 @@ void testSuccess_secDnsAddRemove() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem.xml",
ImmutableSet.of(
SOME_DSDATA, DomainDsData.create(12345, 3, 2, base16().decode(SHA_256_DIGEST))),
SOME_DSDATA, DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(
SOME_DSDATA, DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
true);
}

Expand All @@ -703,12 +703,12 @@ void testSuccess_secDnsAddRemoveToMaxRecords() throws Exception {
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(12345, 3, 2, base16().decode(SHA_256_DIGEST))))),
DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))),
ImmutableSet.copyOf(
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(12346, 3, 2, base16().decode(SHA_256_DIGEST))))),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))))),
true);
}

Expand Down Expand Up @@ -990,6 +990,47 @@ void testFailure_secDnsInvalidAlgorithm() throws Exception {
assertAboutEppExceptions().that(thrown).marshalsToXml();
}

@Test
void testFailure_secDnsForbiddenAlgorithm() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
DatabaseHelper.persistResource(
new FeatureFlag.Builder()
.setFeatureName(FORBID_INSECURE_ALGORITHMS_RFC_9904)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.ACTIVE))
.build());
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}

@Test
void testSuccess_secDnsForbiddenAlgorithm_flagInactive() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
DatabaseHelper.persistResource(
new FeatureFlag.Builder()
.setFeatureName(FORBID_INSECURE_ALGORITHMS_RFC_9904)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.INACTIVE))
.build());
runFlow();
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST)),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST)));
}

@Test
void testFailure_secDnsMultipleInvalidAlgorithms() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@
package google.registry.tools;

import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static org.joda.money.CurrencyUnit.JPY;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.dns.writer.VoidDnsWriter;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.tld.Tld;
import google.registry.model.tld.label.PremiumListDao;
Expand All @@ -49,9 +54,9 @@ void testSuccess_complete() throws Exception {
"--period=1",
"--nameservers=ns1.zdns.google,ns2.zdns.google,ns3.zdns.google,ns4.zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 2"
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
Expand All @@ -63,9 +68,9 @@ void testSuccess_completeWithCanonicalization() throws Exception {
"--period=1",
"--nameservers=NS1.zdns.google,ns2.ZDNS.google,ns3.zdns.gOOglE,ns4.zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 2"
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
Expand All @@ -77,9 +82,9 @@ void testSuccess_completeWithSquareBrackets() throws Exception {
"--period=1",
"--nameservers=ns[1-4].zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 2"
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
Expand All @@ -91,9 +96,9 @@ void testSuccess_completeWithSquareBracketsAndCanonicalization() throws Exceptio
"--period=1",
"--nameservers=NS[1-4].zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 2"
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
Expand Down Expand Up @@ -279,6 +284,25 @@ void testFailure_invalidDigestType() {
assertThat(thrown).hasMessageThat().isEqualTo("DS record uses an unrecognized digest type: 3");
}

@Test
void testFailure_forbiddenAlgorithm() {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(FORBID_INSECURE_ALGORITHMS_RFC_9904)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.ACTIVE))
.build());
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--client=NewRegistrar",
"--ds_records=1 1 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld"));
assertThat(thrown).hasMessageThat().isEqualTo("DS record uses an unrecognized algorithm: 1");
}

@Test
void testFailure_invalidDigestLength() {
IllegalArgumentException thrown =
Expand Down
Loading
Loading