1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Use time-constant comparison in password testing (#3090)

Normal comparison exits out once a difference is found so theoretically
a time variance can leak information. This isn't really a huge deal but
is probably still worth doing.
This commit is contained in:
gbrodman
2026-06-17 17:00:13 -04:00
committed by GitHub
parent 4df4bf1489
commit 84e97aa2db
3 changed files with 10 additions and 5 deletions
@@ -44,6 +44,8 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
@@ -134,7 +136,9 @@ public final class ResourceFlowUtils {
}
String authPassword = authInfo.getPw().getValue();
String domainPassword = domain.getAuthInfo().getPw().getValue();
if (!domainPassword.equals(authPassword)) {
if (!MessageDigest.isEqual(
authPassword.getBytes(StandardCharsets.UTF_8),
domainPassword.getBytes(StandardCharsets.UTF_8))) {
throw new BadAuthInfoForResourceException();
}
}
@@ -25,6 +25,7 @@ import com.google.common.hash.Hashing;
import google.registry.model.server.ServerSecret;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -102,7 +103,7 @@ public final class XsrfTokenManager {
}
// Reconstruct the token to verify validity.
String reconstructedToken = encodeToken(ServerSecret.get().asBytes(), email, timestampMillis);
if (!token.equals(reconstructedToken)) {
if (!MessageDigest.isEqual(token.getBytes(UTF_8), reconstructedToken.getBytes(UTF_8))) {
logger.atWarning().log(
"Reconstructed XSRF mismatch (got != expected): %s != %s", token, reconstructedToken);
return false;
@@ -22,8 +22,8 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Bytes;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Optional;
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.generators.SCrypt;
@@ -143,11 +143,11 @@ public final class PasswordUtils {
byte[] decodedSalt = base64().decode(salt);
byte[] decodedPassword = password.getBytes(US_ASCII);
if (Arrays.equals(decodedHash, ARGON_2_ID.hash(decodedPassword, decodedSalt))) {
if (MessageDigest.isEqual(decodedHash, ARGON_2_ID.hash(decodedPassword, decodedSalt))) {
logger.atInfo().log("ARGON_2_ID hash verified.");
return Optional.of(ARGON_2_ID);
}
if (Arrays.equals(decodedHash, SCRYPT_P_1.hash(decodedPassword, decodedSalt))) {
if (MessageDigest.isEqual(decodedHash, SCRYPT_P_1.hash(decodedPassword, decodedSalt))) {
logger.atInfo().log("SCRYPT_P_1 hash verified.");
return Optional.of(SCRYPT_P_1);
}