Retire V1 authentication tokens

This commit is contained in:
Jon Chambers 2026-04-15 14:15:15 -04:00 committed by Jon Chambers
parent bb589d6daa
commit dcb5187629
2 changed files with 3 additions and 34 deletions

View File

@ -6,20 +6,12 @@ package org.whispersystems.textsecuregcm.auth;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HexFormat;
import org.signal.libsignal.protocol.kdf.HKDF;
public record SaltedTokenHash(String hash, String salt) {
public enum Version {
V1,
V2,
}
public static final Version CURRENT_VERSION = Version.V2;
private static final String V2_PREFIX = "2.";
private static final byte[] AUTH_TOKEN_HKDF_INFO = "authtoken".getBytes(StandardCharsets.UTF_8);
@ -31,21 +23,13 @@ public record SaltedTokenHash(String hash, String salt) {
public static SaltedTokenHash generateFor(final String token) {
final String salt = generateSalt();
final String hash = calculateV2Hash(salt, token);
final String hash = calculateHash(salt, token);
return new SaltedTokenHash(hash, salt);
}
public Version getVersion() {
return hash.startsWith(V2_PREFIX) ? Version.V2 : Version.V1;
}
public boolean verify(final String token) {
final String theirValue = switch (getVersion()) {
case V1 -> calculateV1Hash(salt, token);
case V2 -> calculateV2Hash(salt, token);
};
return MessageDigest.isEqual(
theirValue.getBytes(StandardCharsets.UTF_8),
calculateHash(salt, token).getBytes(StandardCharsets.UTF_8),
hash.getBytes(StandardCharsets.UTF_8));
}
@ -55,16 +39,7 @@ public record SaltedTokenHash(String hash, String salt) {
return HexFormat.of().formatHex(salt);
}
private static String calculateV1Hash(final String salt, final String token) {
try {
return HexFormat.of()
.formatHex(MessageDigest.getInstance("SHA1").digest((salt + token).getBytes(StandardCharsets.UTF_8)));
} catch (final NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static String calculateV2Hash(final String salt, final String token) {
private static String calculateHash(final String salt, final String token) {
final byte[] secret = HKDF.deriveSecrets(
token.getBytes(StandardCharsets.UTF_8), // key
salt.getBytes(StandardCharsets.UTF_8), // salt

View File

@ -14,7 +14,6 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -167,7 +166,6 @@ class AccountAuthenticatorTest {
when(device.getId()).thenReturn(deviceId);
when(device.getAuthTokenHash()).thenReturn(credentials);
when(credentials.verify(password)).thenReturn(true);
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedDevice> maybeAuthenticatedAccount =
accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password));
@ -196,7 +194,6 @@ class AccountAuthenticatorTest {
when(device.getId()).thenReturn(deviceId);
when(device.getAuthTokenHash()).thenReturn(credentials);
when(credentials.verify(password)).thenReturn(true);
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedDevice> maybeAuthenticatedAccount =
accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password));
@ -226,7 +223,6 @@ class AccountAuthenticatorTest {
when(authenticatedDevice.getId()).thenReturn(deviceId);
when(authenticatedDevice.getAuthTokenHash()).thenReturn(credentials);
when(credentials.verify(password)).thenReturn(true);
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final String identifier;
if (authenticatedDeviceIsPrimary) {
@ -267,7 +263,6 @@ class AccountAuthenticatorTest {
when(device.getId()).thenReturn(deviceId);
when(device.getAuthTokenHash()).thenReturn(credentials);
when(credentials.verify(password)).thenReturn(true);
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedDevice> maybeAuthenticatedAccount =
accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password));
@ -295,7 +290,6 @@ class AccountAuthenticatorTest {
when(device.getId()).thenReturn(deviceId);
when(device.getAuthTokenHash()).thenReturn(credentials);
when(credentials.verify(password)).thenReturn(true);
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final String incorrectPassword = password + "incorrect";