Retire V1 authentication tokens
This commit is contained in:
parent
bb589d6daa
commit
dcb5187629
@ -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
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user