AuthenticationCredentials name changed to SaltedTokenHash
This commit is contained in:
parent
dc8f62a4ad
commit
8d0e23bde1
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
package org.whispersystems.textsecuregcm.auth;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import org.apache.commons.codec.binary.Hex;
|
|
||||||
import org.signal.libsignal.protocol.kdf.HKDF;
|
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
|
|
||||||
public class AuthenticationCredentials {
|
|
||||||
private static final String V2_PREFIX = "2.";
|
|
||||||
|
|
||||||
private final String hashedAuthenticationToken;
|
|
||||||
private final String salt;
|
|
||||||
|
|
||||||
public enum Version {
|
|
||||||
V1,
|
|
||||||
V2,
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Version CURRENT_VERSION = Version.V2;
|
|
||||||
|
|
||||||
public AuthenticationCredentials(String hashedAuthenticationToken, String salt) {
|
|
||||||
this.hashedAuthenticationToken = hashedAuthenticationToken;
|
|
||||||
this.salt = salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthenticationCredentials(String authenticationToken) {
|
|
||||||
this.salt = String.valueOf(Util.ensureNonNegativeInt(new SecureRandom().nextInt()));
|
|
||||||
this.hashedAuthenticationToken = getV2HashedValue(salt, authenticationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public AuthenticationCredentials v1ForTesting(String authenticationToken) {
|
|
||||||
String salt = String.valueOf(Util.ensureNonNegativeInt(new SecureRandom().nextInt()));
|
|
||||||
return new AuthenticationCredentials(getV1HashedValue(salt, authenticationToken), salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Version getVersion() {
|
|
||||||
if (this.hashedAuthenticationToken.startsWith(V2_PREFIX)) {
|
|
||||||
return Version.V2;
|
|
||||||
}
|
|
||||||
return Version.V1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHashedAuthenticationToken() {
|
|
||||||
return hashedAuthenticationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSalt() {
|
|
||||||
return salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String authenticationToken) {
|
|
||||||
final String theirValue = switch (getVersion()) {
|
|
||||||
case V1 -> getV1HashedValue(salt, authenticationToken);
|
|
||||||
case V2 -> getV2HashedValue(salt, authenticationToken);
|
|
||||||
};
|
|
||||||
return MessageDigest.isEqual(theirValue.getBytes(StandardCharsets.UTF_8), this.hashedAuthenticationToken.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getV1HashedValue(String salt, String token) {
|
|
||||||
try {
|
|
||||||
return new String(Hex.encodeHex(MessageDigest.getInstance("SHA1").digest((salt + token).getBytes(StandardCharsets.UTF_8))));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final byte[] AUTH_TOKEN_HKDF_INFO = "authtoken".getBytes(StandardCharsets.UTF_8);
|
|
||||||
private static String getV2HashedValue(String salt, String token) {
|
|
||||||
byte[] secret = HKDF.deriveSecrets(
|
|
||||||
token.getBytes(StandardCharsets.UTF_8), // key
|
|
||||||
salt.getBytes(StandardCharsets.UTF_8), // salt
|
|
||||||
AUTH_TOKEN_HKDF_INFO,
|
|
||||||
32);
|
|
||||||
return V2_PREFIX + Hex.encodeHexString(secret);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2021 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -123,15 +123,15 @@ public class BaseAccountAuthenticator {
|
||||||
.increment();
|
.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationCredentials deviceAuthenticationCredentials = device.get().getAuthenticationCredentials();
|
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
|
||||||
if (deviceAuthenticationCredentials.verify(basicCredentials.getPassword())) {
|
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
|
||||||
succeeded = true;
|
succeeded = true;
|
||||||
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
|
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
|
||||||
if (deviceAuthenticationCredentials.getVersion() != AuthenticationCredentials.CURRENT_VERSION) {
|
if (deviceSaltedTokenHash.getVersion() != SaltedTokenHash.CURRENT_VERSION) {
|
||||||
authenticatedAccount = accountsManager.updateDeviceAuthentication(
|
authenticatedAccount = accountsManager.updateDeviceAuthentication(
|
||||||
authenticatedAccount,
|
authenticatedAccount,
|
||||||
device.get(),
|
device.get(),
|
||||||
new AuthenticationCredentials(basicCredentials.getPassword())); // new credentials have current version
|
SaltedTokenHash.generateFor(basicCredentials.getPassword())); // new credentials have current version
|
||||||
}
|
}
|
||||||
return Optional.of(new AuthenticatedAccount(
|
return Optional.of(new AuthenticatedAccount(
|
||||||
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
|
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
package org.whispersystems.textsecuregcm.auth;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
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);
|
||||||
|
|
||||||
|
private static final int SALT_SIZE = 16;
|
||||||
|
|
||||||
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
|
||||||
|
public static SaltedTokenHash generateFor(final String token) {
|
||||||
|
final String salt = generateSalt();
|
||||||
|
final String hash = calculateV2Hash(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),
|
||||||
|
hash.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateSalt() {
|
||||||
|
final byte[] salt = new byte[SALT_SIZE];
|
||||||
|
SECURE_RANDOM.nextBytes(salt);
|
||||||
|
return Hex.encodeHexString(salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String calculateV1Hash(final String salt, final String token) {
|
||||||
|
try {
|
||||||
|
return new String(
|
||||||
|
Hex.encodeHex(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) {
|
||||||
|
final byte[] secret = HKDF.deriveSecrets(
|
||||||
|
token.getBytes(StandardCharsets.UTF_8), // key
|
||||||
|
salt.getBytes(StandardCharsets.UTF_8), // salt
|
||||||
|
AUTH_TOKEN_HKDF_INFO,
|
||||||
|
32);
|
||||||
|
return V2_PREFIX + Hex.encodeHexString(secret);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.auth;
|
package org.whispersystems.textsecuregcm.auth;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
public class StoredRegistrationLock {
|
public class StoredRegistrationLock {
|
||||||
|
@ -56,7 +55,7 @@ public class StoredRegistrationLock {
|
||||||
|
|
||||||
public boolean verify(@Nullable String clientRegistrationLock) {
|
public boolean verify(@Nullable String clientRegistrationLock) {
|
||||||
if (hasLockAndSalt() && Util.nonEmpty(clientRegistrationLock)) {
|
if (hasLockAndSalt() && Util.nonEmpty(clientRegistrationLock)) {
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(registrationLock.get(), registrationLockSalt.get());
|
SaltedTokenHash credentials = new SaltedTokenHash(registrationLock.get(), registrationLockSalt.get());
|
||||||
return credentials.verify(clientRegistrationLock);
|
return credentials.verify(clientRegistrationLock);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -55,14 +55,13 @@ import javax.ws.rs.core.Response.Status;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.spam.FilterSpam;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.BasicAuthorizationHeader;
|
import org.whispersystems.textsecuregcm.auth.BasicAuthorizationHeader;
|
||||||
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
|
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||||
|
@ -97,6 +96,7 @@ import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||||
import org.whispersystems.textsecuregcm.registration.ClientType;
|
import org.whispersystems.textsecuregcm.registration.ClientType;
|
||||||
import org.whispersystems.textsecuregcm.registration.MessageTransport;
|
import org.whispersystems.textsecuregcm.registration.MessageTransport;
|
||||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||||
|
import org.whispersystems.textsecuregcm.spam.FilterSpam;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
|
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
|
||||||
|
@ -615,10 +615,10 @@ public class AccountController {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("/registration_lock")
|
@Path("/registration_lock")
|
||||||
public void setRegistrationLock(@Auth AuthenticatedAccount auth, @NotNull @Valid RegistrationLock accountLock) {
|
public void setRegistrationLock(@Auth AuthenticatedAccount auth, @NotNull @Valid RegistrationLock accountLock) {
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(accountLock.getRegistrationLock());
|
SaltedTokenHash credentials = SaltedTokenHash.generateFor(accountLock.getRegistrationLock());
|
||||||
|
|
||||||
accounts.update(auth.getAccount(),
|
accounts.update(auth.getAccount(),
|
||||||
a -> a.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt()));
|
a -> a.setRegistrationLock(credentials.hash(), credentials.salt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.controllers;
|
package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
@ -30,9 +30,9 @@ import javax.ws.rs.core.Response;
|
||||||
import org.glassfish.jersey.server.ContainerRequest;
|
import org.glassfish.jersey.server.ContainerRequest;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthEnablementRefreshRequirementProvider;
|
import org.whispersystems.textsecuregcm.auth.AuthEnablementRefreshRequirementProvider;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.BasicAuthorizationHeader;
|
import org.whispersystems.textsecuregcm.auth.BasicAuthorizationHeader;
|
||||||
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
|
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.DeviceInfo;
|
import org.whispersystems.textsecuregcm.entities.DeviceInfo;
|
||||||
|
@ -192,7 +192,7 @@ public class DeviceController {
|
||||||
|
|
||||||
Device device = new Device();
|
Device device = new Device();
|
||||||
device.setName(accountAttributes.getName());
|
device.setName(accountAttributes.getName());
|
||||||
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
device.setAuthTokenHash(SaltedTokenHash.generateFor(password));
|
||||||
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
||||||
device.setRegistrationId(accountAttributes.getRegistrationId());
|
device.setRegistrationId(accountAttributes.getRegistrationId());
|
||||||
accountAttributes.getPhoneNumberIdentityRegistrationId().ifPresent(device::setPhoneNumberIdentityRegistrationId);
|
accountAttributes.getPhoneNumberIdentityRegistrationId().ifPresent(device::setPhoneNumberIdentityRegistrationId);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
@ -18,7 +18,7 @@ import java.util.function.Predicate;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
|
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
|
||||||
|
@ -390,8 +390,8 @@ public class Account {
|
||||||
|
|
||||||
public void setRegistrationLockFromAttributes(final AccountAttributes attributes) {
|
public void setRegistrationLockFromAttributes(final AccountAttributes attributes) {
|
||||||
if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(attributes.getRegistrationLock());
|
SaltedTokenHash credentials = SaltedTokenHash.generateFor(attributes.getRegistrationLock());
|
||||||
setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
setRegistrationLock(credentials.hash(), credentials.salt());
|
||||||
} else {
|
} else {
|
||||||
setRegistrationLock(null, null);
|
setRegistrationLock(null, null);
|
||||||
}
|
}
|
||||||
|
@ -485,8 +485,8 @@ public class Account {
|
||||||
* of the phone number, or after 7 days the phone number holder can register a new
|
* of the phone number, or after 7 days the phone number holder can register a new
|
||||||
* account.
|
* account.
|
||||||
*/
|
*/
|
||||||
public void lockAuthenticationCredentials() {
|
public void lockAuthTokenHash() {
|
||||||
devices.forEach(Device::lockAuthenticationCredentials);
|
devices.forEach(Device::lockAuthTokenHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isStale() {
|
boolean isStale() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
@ -36,7 +36,7 @@ import java.util.function.Supplier;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
|
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
|
@ -169,7 +169,7 @@ public class AccountsManager {
|
||||||
deletedAccountsManager.lockAndTake(number, maybeRecentlyDeletedUuid -> {
|
deletedAccountsManager.lockAndTake(number, maybeRecentlyDeletedUuid -> {
|
||||||
Device device = new Device();
|
Device device = new Device();
|
||||||
device.setId(Device.MASTER_ID);
|
device.setId(Device.MASTER_ID);
|
||||||
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
device.setAuthTokenHash(SaltedTokenHash.generateFor(password));
|
||||||
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
||||||
device.setRegistrationId(accountAttributes.getRegistrationId());
|
device.setRegistrationId(accountAttributes.getRegistrationId());
|
||||||
accountAttributes.getPhoneNumberIdentityRegistrationId().ifPresent(device::setPhoneNumberIdentityRegistrationId);
|
accountAttributes.getPhoneNumberIdentityRegistrationId().ifPresent(device::setPhoneNumberIdentityRegistrationId);
|
||||||
|
@ -496,12 +496,12 @@ public class AccountsManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account updateDeviceAuthentication(final Account account, final Device device, final AuthenticationCredentials credentials) {
|
public Account updateDeviceAuthentication(final Account account, final Device device, final SaltedTokenHash credentials) {
|
||||||
Preconditions.checkArgument(credentials.getVersion() == AuthenticationCredentials.CURRENT_VERSION);
|
Preconditions.checkArgument(credentials.getVersion() == SaltedTokenHash.CURRENT_VERSION);
|
||||||
return updateDevice(account, device.getId(), new Consumer<Device>() {
|
return updateDevice(account, device.getId(), new Consumer<Device>() {
|
||||||
@Override
|
@Override
|
||||||
public void accept(final Device device) {
|
public void accept(final Device device) {
|
||||||
device.setAuthenticationCredentials(credentials);
|
device.setAuthTokenHash(credentials);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
@ -9,7 +9,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
|
@ -144,9 +144,9 @@ public class Device {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthenticationCredentials(AuthenticationCredentials credentials) {
|
public void setAuthTokenHash(SaltedTokenHash credentials) {
|
||||||
this.authToken = credentials.getHashedAuthenticationToken();
|
this.authToken = credentials.hash();
|
||||||
this.salt = credentials.getSalt();
|
this.salt = credentials.salt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,8 +158,8 @@ public class Device {
|
||||||
* @return true if the credential was locked, false otherwise.
|
* @return true if the credential was locked, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasLockedCredentials() {
|
public boolean hasLockedCredentials() {
|
||||||
AuthenticationCredentials auth = getAuthenticationCredentials();
|
SaltedTokenHash auth = getAuthTokenHash();
|
||||||
return auth.getHashedAuthenticationToken().startsWith("!");
|
return auth.hash().startsWith("!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,15 +169,15 @@ public class Device {
|
||||||
*
|
*
|
||||||
* See that method for more information.
|
* See that method for more information.
|
||||||
*/
|
*/
|
||||||
public void lockAuthenticationCredentials() {
|
public void lockAuthTokenHash() {
|
||||||
AuthenticationCredentials oldAuth = getAuthenticationCredentials();
|
SaltedTokenHash oldAuth = getAuthTokenHash();
|
||||||
String token = "!" + oldAuth.getHashedAuthenticationToken();
|
String token = "!" + oldAuth.hash();
|
||||||
String salt = oldAuth.getSalt();
|
String salt = oldAuth.salt();
|
||||||
setAuthenticationCredentials(new AuthenticationCredentials(token, salt));
|
setAuthTokenHash(new SaltedTokenHash(token, salt));
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationCredentials getAuthenticationCredentials() {
|
public SaltedTokenHash getAuthTokenHash() {
|
||||||
return new AuthenticationCredentials(authToken, salt);
|
return new SaltedTokenHash(authToken, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2021 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device device = mock(Device.class);
|
final Device device = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -164,9 +164,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(deviceId);
|
when(device.getId()).thenReturn(deviceId);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
when(device.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.CURRENT_VERSION);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
|
||||||
|
|
||||||
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
||||||
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
|
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
|
||||||
|
@ -185,7 +185,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device device = mock(Device.class);
|
final Device device = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -194,9 +194,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(deviceId);
|
when(device.getId()).thenReturn(deviceId);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
when(device.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.CURRENT_VERSION);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
|
||||||
|
|
||||||
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
||||||
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password), true);
|
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password), true);
|
||||||
|
@ -219,7 +219,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device authenticatedDevice = mock(Device.class);
|
final Device authenticatedDevice = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -228,9 +228,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(accountEnabled);
|
when(account.isEnabled()).thenReturn(accountEnabled);
|
||||||
when(authenticatedDevice.getId()).thenReturn(deviceId);
|
when(authenticatedDevice.getId()).thenReturn(deviceId);
|
||||||
when(authenticatedDevice.isEnabled()).thenReturn(deviceEnabled);
|
when(authenticatedDevice.isEnabled()).thenReturn(deviceEnabled);
|
||||||
when(authenticatedDevice.getAuthenticationCredentials()).thenReturn(credentials);
|
when(authenticatedDevice.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.CURRENT_VERSION);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
|
||||||
|
|
||||||
final String identifier;
|
final String identifier;
|
||||||
if (authenticatedDeviceIsPrimary) {
|
if (authenticatedDeviceIsPrimary) {
|
||||||
|
@ -258,7 +258,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device device = mock(Device.class);
|
final Device device = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -267,9 +267,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(deviceId);
|
when(device.getId()).thenReturn(deviceId);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
when(device.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.Version.V1);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.Version.V1);
|
||||||
|
|
||||||
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
||||||
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
|
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
|
||||||
|
@ -295,7 +295,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device device = mock(Device.class);
|
final Device device = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -304,9 +304,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(deviceId);
|
when(device.getId()).thenReturn(deviceId);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
when(device.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.CURRENT_VERSION);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
|
||||||
|
|
||||||
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
|
||||||
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password), true);
|
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password), true);
|
||||||
|
@ -323,7 +323,7 @@ class BaseAccountAuthenticatorTest {
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
final Device device = mock(Device.class);
|
final Device device = mock(Device.class);
|
||||||
final AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
final SaltedTokenHash credentials = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
clock.unpin();
|
clock.unpin();
|
||||||
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
when(accountsManager.getByAccountIdentifier(uuid)).thenReturn(Optional.of(account));
|
||||||
|
@ -332,9 +332,9 @@ class BaseAccountAuthenticatorTest {
|
||||||
when(account.isEnabled()).thenReturn(true);
|
when(account.isEnabled()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(deviceId);
|
when(device.getId()).thenReturn(deviceId);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
when(device.getAuthTokenHash()).thenReturn(credentials);
|
||||||
when(credentials.verify(password)).thenReturn(true);
|
when(credentials.verify(password)).thenReturn(true);
|
||||||
when(credentials.getVersion()).thenReturn(AuthenticationCredentials.CURRENT_VERSION);
|
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
|
||||||
|
|
||||||
final String incorrectPassword = password + "incorrect";
|
final String incorrectPassword = password + "incorrect";
|
||||||
|
|
||||||
|
|
|
@ -63,9 +63,9 @@ import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||||
|
@ -213,7 +213,7 @@ class AccountControllerTest {
|
||||||
clearInvocations(AuthHelper.VALID_ACCOUNT, AuthHelper.UNDISCOVERABLE_ACCOUNT);
|
clearInvocations(AuthHelper.VALID_ACCOUNT, AuthHelper.UNDISCOVERABLE_ACCOUNT);
|
||||||
|
|
||||||
new SecureRandom().nextBytes(registration_lock_key);
|
new SecureRandom().nextBytes(registration_lock_key);
|
||||||
AuthenticationCredentials registrationLockCredentials = new AuthenticationCredentials(Hex.toStringCondensed(registration_lock_key));
|
SaltedTokenHash registrationLockCredentials = SaltedTokenHash.generateFor(Hex.toStringCondensed(registration_lock_key));
|
||||||
|
|
||||||
AccountsHelper.setupMockUpdate(accountsManager);
|
AccountsHelper.setupMockUpdate(accountsManager);
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ class AccountControllerTest {
|
||||||
when(senderHasStorage.isStorageSupported()).thenReturn(true);
|
when(senderHasStorage.isStorageSupported()).thenReturn(true);
|
||||||
when(senderHasStorage.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
when(senderHasStorage.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||||
|
|
||||||
when(senderRegLockAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.of(registrationLockCredentials.getHashedAuthenticationToken()), Optional.of(registrationLockCredentials.getSalt()), System.currentTimeMillis()));
|
when(senderRegLockAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.of(registrationLockCredentials.hash()), Optional.of(registrationLockCredentials.salt()), System.currentTimeMillis()));
|
||||||
when(senderRegLockAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
when(senderRegLockAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||||
when(senderRegLockAccount.getUuid()).thenReturn(SENDER_REG_LOCK_UUID);
|
when(senderRegLockAccount.getUuid()).thenReturn(SENDER_REG_LOCK_UUID);
|
||||||
when(senderRegLockAccount.getNumber()).thenReturn(SENDER_REG_LOCK);
|
when(senderRegLockAccount.getNumber()).thenReturn(SENDER_REG_LOCK);
|
||||||
|
@ -1483,7 +1483,7 @@ class AccountControllerTest {
|
||||||
MediaType.APPLICATION_JSON_TYPE));
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
verify(senderRegLockAccount, never()).lockAuthenticationCredentials();
|
verify(senderRegLockAccount, never()).lockAuthTokenHash();
|
||||||
verify(clientPresenceManager, never()).disconnectAllPresences(eq(SENDER_REG_LOCK_UUID), any());
|
verify(clientPresenceManager, never()).disconnectAllPresences(eq(SENDER_REG_LOCK_UUID), any());
|
||||||
verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any(), any(), any(), any());
|
verify(changeNumberManager).changeNumber(eq(AuthHelper.VALID_ACCOUNT), any(), any(), any(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
|
@ -199,7 +199,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
final byte[] unidentifiedAccessKey = new byte[]{1};
|
final byte[] unidentifiedAccessKey = new byte[]{1};
|
||||||
final String pin = "1234";
|
final String pin = "1234";
|
||||||
final String registrationLock = "reglock";
|
final String registrationLock = "reglock";
|
||||||
final AuthenticationCredentials credentials = new AuthenticationCredentials(registrationLock);
|
final SaltedTokenHash credentials = SaltedTokenHash.generateFor(registrationLock);
|
||||||
final boolean unrestrictedUnidentifiedAccess = true;
|
final boolean unrestrictedUnidentifiedAccess = true;
|
||||||
final long lastSeen = Instant.now().getEpochSecond();
|
final long lastSeen = Instant.now().getEpochSecond();
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
modifyAccount(uuid, account -> account.setCurrentProfileVersion(currentProfileVersion)),
|
modifyAccount(uuid, account -> account.setCurrentProfileVersion(currentProfileVersion)),
|
||||||
modifyAccount(uuid, account -> account.setIdentityKey(identityKey)),
|
modifyAccount(uuid, account -> account.setIdentityKey(identityKey)),
|
||||||
modifyAccount(uuid, account -> account.setUnidentifiedAccessKey(unidentifiedAccessKey)),
|
modifyAccount(uuid, account -> account.setUnidentifiedAccessKey(unidentifiedAccessKey)),
|
||||||
modifyAccount(uuid, account -> account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt())),
|
modifyAccount(uuid, account -> account.setRegistrationLock(credentials.hash(), credentials.salt())),
|
||||||
modifyAccount(uuid, account -> account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess)),
|
modifyAccount(uuid, account -> account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess)),
|
||||||
modifyDevice(uuid, Device.MASTER_ID, device -> device.setLastSeen(lastSeen)),
|
modifyDevice(uuid, Device.MASTER_ID, device -> device.setLastSeen(lastSeen)),
|
||||||
modifyDevice(uuid, Device.MASTER_ID, device -> device.setName("deviceName"))
|
modifyDevice(uuid, Device.MASTER_ID, device -> device.setName("deviceName"))
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.tests.auth;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|
||||||
|
|
||||||
class AuthenticationCredentialsTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCreating() {
|
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials("mypassword");
|
|
||||||
assertThat(credentials.getSalt()).isNotEmpty();
|
|
||||||
assertThat(credentials.getHashedAuthenticationToken()).isNotEmpty();
|
|
||||||
assertThat(credentials.getHashedAuthenticationToken().length()).isEqualTo(66);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testMatching() {
|
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials("mypassword");
|
|
||||||
|
|
||||||
AuthenticationCredentials provided = new AuthenticationCredentials(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
|
||||||
assertThat(provided.verify("mypassword")).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testMisMatching() {
|
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials("mypassword");
|
|
||||||
|
|
||||||
AuthenticationCredentials provided = new AuthenticationCredentials(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
|
||||||
assertThat(provided.verify("wrong")).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.tests.auth;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
|
|
||||||
|
class SaltedTokenHashTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreating() {
|
||||||
|
SaltedTokenHash credentials = SaltedTokenHash.generateFor("mypassword");
|
||||||
|
assertThat(credentials.salt()).isNotEmpty();
|
||||||
|
assertThat(credentials.hash()).isNotEmpty();
|
||||||
|
assertThat(credentials.hash().length()).isEqualTo(66);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMatching() {
|
||||||
|
SaltedTokenHash credentials = SaltedTokenHash.generateFor("mypassword");
|
||||||
|
|
||||||
|
SaltedTokenHash provided = new SaltedTokenHash(credentials.hash(), credentials.salt());
|
||||||
|
assertThat(provided.verify("mypassword")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMisMatching() {
|
||||||
|
SaltedTokenHash credentials = SaltedTokenHash.generateFor("mypassword");
|
||||||
|
|
||||||
|
SaltedTokenHash provided = new SaltedTokenHash(credentials.hash(), credentials.salt());
|
||||||
|
assertThat(provided.verify("wrong")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2022 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.mockito.MockingDetails;
|
import org.mockito.MockingDetails;
|
||||||
import org.mockito.stubbing.Stubbing;
|
import org.mockito.stubbing.Stubbing;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
@ -75,7 +75,7 @@ public class AccountsHelper {
|
||||||
});
|
});
|
||||||
|
|
||||||
when(mockAccountsManager.updateDeviceAuthentication(any(), any(), any())).thenAnswer(answer -> {
|
when(mockAccountsManager.updateDeviceAuthentication(any(), any(), any())).thenAnswer(answer -> {
|
||||||
answer.getArgument(1, Device.class).setAuthenticationCredentials(answer.getArgument(2, AuthenticationCredentials.class));
|
answer.getArgument(1, Device.class).setAuthTokenHash(answer.getArgument(2, SaltedTokenHash.class));
|
||||||
return mockAccountsManager.update(answer.getArgument(0, Account.class), account -> {});
|
return mockAccountsManager.update(answer.getArgument(0, Account.class), account -> {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013-2023 Signal Messenger, LLC
|
* Copyright 2013 Signal Messenger, LLC
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
|
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
@ -79,12 +79,12 @@ public class AuthHelper {
|
||||||
public static Device VALID_DEVICE_3_PRIMARY = mock(Device.class);
|
public static Device VALID_DEVICE_3_PRIMARY = mock(Device.class);
|
||||||
public static Device VALID_DEVICE_3_LINKED = mock(Device.class);
|
public static Device VALID_DEVICE_3_LINKED = mock(Device.class);
|
||||||
|
|
||||||
private static AuthenticationCredentials VALID_CREDENTIALS = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash VALID_CREDENTIALS = mock(SaltedTokenHash.class);
|
||||||
private static AuthenticationCredentials VALID_CREDENTIALS_TWO = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash VALID_CREDENTIALS_TWO = mock(SaltedTokenHash.class);
|
||||||
private static AuthenticationCredentials VALID_CREDENTIALS_3_PRIMARY = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash VALID_CREDENTIALS_3_PRIMARY = mock(SaltedTokenHash.class);
|
||||||
private static AuthenticationCredentials VALID_CREDENTIALS_3_LINKED = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash VALID_CREDENTIALS_3_LINKED = mock(SaltedTokenHash.class);
|
||||||
private static AuthenticationCredentials DISABLED_CREDENTIALS = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash DISABLED_CREDENTIALS = mock(SaltedTokenHash.class);
|
||||||
private static AuthenticationCredentials UNDISCOVERABLE_CREDENTIALS = mock(AuthenticationCredentials.class);
|
private static SaltedTokenHash UNDISCOVERABLE_CREDENTIALS = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
public static PolymorphicAuthDynamicFeature<? extends Principal> getAuthFilter() {
|
public static PolymorphicAuthDynamicFeature<? extends Principal> getAuthFilter() {
|
||||||
when(VALID_CREDENTIALS.verify("foo")).thenReturn(true);
|
when(VALID_CREDENTIALS.verify("foo")).thenReturn(true);
|
||||||
|
@ -94,12 +94,12 @@ public class AuthHelper {
|
||||||
when(DISABLED_CREDENTIALS.verify(DISABLED_PASSWORD)).thenReturn(true);
|
when(DISABLED_CREDENTIALS.verify(DISABLED_PASSWORD)).thenReturn(true);
|
||||||
when(UNDISCOVERABLE_CREDENTIALS.verify(UNDISCOVERABLE_PASSWORD)).thenReturn(true);
|
when(UNDISCOVERABLE_CREDENTIALS.verify(UNDISCOVERABLE_PASSWORD)).thenReturn(true);
|
||||||
|
|
||||||
when(VALID_DEVICE.getAuthenticationCredentials()).thenReturn(VALID_CREDENTIALS);
|
when(VALID_DEVICE.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS);
|
||||||
when(VALID_DEVICE_TWO.getAuthenticationCredentials()).thenReturn(VALID_CREDENTIALS_TWO);
|
when(VALID_DEVICE_TWO.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_TWO);
|
||||||
when(VALID_DEVICE_3_PRIMARY.getAuthenticationCredentials()).thenReturn(VALID_CREDENTIALS_3_PRIMARY);
|
when(VALID_DEVICE_3_PRIMARY.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_PRIMARY);
|
||||||
when(VALID_DEVICE_3_LINKED.getAuthenticationCredentials()).thenReturn(VALID_CREDENTIALS_3_LINKED);
|
when(VALID_DEVICE_3_LINKED.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_LINKED);
|
||||||
when(DISABLED_DEVICE.getAuthenticationCredentials()).thenReturn(DISABLED_CREDENTIALS);
|
when(DISABLED_DEVICE.getAuthTokenHash()).thenReturn(DISABLED_CREDENTIALS);
|
||||||
when(UNDISCOVERABLE_DEVICE.getAuthenticationCredentials()).thenReturn(UNDISCOVERABLE_CREDENTIALS);
|
when(UNDISCOVERABLE_DEVICE.getAuthTokenHash()).thenReturn(UNDISCOVERABLE_CREDENTIALS);
|
||||||
|
|
||||||
when(VALID_DEVICE.isMaster()).thenReturn(true);
|
when(VALID_DEVICE.isMaster()).thenReturn(true);
|
||||||
when(VALID_DEVICE_TWO.isMaster()).thenReturn(true);
|
when(VALID_DEVICE_TWO.isMaster()).thenReturn(true);
|
||||||
|
@ -231,7 +231,7 @@ public class AuthHelper {
|
||||||
public final String password;
|
public final String password;
|
||||||
public final Account account = mock(Account.class);
|
public final Account account = mock(Account.class);
|
||||||
public final Device device = mock(Device.class);
|
public final Device device = mock(Device.class);
|
||||||
public final AuthenticationCredentials authenticationCredentials = mock(AuthenticationCredentials.class);
|
public final SaltedTokenHash saltedTokenHash = mock(SaltedTokenHash.class);
|
||||||
|
|
||||||
public TestAccount(String number, UUID uuid, String password) {
|
public TestAccount(String number, UUID uuid, String password) {
|
||||||
this.number = number;
|
this.number = number;
|
||||||
|
@ -244,8 +244,8 @@ public class AuthHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(final AccountsManager accountsManager) {
|
private void setup(final AccountsManager accountsManager) {
|
||||||
when(authenticationCredentials.verify(password)).thenReturn(true);
|
when(saltedTokenHash.verify(password)).thenReturn(true);
|
||||||
when(device.getAuthenticationCredentials()).thenReturn(authenticationCredentials);
|
when(device.getAuthTokenHash()).thenReturn(saltedTokenHash);
|
||||||
when(device.isMaster()).thenReturn(true);
|
when(device.isMaster()).thenReturn(true);
|
||||||
when(device.getId()).thenReturn(1L);
|
when(device.getId()).thenReturn(1L);
|
||||||
when(device.isEnabled()).thenReturn(true);
|
when(device.isEnabled()).thenReturn(true);
|
||||||
|
|
Loading…
Reference in New Issue