Skip to content
Merged
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 @@ -23,6 +23,7 @@
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
Expand Down Expand Up @@ -73,6 +74,7 @@
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Create;
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
Expand Down Expand Up @@ -341,17 +343,24 @@ static void validateDsData(Set<DomainDsData> dsData) throws EppException {
}
ImmutableList<DomainDsData> invalidAlgorithms =
dsData.stream()
.filter(ds -> !validateAlgorithm(ds.getAlgorithm()))
.filter(ds -> algorithmIsInvalid(ds.getAlgorithm()))
.collect(toImmutableList());
if (!invalidAlgorithms.isEmpty()) {
throw new InvalidDsRecordException(
String.format(
"Domain contains DS record(s) with an invalid algorithm wire value: %s",
invalidAlgorithms));
}
boolean forbidInsecureTypes = FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904);
ImmutableList<DomainDsData> invalidDigestTypes =
dsData.stream()
.filter(ds -> DigestType.fromWireValue(ds.getDigestType()).isEmpty())
.filter(
ds -> {
Optional<DigestType> digestType = DigestType.fromWireValue(ds.getDigestType());
return digestType
.map(type -> forbidInsecureTypes && !type.isAllowedInRfc9904())
.orElse(true);
})
.collect(toImmutableList());
if (!invalidDigestTypes.isEmpty()) {
throw new InvalidDsRecordException(
Expand All @@ -376,14 +385,14 @@ static void validateDsData(Set<DomainDsData> dsData) throws EppException {
}
}

public static boolean validateAlgorithm(int alg) {
public static boolean algorithmIsInvalid(int alg) {
if (alg > 255 || alg < 0) {
return false;
return true;
}
// Algorithms that are reserved or unassigned will just return a string representation of their
// integer wire value.
String algorithm = Algorithm.string(alg);
return !algorithm.equals(Integer.toString(alg));
return algorithm.equals(Integer.toString(alg));
}

/** We only allow specifying years in a period. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ public enum FeatureName {
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE),

/** If we're prohibiting the inclusion of the contact object URI on login. */
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE);
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE),

/** If we're prohibiting insecure algorithms as detailed by RFC 9904. */
FORBID_INSECURE_ALGORITHMS_RFC_9904(FeatureStatus.INACTIVE);

private final FeatureStatus defaultStatus;

Expand Down
18 changes: 14 additions & 4 deletions core/src/main/java/google/registry/tools/DigestType.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,26 @@
* https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
*/
public enum DigestType {
SHA1(1, 20),
SHA256(2, 32),
// Algorithm number 1 is SHA-1 and will be is deliberately NOT SUPPORTED.
// RFC 9904 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// This prohibition is gated behind a feature flag.
SHA1(1, 20, false),
SHA256(2, 32, true),
// Algorithm number 3 is GOST R 34.11-94 and is deliberately NOT SUPPORTED.
// This algorithm was reviewed by ise-crypto and deemed academically broken (b/207029800).
// In addition, RFC 8624 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// TODO(sarhabot@): Add note in Cloud DNS code to notify the Registry of any new changes to
// supported digest types.
SHA384(4, 48);
SHA384(4, 48, true);

private final int wireValue;
private final int bytes;
private final boolean allowedInRfc9904;

DigestType(int wireValue, int bytes) {
DigestType(int wireValue, int bytes, boolean allowedInRfc9904) {
this.wireValue = wireValue;
this.bytes = bytes;
this.allowedInRfc9904 = allowedInRfc9904;
}

private static final ImmutableMap<Integer, DigestType> WIRE_VALUE_TO_DIGEST_TYPE =
Expand All @@ -63,4 +68,9 @@ public int getWireValue() {
public int getBytes() {
return bytes;
}

/** Whether this digest type is supported as of RFC 9904. */
public boolean isAllowedInRfc9904() {
return allowedInRfc9904;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/google/registry/tools/DsRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,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.validateAlgorithm(alg)) {
if (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 @@ -24,6 +24,7 @@
import static google.registry.model.billing.BillingBase.Flag.SUNRISE;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
Expand Down Expand Up @@ -150,6 +151,8 @@
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
Expand Down Expand Up @@ -794,7 +797,11 @@ void testSuccess_secDns() throws Exception {
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))
12345,
3,
2,
base16()
.decode("D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A"))
.cloneWithDomainRepoId(domain.getRepoId()));
}

Expand Down Expand Up @@ -957,6 +964,38 @@ void testFailure_secDnsInvalidDigestType() throws Exception {
assertAboutEppExceptions().that(thrown).marshalsToXml();
}

@Test
void testFailure_secDnsSha1DigestType() throws Exception {
setEppInput("domain_create_dsdata_sha1.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_secDnsSha1_flagInactive() throws Exception {
setEppInput("domain_create_dsdata_sha1.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, 3, 1, base16().decode("49FD46E6C4B45C55D4AC49FD46E6C4B45C55D4AC"))
.cloneWithDomainRepoId(domain.getRepoId()));
}

@Test
void testFailure_secDnsInvalidAlgorithm() throws Exception {
setEppInput("domain_create_dsdata_bad_algorithms.xml");
Expand Down
Loading
Loading