Profile gRPC: Define `getExpiringProfileKeyCredential` endpoint
This commit is contained in:
parent
dd18fcaea2
commit
6a37b73463
|
@ -648,8 +648,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
.addService(ServerInterceptors.intercept(new KeysGrpcService(accountsManager, keys, rateLimiters), basicCredentialAuthenticationInterceptor))
|
||||
.addService(new KeysAnonymousGrpcService(accountsManager, keys))
|
||||
.addService(ServerInterceptors.intercept(new ProfileGrpcService(clock, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||
config.getBadges(), asyncCdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, profileBadgeConverter, rateLimiters, config.getCdnConfiguration().bucket()), basicCredentialAuthenticationInterceptor))
|
||||
.addService(new ProfileAnonymousGrpcService(accountsManager, profilesManager, profileBadgeConverter));
|
||||
config.getBadges(), asyncCdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, profileBadgeConverter, rateLimiters, zkProfileOperations, config.getCdnConfiguration().bucket()), basicCredentialAuthenticationInterceptor))
|
||||
.addService(new ProfileAnonymousGrpcService(accountsManager, profilesManager, profileBadgeConverter, zkProfileOperations));
|
||||
|
||||
RemoteDeprecationFilter remoteDeprecationFilter = new RemoteDeprecationFilter(dynamicConfigurationManager);
|
||||
environment.servlets()
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.controllers;
|
|||
|
||||
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.dropwizard.auth.Auth;
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
|
@ -18,11 +17,7 @@ import io.vavr.Tuple;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
@ -66,8 +61,6 @@ import org.signal.libsignal.protocol.ServiceId;
|
|||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -130,9 +123,6 @@ public class ProfileController {
|
|||
|
||||
private final Executor batchIdentityCheckExecutor;
|
||||
|
||||
@VisibleForTesting
|
||||
static final Duration EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION = Duration.ofDays(7);
|
||||
|
||||
private static final String EXPIRING_PROFILE_KEY_CREDENTIAL_TYPE = "expiringProfileKey";
|
||||
|
||||
private static final Counter VERSION_NOT_FOUND_COUNTER = Metrics.counter(name(ProfileController.class, "versionNotFound"));
|
||||
|
@ -276,7 +266,6 @@ public class ProfileController {
|
|||
version,
|
||||
credentialRequest,
|
||||
isSelf,
|
||||
Instant.now().plus(EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION).truncatedTo(ChronoUnit.DAYS),
|
||||
containerRequestContext);
|
||||
}
|
||||
|
||||
|
@ -387,11 +376,19 @@ public class ProfileController {
|
|||
final String version,
|
||||
final String encodedCredentialRequest,
|
||||
final boolean isSelf,
|
||||
final Instant expiration,
|
||||
final ContainerRequestContext containerRequestContext) {
|
||||
|
||||
final ExpiringProfileKeyCredentialResponse expiringProfileKeyCredentialResponse = profilesManager.get(account.getUuid(), version)
|
||||
.map(profile -> getExpiringProfileKeyCredentialResponse(encodedCredentialRequest, profile, new ServiceId.Aci(account.getUuid()), expiration))
|
||||
.map(profile -> {
|
||||
final ExpiringProfileKeyCredentialResponse profileKeyCredentialResponse;
|
||||
try {
|
||||
profileKeyCredentialResponse = ProfileHelper.getExpiringProfileKeyCredential(HexFormat.of().parseHex(encodedCredentialRequest),
|
||||
profile, new ServiceId.Aci(account.getUuid()), zkProfileOperations);
|
||||
} catch (VerificationFailedException | InvalidInputException e) {
|
||||
throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST).build(), e);
|
||||
}
|
||||
return profileKeyCredentialResponse;
|
||||
})
|
||||
.orElse(null);
|
||||
|
||||
return new ExpiringProfileKeyCredentialProfileResponse(
|
||||
|
@ -453,23 +450,6 @@ public class ProfileController {
|
|||
new PniServiceIdentifier(account.getPhoneNumberIdentifier()));
|
||||
}
|
||||
|
||||
private ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse(
|
||||
final String encodedCredentialRequest,
|
||||
final VersionedProfile profile,
|
||||
final ServiceId.Aci accountIdentifier,
|
||||
final Instant expiration) {
|
||||
|
||||
try {
|
||||
final ProfileKeyCommitment commitment = new ProfileKeyCommitment(profile.commitment());
|
||||
final ProfileKeyCredentialRequest request = new ProfileKeyCredentialRequest(
|
||||
HexFormat.of().parseHex(encodedCredentialRequest));
|
||||
|
||||
return zkProfileOperations.issueExpiringProfileKeyCredential(request, accountIdentifier, commitment, expiration);
|
||||
} catch (IllegalArgumentException | VerificationFailedException | InvalidInputException e) {
|
||||
throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).build());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Locale> getAcceptableLanguagesForRequest(final ContainerRequestContext containerRequestContext) {
|
||||
try {
|
||||
return containerRequestContext.getAcceptableLanguages();
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package org.whispersystems.textsecuregcm.grpc;
|
||||
|
||||
import io.grpc.Status;
|
||||
import org.signal.chat.profile.CredentialType;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialAnonymousRequest;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.chat.profile.GetUnversionedProfileAnonymousRequest;
|
||||
import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||
import org.signal.chat.profile.GetVersionedProfileAnonymousRequest;
|
||||
import org.signal.chat.profile.GetVersionedProfileResponse;
|
||||
import org.signal.chat.profile.ReactorProfileAnonymousGrpc;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
|
||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||
|
@ -19,14 +23,17 @@ public class ProfileAnonymousGrpcService extends ReactorProfileAnonymousGrpc.Pro
|
|||
private final AccountsManager accountsManager;
|
||||
private final ProfilesManager profilesManager;
|
||||
private final ProfileBadgeConverter profileBadgeConverter;
|
||||
private final ServerZkProfileOperations zkProfileOperations;
|
||||
|
||||
public ProfileAnonymousGrpcService(
|
||||
final AccountsManager accountsManager,
|
||||
final ProfilesManager profilesManager,
|
||||
final ProfileBadgeConverter profileBadgeConverter) {
|
||||
final ProfileBadgeConverter profileBadgeConverter,
|
||||
final ServerZkProfileOperations zkProfileOperations) {
|
||||
this.accountsManager = accountsManager;
|
||||
this.profilesManager = profilesManager;
|
||||
this.profileBadgeConverter = profileBadgeConverter;
|
||||
this.zkProfileOperations = zkProfileOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,10 +65,28 @@ public class ProfileAnonymousGrpcService extends ReactorProfileAnonymousGrpc.Pro
|
|||
.flatMap(targetAccount -> ProfileGrpcHelper.getVersionedProfile(targetAccount, profilesManager, request.getRequest().getVersion()));
|
||||
}
|
||||
|
||||
private Mono<Account> getTargetAccountAndValidateUnidentifiedAccess(final ServiceIdentifier targetIdentifier, final byte[] unidentifiedAccessKey) {
|
||||
return Mono.fromFuture(() -> accountsManager.getByServiceIdentifierAsync(targetIdentifier))
|
||||
.flatMap(Mono::justOrEmpty)
|
||||
.filter(targetAccount -> UnidentifiedAccessUtil.checkUnidentifiedAccess(targetAccount, unidentifiedAccessKey))
|
||||
.switchIfEmpty(Mono.error(Status.UNAUTHENTICATED.asException()));
|
||||
@Override
|
||||
public Mono<GetExpiringProfileKeyCredentialResponse> getExpiringProfileKeyCredential(
|
||||
final GetExpiringProfileKeyCredentialAnonymousRequest request) {
|
||||
final ServiceIdentifier targetIdentifier = ServiceIdentifierUtil.fromGrpcServiceIdentifier(request.getRequest().getAccountIdentifier());
|
||||
|
||||
if (targetIdentifier.identityType() != IdentityType.ACI) {
|
||||
throw Status.INVALID_ARGUMENT.withDescription("Expected ACI service identifier").asRuntimeException();
|
||||
}
|
||||
|
||||
if (request.getRequest().getCredentialType() != CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY) {
|
||||
throw Status.INVALID_ARGUMENT.withDescription("Expected expiring profile key credential type").asRuntimeException();
|
||||
}
|
||||
|
||||
return getTargetAccountAndValidateUnidentifiedAccess(targetIdentifier, request.getUnidentifiedAccessKey().toByteArray())
|
||||
.flatMap(account -> ProfileGrpcHelper.getExpiringProfileKeyCredentialResponse(account.getUuid(),
|
||||
request.getRequest().getVersion(), request.getRequest().getCredentialRequest().toByteArray(), profilesManager, zkProfileOperations));
|
||||
}
|
||||
|
||||
private Mono<Account> getTargetAccountAndValidateUnidentifiedAccess(final ServiceIdentifier targetIdentifier, final byte[] unidentifiedAccessKey) {
|
||||
return Mono.fromFuture(() -> accountsManager.getByServiceIdentifierAsync(targetIdentifier))
|
||||
.flatMap(Mono::justOrEmpty)
|
||||
.filter(targetAccount -> UnidentifiedAccessUtil.checkUnidentifiedAccess(targetAccount, unidentifiedAccessKey))
|
||||
.switchIfEmpty(Mono.error(Status.UNAUTHENTICATED.asException()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,15 @@ import java.util.UUID;
|
|||
import io.grpc.Status;
|
||||
import org.signal.chat.profile.Badge;
|
||||
import org.signal.chat.profile.BadgeSvg;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||
import org.signal.chat.profile.GetVersionedProfileResponse;
|
||||
import org.signal.chat.profile.UserCapabilities;
|
||||
import org.signal.libsignal.protocol.ServiceId;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||
|
@ -119,4 +125,28 @@ public class ProfileGrpcHelper {
|
|||
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
static Mono<GetExpiringProfileKeyCredentialResponse> getExpiringProfileKeyCredentialResponse(
|
||||
final UUID targetUuid,
|
||||
final String version,
|
||||
final byte[] encodedCredentialRequest,
|
||||
final ProfilesManager profilesManager,
|
||||
final ServerZkProfileOperations zkProfileOperations) {
|
||||
return Mono.fromFuture(profilesManager.getAsync(targetUuid, version))
|
||||
.flatMap(Mono::justOrEmpty)
|
||||
.map(profile -> {
|
||||
final ExpiringProfileKeyCredentialResponse profileKeyCredentialResponse;
|
||||
try {
|
||||
profileKeyCredentialResponse = ProfileHelper.getExpiringProfileKeyCredential(encodedCredentialRequest,
|
||||
profile, new ServiceId.Aci(targetUuid), zkProfileOperations);
|
||||
} catch (VerificationFailedException | InvalidInputException e) {
|
||||
throw Status.INVALID_ARGUMENT.withCause(e).asRuntimeException();
|
||||
}
|
||||
|
||||
return GetExpiringProfileKeyCredentialResponse.newBuilder()
|
||||
.setProfileKeyCredential(ByteString.copyFrom(profileKeyCredentialResponse.serialize()))
|
||||
.build();
|
||||
})
|
||||
.switchIfEmpty(Mono.error(Status.NOT_FOUND.withDescription("Profile version not found").asException()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package org.whispersystems.textsecuregcm.grpc;
|
|||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.grpc.Status;
|
||||
import org.signal.chat.profile.CredentialType;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialRequest;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.chat.profile.GetUnversionedProfileRequest;
|
||||
import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||
import org.signal.chat.profile.GetVersionedProfileRequest;
|
||||
|
@ -11,6 +14,7 @@ import org.signal.chat.profile.ProfileAvatarUploadAttributes;
|
|||
import org.signal.chat.profile.ReactorProfileGrpc;
|
||||
import org.signal.chat.profile.SetProfileRequest;
|
||||
import org.signal.chat.profile.SetProfileResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.grpc.AuthenticatedDevice;
|
||||
import org.whispersystems.textsecuregcm.auth.grpc.AuthenticationUtil;
|
||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||
|
@ -56,6 +60,7 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase {
|
|||
private final PolicySigner policySigner;
|
||||
private final ProfileBadgeConverter profileBadgeConverter;
|
||||
private final RateLimiters rateLimiters;
|
||||
private final ServerZkProfileOperations zkProfileOperations;
|
||||
private final String bucket;
|
||||
|
||||
private record AvatarData(Optional<String> currentAvatar,
|
||||
|
@ -73,6 +78,7 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase {
|
|||
final PolicySigner policySigner,
|
||||
final ProfileBadgeConverter profileBadgeConverter,
|
||||
final RateLimiters rateLimiters,
|
||||
final ServerZkProfileOperations zkProfileOperations,
|
||||
final String bucket) {
|
||||
this.clock = clock;
|
||||
this.accountsManager = accountsManager;
|
||||
|
@ -85,6 +91,7 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase {
|
|||
this.policySigner = policySigner;
|
||||
this.profileBadgeConverter = profileBadgeConverter;
|
||||
this.rateLimiters = rateLimiters;
|
||||
this.zkProfileOperations = zkProfileOperations;
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
|
@ -186,6 +193,26 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase {
|
|||
.flatMap(account -> ProfileGrpcHelper.getVersionedProfile(account, profilesManager, request.getVersion()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<GetExpiringProfileKeyCredentialResponse> getExpiringProfileKeyCredential(
|
||||
final GetExpiringProfileKeyCredentialRequest request) {
|
||||
final AuthenticatedDevice authenticatedDevice = AuthenticationUtil.requireAuthenticatedDevice();
|
||||
final ServiceIdentifier targetIdentifier = ServiceIdentifierUtil.fromGrpcServiceIdentifier(request.getAccountIdentifier());
|
||||
|
||||
if (targetIdentifier.identityType() != IdentityType.ACI) {
|
||||
throw Status.INVALID_ARGUMENT.withDescription("Expected ACI service identifier").asRuntimeException();
|
||||
}
|
||||
|
||||
if (request.getCredentialType() != CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY) {
|
||||
throw Status.INVALID_ARGUMENT.withDescription("Expected expiring profile key credential type").asRuntimeException();
|
||||
}
|
||||
|
||||
return validateRateLimitAndGetAccount(authenticatedDevice.accountIdentifier(), targetIdentifier)
|
||||
.flatMap(targetAccount -> ProfileGrpcHelper.getExpiringProfileKeyCredentialResponse(targetAccount.getUuid(),
|
||||
request.getVersion(), request.getCredentialRequest().toByteArray(), profilesManager, zkProfileOperations));
|
||||
}
|
||||
|
||||
|
||||
private Mono<Account> validateRateLimitAndGetAccount(final UUID requesterUuid,
|
||||
final ServiceIdentifier targetIdentifier) {
|
||||
return rateLimiters.getProfileLimiter().validateReactive(requesterUuid)
|
||||
|
|
|
@ -1,12 +1,23 @@
|
|||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.signal.libsignal.protocol.ServiceId;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountBadge;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import javax.annotation.Nullable;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -16,6 +27,9 @@ import java.util.UUID;
|
|||
|
||||
public class ProfileHelper {
|
||||
public static int MAX_PROFILE_AVATAR_SIZE_BYTES = 10 * 1024 * 1024;
|
||||
@VisibleForTesting
|
||||
public static final Duration EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION = Duration.ofDays(7);
|
||||
|
||||
public static List<AccountBadge> mergeBadgeIdsWithExistingAccountBadges(
|
||||
final Clock clock,
|
||||
final Map<String, BadgeConfiguration> badgeConfigurationMap,
|
||||
|
@ -70,4 +84,17 @@ public class ProfileHelper {
|
|||
public static boolean isSelfProfileRequest(@Nullable final UUID requesterUuid, final AciServiceIdentifier targetIdentifier) {
|
||||
return targetIdentifier.uuid().equals(requesterUuid);
|
||||
}
|
||||
|
||||
public static ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredential(
|
||||
final byte[] encodedCredentialRequest,
|
||||
final VersionedProfile profile,
|
||||
final ServiceId.Aci accountIdentifier,
|
||||
final ServerZkProfileOperations zkProfileOperations) throws InvalidInputException, VerificationFailedException {
|
||||
final Instant expiration = Instant.now().plus(EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION).truncatedTo(ChronoUnit.DAYS);
|
||||
final ProfileKeyCommitment commitment = new ProfileKeyCommitment(profile.commitment());
|
||||
final ProfileKeyCredentialRequest request = new ProfileKeyCredentialRequest(
|
||||
encodedCredentialRequest);
|
||||
|
||||
return zkProfileOperations.issueExpiringProfileKeyCredential(request, accountIdentifier, commitment, expiration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -253,6 +253,10 @@ message GetExpiringProfileKeyCredentialRequest {
|
|||
* The type of credential being requested.
|
||||
*/
|
||||
CredentialType credential_type = 3;
|
||||
/**
|
||||
* The profile version for which to generate a profile key credential.
|
||||
*/
|
||||
string version = 4;
|
||||
}
|
||||
|
||||
message GetExpiringProfileKeyCredentialAnonymousRequest {
|
||||
|
@ -271,7 +275,7 @@ message GetExpiringProfileKeyCredentialResponse {
|
|||
* A zkgroup credential used by a client to prove that it has the profile key
|
||||
* of a targeted account.
|
||||
*/
|
||||
bytes profileKeyCredential = 2;
|
||||
bytes profileKeyCredential = 1;
|
||||
}
|
||||
|
||||
message CheckIdentityKeysRequest {
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.controllers;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.refEq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
|
@ -107,7 +108,7 @@ import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
|||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
@ -222,9 +223,9 @@ class ProfileControllerTest {
|
|||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(capabilitiesAccount));
|
||||
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(capabilitiesAccount));
|
||||
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID), eq("someversion"))).thenReturn(Optional.empty());
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile(
|
||||
|
@ -409,14 +410,14 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWantAvatarUpload() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
final ProfileAvatarUploadAttributes uploadAttributes = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "someversion",
|
||||
ProfileHelper.encodeToBase64(name), null, null,
|
||||
ProfileTestHelper.encodeToBase64(name), null, null,
|
||||
null, true, false, List.of()), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class);
|
||||
|
||||
final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class);
|
||||
|
@ -437,7 +438,7 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWantAvatarUploadWithBadProfileSize() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
final String name = ProfileHelper.generateRandomBase64FromByteArray(82);
|
||||
final String name = ProfileTestHelper.generateRandomBase64FromByteArray(82);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
|
@ -453,7 +454,7 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWithoutAvatarUpload() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
|
@ -461,7 +462,7 @@ class ProfileControllerTest {
|
|||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", ProfileHelper.encodeToBase64(name), null, null,
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", ProfileTestHelper.encodeToBase64(name), null, null,
|
||||
null, false, false, List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -486,14 +487,14 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWithAvatarUploadAndPreviousAvatar() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion",
|
||||
ProfileHelper.encodeToBase64(name), null, null,
|
||||
ProfileTestHelper.encodeToBase64(name), null, null,
|
||||
null, true, false, List.of()), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class);
|
||||
|
||||
final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class);
|
||||
|
@ -513,13 +514,13 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileClearPreviousAvatar() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileHelper.encodeToBase64(name),
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, null, false, false, List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -543,13 +544,13 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWithSameAvatar() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileHelper.encodeToBase64(name),
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, null, true, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -573,13 +574,13 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileClearPreviousAvatarDespiteSameAvatarFlagSet() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
try (final Response ignored = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileHelper.encodeToBase64(name),
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null,
|
||||
null, false, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
|
@ -601,13 +602,13 @@ class ProfileControllerTest {
|
|||
@Test
|
||||
void testSetProfileWithSameAvatarDespiteNoPreviousAvatar() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileHelper.encodeToBase64(name),
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, null, true, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -632,14 +633,14 @@ class ProfileControllerTest {
|
|||
void testSetProfileExtendedName() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO));
|
||||
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(285);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(285);
|
||||
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(
|
||||
new CreateProfileRequest(commitment, "validversion", ProfileHelper.encodeToBase64(name),
|
||||
new CreateProfileRequest(commitment, "validversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, null, true, false, List.of()),
|
||||
MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class);
|
||||
|
||||
|
@ -663,17 +664,17 @@ class ProfileControllerTest {
|
|||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(
|
||||
new CreateProfileRequest(commitment, "anotherversion", ProfileHelper.encodeToBase64(name),
|
||||
ProfileHelper.encodeToBase64(emoji), ProfileHelper.encodeToBase64(about), null, false, false, List.of()),
|
||||
new CreateProfileRequest(commitment, "anotherversion", ProfileTestHelper.encodeToBase64(name),
|
||||
ProfileTestHelper.encodeToBase64(emoji), ProfileTestHelper.encodeToBase64(about), null, false, false, List.of()),
|
||||
MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -703,16 +704,16 @@ class ProfileControllerTest {
|
|||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(
|
||||
new CreateProfileRequest(commitment, "yetanotherversion", ProfileHelper.encodeToBase64(name),
|
||||
null, null, ProfileHelper.encodeToBase64(paymentAddress), false, false,
|
||||
new CreateProfileRequest(commitment, "yetanotherversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, ProfileTestHelper.encodeToBase64(paymentAddress), false, false,
|
||||
List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
|
@ -745,8 +746,8 @@ class ProfileControllerTest {
|
|||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final String name = ProfileHelper.generateRandomBase64FromByteArray(81);
|
||||
final String paymentAddress = ProfileHelper.generateRandomBase64FromByteArray(582);
|
||||
final String name = ProfileTestHelper.generateRandomBase64FromByteArray(81);
|
||||
final String paymentAddress = ProfileTestHelper.generateRandomBase64FromByteArray(582);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile")
|
||||
|
@ -772,15 +773,15 @@ class ProfileControllerTest {
|
|||
.thenReturn(List.of(AuthHelper.VALID_NUMBER_TWO.substring(0, 3)));
|
||||
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), any()))
|
||||
.thenReturn(Optional.of(
|
||||
new VersionedProfile("1", name, null, null, null,
|
||||
existingPaymentAddressOnProfile ? ProfileHelper.generateRandomByteArray(582) : null,
|
||||
existingPaymentAddressOnProfile ? ProfileTestHelper.generateRandomByteArray(582) : null,
|
||||
commitment.serialize())));
|
||||
|
||||
|
||||
|
@ -789,8 +790,8 @@ class ProfileControllerTest {
|
|||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(
|
||||
new CreateProfileRequest(commitment, "yetanotherversion", ProfileHelper.encodeToBase64(name),
|
||||
null, null, ProfileHelper.encodeToBase64(paymentAddress), false, false,
|
||||
new CreateProfileRequest(commitment, "yetanotherversion", ProfileTestHelper.encodeToBase64(name),
|
||||
null, null, ProfileTestHelper.encodeToBase64(paymentAddress), false, false,
|
||||
List.of()), MediaType.APPLICATION_JSON_TYPE))) {
|
||||
|
||||
if (existingPaymentAddressOnProfile) {
|
||||
|
@ -823,9 +824,9 @@ class ProfileControllerTest {
|
|||
|
||||
@Test
|
||||
void testGetProfileByVersion() throws RateLimitExceededException {
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile(
|
||||
"validversion", name, "profiles/validavatar", emoji, about, null, "validcommitmnet".getBytes())));
|
||||
|
@ -837,9 +838,9 @@ class ProfileControllerTest {
|
|||
.get(VersionedProfileResponse.class);
|
||||
|
||||
assertThat(profile.getBaseProfileResponse().getIdentityKey()).isEqualTo(ACCOUNT_TWO_IDENTITY_KEY);
|
||||
assertThat(profile.getName()).isEqualTo(ProfileHelper.encodeToBase64(name));
|
||||
assertThat(profile.getAbout()).isEqualTo(ProfileHelper.encodeToBase64(about));
|
||||
assertThat(profile.getAboutEmoji()).isEqualTo(ProfileHelper.encodeToBase64(emoji));
|
||||
assertThat(profile.getName()).isEqualTo(ProfileTestHelper.encodeToBase64(name));
|
||||
assertThat(profile.getAbout()).isEqualTo(ProfileTestHelper.encodeToBase64(about));
|
||||
assertThat(profile.getAboutEmoji()).isEqualTo(ProfileTestHelper.encodeToBase64(emoji));
|
||||
assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar");
|
||||
assertThat(profile.getBaseProfileResponse().getCapabilities().gv1Migration()).isTrue();
|
||||
assertThat(profile.getBaseProfileResponse().getUuid()).isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO));
|
||||
|
@ -858,8 +859,8 @@ class ProfileControllerTest {
|
|||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final String name = ProfileHelper.generateRandomBase64FromByteArray(81);
|
||||
final String paymentAddress = ProfileHelper.generateRandomBase64FromByteArray(582);
|
||||
final String name = ProfileTestHelper.generateRandomBase64FromByteArray(81);
|
||||
final String paymentAddress = ProfileTestHelper.generateRandomBase64FromByteArray(582);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile")
|
||||
|
@ -878,7 +879,7 @@ class ProfileControllerTest {
|
|||
|
||||
@Test
|
||||
void testGetProfileReturnsNoPaymentAddressIfCurrentVersionMismatch() {
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID_TWO, "validversion")).thenReturn(
|
||||
Optional.of(new VersionedProfile(null, null, null, null, null, paymentAddress, null)));
|
||||
|
||||
|
@ -889,7 +890,7 @@ class ProfileControllerTest {
|
|||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(VersionedProfileResponse.class);
|
||||
|
||||
assertThat(profile.getPaymentAddress()).isEqualTo(ProfileHelper.encodeToBase64(paymentAddress));
|
||||
assertThat(profile.getPaymentAddress()).isEqualTo(ProfileTestHelper.encodeToBase64(paymentAddress));
|
||||
}
|
||||
|
||||
when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of("validversion"));
|
||||
|
@ -901,7 +902,7 @@ class ProfileControllerTest {
|
|||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(VersionedProfileResponse.class);
|
||||
|
||||
assertThat(profile.getPaymentAddress()).isEqualTo(ProfileHelper.encodeToBase64(paymentAddress));
|
||||
assertThat(profile.getPaymentAddress()).isEqualTo(ProfileTestHelper.encodeToBase64(paymentAddress));
|
||||
}
|
||||
|
||||
when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of("someotherversion"));
|
||||
|
@ -948,9 +949,9 @@ class ProfileControllerTest {
|
|||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final String name = ProfileHelper.generateRandomBase64FromByteArray(81);
|
||||
final String emoji = ProfileHelper.generateRandomBase64FromByteArray(60);
|
||||
final String about = ProfileHelper.generateRandomBase64FromByteArray(156);
|
||||
final String name = ProfileTestHelper.generateRandomBase64FromByteArray(81);
|
||||
final String emoji = ProfileTestHelper.generateRandomBase64FromByteArray(60);
|
||||
final String about = ProfileTestHelper.generateRandomBase64FromByteArray(156);
|
||||
|
||||
try (final Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
|
@ -1081,7 +1082,7 @@ class ProfileControllerTest {
|
|||
when(account.isEnabled()).thenReturn(true);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(UNIDENTIFIED_ACCESS_KEY));
|
||||
|
||||
final Instant expiration = Instant.now().plus(ProfileController.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
||||
final Instant expiration = Instant.now().plus(org.whispersystems.textsecuregcm.util.ProfileHelper.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
||||
.truncatedTo(ChronoUnit.DAYS);
|
||||
|
||||
final ExpiringProfileKeyCredentialResponse credentialResponse =
|
||||
|
@ -1089,7 +1090,7 @@ class ProfileControllerTest {
|
|||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
|
||||
when(zkProfileOperations.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration))
|
||||
when(zkProfileOperations.issueExpiringProfileKeyCredential(eq(credentialRequest), eq(new ServiceId.Aci(AuthHelper.VALID_UUID)), eq(profileKeyCommitment), any()))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
|
@ -1119,15 +1120,60 @@ class ProfileControllerTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithExpiringProfileKeyCredentialBadRequest()
|
||||
throws VerificationFailedException, InvalidInputException {
|
||||
final String version = "version";
|
||||
|
||||
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
||||
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
|
||||
|
||||
final ClientZkProfileOperations clientZkProfile = new ClientZkProfileOperations(serverPublicParams);
|
||||
|
||||
final byte[] profileKeyBytes = new byte[32];
|
||||
new SecureRandom().nextBytes(profileKeyBytes);
|
||||
|
||||
final ProfileKey profileKey = new ProfileKey(profileKeyBytes);
|
||||
final ProfileKeyCommitment profileKeyCommitment = profileKey.getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
|
||||
final VersionedProfile versionedProfile = mock(VersionedProfile.class);
|
||||
when(versionedProfile.commitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
|
||||
final ProfileKeyCredentialRequestContext profileKeyCredentialRequestContext =
|
||||
clientZkProfile.createProfileKeyCredentialRequestContext(new ServiceId.Aci(AuthHelper.VALID_UUID), profileKey);
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest = profileKeyCredentialRequestContext.getRequest();
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(UNIDENTIFIED_ACCESS_KEY));
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
|
||||
when(zkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any()))
|
||||
.thenThrow(new VerificationFailedException());
|
||||
|
||||
final Response response = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
|
||||
HexFormat.of().formatHex(credentialRequest.serialize())))
|
||||
.queryParam("credentialType", "expiringProfileKey")
|
||||
.request()
|
||||
.headers(new MultivaluedHashMap<>(Map.of(OptionalAccess.UNIDENTIFIED, Base64.getEncoder().encodeToString(UNIDENTIFIED_ACCESS_KEY))))
|
||||
.get();
|
||||
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetProfileBadgesMissingFromRequest() throws InvalidInputException {
|
||||
final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID));
|
||||
|
||||
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
|
||||
|
||||
final String name = ProfileHelper.generateRandomBase64FromByteArray(81);
|
||||
final String emoji = ProfileHelper.generateRandomBase64FromByteArray(60);
|
||||
final String text = ProfileHelper.generateRandomBase64FromByteArray(156);
|
||||
final String name = ProfileTestHelper.generateRandomBase64FromByteArray(81);
|
||||
final String emoji = ProfileTestHelper.generateRandomBase64FromByteArray(60);
|
||||
final String text = ProfileTestHelper.generateRandomBase64FromByteArray(156);
|
||||
|
||||
when(AuthHelper.VALID_ACCOUNT_TWO.getBadges()).thenReturn(List.of(
|
||||
new AccountBadge("TEST", Instant.ofEpochSecond(42 + 86400), true)
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
package org.whispersystems.textsecuregcm.grpc;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatNoException;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Status;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -28,6 +34,10 @@ import org.junit.jupiter.params.provider.Arguments;
|
|||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.signal.chat.common.IdentityType;
|
||||
import org.signal.chat.common.ServiceIdentifier;
|
||||
import org.signal.chat.profile.CredentialType;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialAnonymousRequest;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialRequest;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.chat.profile.GetUnversionedProfileAnonymousRequest;
|
||||
import org.signal.chat.profile.GetUnversionedProfileRequest;
|
||||
import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||
|
@ -36,8 +46,20 @@ import org.signal.chat.profile.GetVersionedProfileRequest;
|
|||
import org.signal.chat.profile.GetVersionedProfileResponse;
|
||||
import org.signal.chat.profile.ProfileAnonymousGrpc;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.signal.libsignal.protocol.ServiceId;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.ServerPublicParams;
|
||||
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||
import org.whispersystems.textsecuregcm.entities.Badge;
|
||||
|
@ -48,7 +70,7 @@ import org.whispersystems.textsecuregcm.storage.Account;
|
|||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -58,6 +80,7 @@ public class ProfileAnonymousGrpcServiceTest {
|
|||
private ProfilesManager profilesManager;
|
||||
private ProfileBadgeConverter profileBadgeConverter;
|
||||
private ProfileAnonymousGrpc.ProfileAnonymousBlockingStub profileAnonymousBlockingStub;
|
||||
private ServerZkProfileOperations serverZkProfileOperations;
|
||||
|
||||
@RegisterExtension
|
||||
static final GrpcServerExtension GRPC_SERVER_EXTENSION = new GrpcServerExtension();
|
||||
|
@ -68,6 +91,7 @@ public class ProfileAnonymousGrpcServiceTest {
|
|||
accountsManager = mock(AccountsManager.class);
|
||||
profilesManager = mock(ProfilesManager.class);
|
||||
profileBadgeConverter = mock(ProfileBadgeConverter.class);
|
||||
serverZkProfileOperations = mock(ServerZkProfileOperations.class);
|
||||
|
||||
final Metadata metadata = new Metadata();
|
||||
metadata.put(AcceptLanguageInterceptor.ACCEPTABLE_LANGUAGES_GRPC_HEADER, "en-us");
|
||||
|
@ -79,7 +103,8 @@ public class ProfileAnonymousGrpcServiceTest {
|
|||
final ProfileAnonymousGrpcService profileAnonymousGrpcService = new ProfileAnonymousGrpcService(
|
||||
accountsManager,
|
||||
profilesManager,
|
||||
profileBadgeConverter
|
||||
profileBadgeConverter,
|
||||
serverZkProfileOperations
|
||||
);
|
||||
|
||||
GRPC_SERVER_EXTENSION.getServiceRegistry()
|
||||
|
@ -187,11 +212,11 @@ public class ProfileAnonymousGrpcServiceTest {
|
|||
new SecureRandom().nextBytes(unidentifiedAccessKey);
|
||||
|
||||
final VersionedProfile profile = mock(VersionedProfile.class);
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final String avatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
|
||||
when(profile.name()).thenReturn(name);
|
||||
when(profile.aboutEmoji()).thenReturn(emoji);
|
||||
|
@ -326,4 +351,190 @@ public class ProfileAnonymousGrpcServiceTest {
|
|||
|
||||
assertEquals(Status.INVALID_ARGUMENT.getCode(), statusRuntimeException.getStatus().getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getExpiringProfileKeyCredential() throws InvalidInputException, VerificationFailedException {
|
||||
final byte[] unidentifiedAccessKey = new byte[16];
|
||||
new SecureRandom().nextBytes(unidentifiedAccessKey);
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
||||
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
|
||||
|
||||
final ServerZkProfileOperations serverZkProfile = new ServerZkProfileOperations(serverSecretParams);
|
||||
final ClientZkProfileOperations clientZkProfile = new ClientZkProfileOperations(serverPublicParams);
|
||||
|
||||
final byte[] profileKeyBytes = new byte[32];
|
||||
new SecureRandom().nextBytes(profileKeyBytes);
|
||||
|
||||
final ProfileKey profileKey = new ProfileKey(profileKeyBytes);
|
||||
final ProfileKeyCommitment profileKeyCommitment = profileKey.getCommitment(new ServiceId.Aci(targetUuid));
|
||||
final ProfileKeyCredentialRequestContext profileKeyCredentialRequestContext =
|
||||
clientZkProfile.createProfileKeyCredentialRequestContext(new ServiceId.Aci(targetUuid), profileKey);
|
||||
|
||||
final VersionedProfile profile = mock(VersionedProfile.class);
|
||||
when(profile.commitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(Optional.of(profile)));
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest = profileKeyCredentialRequestContext.getRequest();
|
||||
|
||||
final Instant expiration = Instant.now().plus(org.whispersystems.textsecuregcm.util.ProfileHelper.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
||||
.truncatedTo(ChronoUnit.DAYS);
|
||||
|
||||
final ExpiringProfileKeyCredentialResponse credentialResponse =
|
||||
serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration);
|
||||
|
||||
when(serverZkProfileOperations.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final GetExpiringProfileKeyCredentialAnonymousRequest request = GetExpiringProfileKeyCredentialAnonymousRequest.newBuilder()
|
||||
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom(credentialRequest.serialize()))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build())
|
||||
.setUnidentifiedAccessKey(ByteString.copyFrom(unidentifiedAccessKey))
|
||||
.build();
|
||||
|
||||
final GetExpiringProfileKeyCredentialResponse response = profileAnonymousBlockingStub.getExpiringProfileKeyCredential(request);
|
||||
|
||||
assertArrayEquals(credentialResponse.serialize(), response.getProfileKeyCredential().toByteArray());
|
||||
|
||||
verify(serverZkProfileOperations).issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration);
|
||||
|
||||
final ClientZkProfileOperations clientZkProfileCipher = new ClientZkProfileOperations(serverPublicParams);
|
||||
assertThatNoException().isThrownBy(() ->
|
||||
clientZkProfileCipher.receiveExpiringProfileKeyCredential(profileKeyCredentialRequestContext, new ExpiringProfileKeyCredentialResponse(response.getProfileKeyCredential().toByteArray())));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getExpiringProfileKeyCredentialUnauthenticated(final boolean missingAccount, final boolean missingUnidentifiedAccessKey) {
|
||||
final byte[] unidentifiedAccessKey = new byte[16];
|
||||
new SecureRandom().nextBytes(unidentifiedAccessKey);
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(
|
||||
CompletableFuture.completedFuture(missingAccount ? Optional.empty() : Optional.of(account)));
|
||||
|
||||
final GetExpiringProfileKeyCredentialAnonymousRequest.Builder requestBuilder = GetExpiringProfileKeyCredentialAnonymousRequest.newBuilder()
|
||||
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build());
|
||||
|
||||
if (!missingUnidentifiedAccessKey) {
|
||||
requestBuilder.setUnidentifiedAccessKey(ByteString.copyFrom(unidentifiedAccessKey));
|
||||
}
|
||||
|
||||
final StatusRuntimeException statusRuntimeException = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileAnonymousBlockingStub.getExpiringProfileKeyCredential(requestBuilder.build()));
|
||||
|
||||
assertEquals(Status.UNAUTHENTICATED.getCode(), statusRuntimeException.getStatus().getCode());
|
||||
|
||||
verifyNoInteractions(profilesManager);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getExpiringProfileKeyCredentialUnauthenticated() {
|
||||
return Stream.of(
|
||||
Arguments.of(true, false),
|
||||
Arguments.of(false, true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getExpiringProfileKeyCredentialProfileNotFound() {
|
||||
final byte[] unidentifiedAccessKey = new byte[16];
|
||||
new SecureRandom().nextBytes(unidentifiedAccessKey);
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(
|
||||
CompletableFuture.completedFuture(Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||
|
||||
final GetExpiringProfileKeyCredentialAnonymousRequest request = GetExpiringProfileKeyCredentialAnonymousRequest.newBuilder()
|
||||
.setUnidentifiedAccessKey(ByteString.copyFrom(unidentifiedAccessKey))
|
||||
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
final StatusRuntimeException statusRuntimeException = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileAnonymousBlockingStub.getExpiringProfileKeyCredential(request));
|
||||
|
||||
assertEquals(Status.NOT_FOUND.getCode(), statusRuntimeException.getStatus().getCode());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getExpiringProfileKeyCredentialInvalidArgument(final IdentityType identityType, final CredentialType credentialType,
|
||||
final boolean throwZkVerificationException) throws VerificationFailedException {
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
final byte[] unidentifiedAccessKey = new byte[16];
|
||||
new SecureRandom().nextBytes(unidentifiedAccessKey);
|
||||
|
||||
if (throwZkVerificationException) {
|
||||
when(serverZkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any())).thenThrow(new VerificationFailedException());
|
||||
}
|
||||
|
||||
final VersionedProfile profile = mock(VersionedProfile.class);
|
||||
when(profile.commitment()).thenReturn("commitment".getBytes(StandardCharsets.UTF_8));
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(Optional.of(profile)));
|
||||
|
||||
final GetExpiringProfileKeyCredentialAnonymousRequest request = GetExpiringProfileKeyCredentialAnonymousRequest.newBuilder()
|
||||
.setUnidentifiedAccessKey(ByteString.copyFrom(unidentifiedAccessKey))
|
||||
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(identityType)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(credentialType)
|
||||
.setVersion("someVersion")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
final StatusRuntimeException statusRuntimeException = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileAnonymousBlockingStub.getExpiringProfileKeyCredential(request));
|
||||
|
||||
assertEquals(Status.INVALID_ARGUMENT.getCode(), statusRuntimeException.getStatus().getCode());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getExpiringProfileKeyCredentialInvalidArgument() {
|
||||
return Stream.of(
|
||||
// Credential type unspecified
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, CredentialType.CREDENTIAL_TYPE_UNSPECIFIED, false),
|
||||
// Illegal identity type
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_PNI, CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY, false),
|
||||
// Artificially fails zero knowledge verification
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ import org.junit.jupiter.params.provider.ValueSource;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.signal.chat.common.IdentityType;
|
||||
import org.signal.chat.common.ServiceIdentifier;
|
||||
import org.signal.chat.profile.CredentialType;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialRequest;
|
||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.chat.profile.GetUnversionedProfileRequest;
|
||||
import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||
import org.signal.chat.profile.GetVersionedProfileRequest;
|
||||
|
@ -32,7 +35,16 @@ import org.signal.libsignal.protocol.ServiceId;
|
|||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.ServerPublicParams;
|
||||
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||
import org.whispersystems.textsecuregcm.auth.grpc.MockAuthenticationInterceptor;
|
||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||
|
@ -57,15 +69,18 @@ import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
|||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||
import reactor.core.publisher.Mono;
|
||||
import software.amazon.awssdk.services.s3.S3AsyncClient;
|
||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -75,6 +90,8 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatNoException;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -103,6 +120,7 @@ public class ProfileGrpcServiceTest {
|
|||
private Account account;
|
||||
private RateLimiter rateLimiter;
|
||||
private ProfileBadgeConverter profileBadgeConverter;
|
||||
private ServerZkProfileOperations serverZkProfileOperations;
|
||||
private ProfileGrpc.ProfileBlockingStub profileBlockingStub;
|
||||
|
||||
@RegisterExtension
|
||||
|
@ -118,6 +136,7 @@ public class ProfileGrpcServiceTest {
|
|||
account = mock(Account.class);
|
||||
rateLimiter = mock(RateLimiter.class);
|
||||
profileBadgeConverter = mock(ProfileBadgeConverter.class);
|
||||
serverZkProfileOperations = mock(ServerZkProfileOperations.class);
|
||||
|
||||
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
|
@ -160,6 +179,7 @@ public class ProfileGrpcServiceTest {
|
|||
policySigner,
|
||||
profileBadgeConverter,
|
||||
rateLimiters,
|
||||
serverZkProfileOperations,
|
||||
S3_BUCKET
|
||||
);
|
||||
|
||||
|
@ -482,11 +502,11 @@ public class ProfileGrpcServiceTest {
|
|||
@MethodSource
|
||||
void getVersionedProfile(final String requestVersion, @Nullable final String accountVersion, final boolean expectResponseHasPaymentAddress) {
|
||||
final VersionedProfile profile = mock(VersionedProfile.class);
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final String avatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
|
||||
final GetVersionedProfileRequest request = GetVersionedProfileRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
|
@ -594,4 +614,162 @@ public class ProfileGrpcServiceTest {
|
|||
() -> profileBlockingStub.getVersionedProfile(request));
|
||||
assertEquals(Status.INVALID_ARGUMENT.getCode(), exception.getStatus().getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getExpiringProfileKeyCredential() throws InvalidInputException, VerificationFailedException {
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
||||
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
|
||||
|
||||
final ServerZkProfileOperations serverZkProfile = new ServerZkProfileOperations(serverSecretParams);
|
||||
final ClientZkProfileOperations clientZkProfile = new ClientZkProfileOperations(serverPublicParams);
|
||||
|
||||
final byte[] profileKeyBytes = new byte[32];
|
||||
new SecureRandom().nextBytes(profileKeyBytes);
|
||||
|
||||
final ProfileKey profileKey = new ProfileKey(profileKeyBytes);
|
||||
final ProfileKeyCommitment profileKeyCommitment = profileKey.getCommitment(new ServiceId.Aci(targetUuid));
|
||||
final ProfileKeyCredentialRequestContext profileKeyCredentialRequestContext =
|
||||
clientZkProfile.createProfileKeyCredentialRequestContext(new ServiceId.Aci(targetUuid), profileKey);
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(profile.commitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(Optional.of(profile)));
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest = profileKeyCredentialRequestContext.getRequest();
|
||||
|
||||
final Instant expiration = Instant.now().plus(org.whispersystems.textsecuregcm.util.ProfileHelper.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
||||
.truncatedTo(ChronoUnit.DAYS);
|
||||
|
||||
final ExpiringProfileKeyCredentialResponse credentialResponse =
|
||||
serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration);
|
||||
|
||||
when(serverZkProfileOperations.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final GetExpiringProfileKeyCredentialRequest request = GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom(credentialRequest.serialize()))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build();
|
||||
|
||||
final GetExpiringProfileKeyCredentialResponse response = profileBlockingStub.getExpiringProfileKeyCredential(request);
|
||||
|
||||
assertArrayEquals(credentialResponse.serialize(), response.getProfileKeyCredential().toByteArray());
|
||||
|
||||
verify(serverZkProfileOperations).issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(targetUuid), profileKeyCommitment, expiration);
|
||||
|
||||
final ClientZkProfileOperations clientZkProfileCipher = new ClientZkProfileOperations(serverPublicParams);
|
||||
assertThatNoException().isThrownBy(() ->
|
||||
clientZkProfileCipher.receiveExpiringProfileKeyCredential(profileKeyCredentialRequestContext, new ExpiringProfileKeyCredentialResponse(response.getProfileKeyCredential().toByteArray())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getExpiringProfileKeyCredentialRateLimited() {
|
||||
final Duration retryAfterDuration = Duration.ofMinutes(5);
|
||||
when(rateLimiter.validateReactive(AUTHENTICATED_ACI))
|
||||
.thenReturn(Mono.error(new RateLimitExceededException(retryAfterDuration, false)));
|
||||
when(accountsManager.getByServiceIdentifierAsync(any())).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
final GetExpiringProfileKeyCredentialRequest request = GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(UUID.randomUUID())))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build();
|
||||
|
||||
StatusRuntimeException exception = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileBlockingStub.getExpiringProfileKeyCredential(request));
|
||||
|
||||
assertEquals(Status.Code.RESOURCE_EXHAUSTED, exception.getStatus().getCode());
|
||||
assertNotNull(exception.getTrailers());
|
||||
assertEquals(retryAfterDuration, exception.getTrailers().get(RateLimitUtil.RETRY_AFTER_DURATION_KEY));
|
||||
|
||||
verifyNoInteractions(profilesManager);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getExpiringProfileKeyCredentialAccountOrProfileNotFound(final boolean missingAccount,
|
||||
final boolean missingProfile) {
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(CompletableFuture.completedFuture(
|
||||
missingAccount ? Optional.empty() : Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(missingProfile ? Optional.empty() : Optional.of(profile)));
|
||||
|
||||
final GetExpiringProfileKeyCredentialRequest request = GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(IdentityType.IDENTITY_TYPE_ACI)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY)
|
||||
.setVersion("someVersion")
|
||||
.build();
|
||||
|
||||
final StatusRuntimeException statusRuntimeException = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileBlockingStub.getExpiringProfileKeyCredential(request));
|
||||
|
||||
assertEquals(Status.Code.NOT_FOUND, statusRuntimeException.getStatus().getCode());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getExpiringProfileKeyCredentialAccountOrProfileNotFound() {
|
||||
return Stream.of(
|
||||
Arguments.of(true, false),
|
||||
Arguments.of(false, true)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getExpiringProfileKeyCredentialInvalidArgument(final IdentityType identityType, final CredentialType credentialType,
|
||||
final boolean throwZkVerificationException) throws VerificationFailedException {
|
||||
final UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
if (throwZkVerificationException) {
|
||||
when(serverZkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any())).thenThrow(new VerificationFailedException());
|
||||
}
|
||||
|
||||
when(account.getUuid()).thenReturn(targetUuid);
|
||||
when(profile.commitment()).thenReturn("commitment".getBytes(StandardCharsets.UTF_8));
|
||||
when(accountsManager.getByServiceIdentifierAsync(new AciServiceIdentifier(targetUuid))).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
when(profilesManager.getAsync(targetUuid, "someVersion")).thenReturn(CompletableFuture.completedFuture(Optional.of(profile)));
|
||||
|
||||
final GetExpiringProfileKeyCredentialRequest request = GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||
.setIdentityType(identityType)
|
||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(targetUuid)))
|
||||
.build())
|
||||
.setCredentialRequest(ByteString.copyFrom("credentialRequest".getBytes(StandardCharsets.UTF_8)))
|
||||
.setCredentialType(credentialType)
|
||||
.setVersion("someVersion")
|
||||
.build();
|
||||
|
||||
StatusRuntimeException exception = assertThrows(StatusRuntimeException.class,
|
||||
() -> profileBlockingStub.getExpiringProfileKeyCredential(request));
|
||||
|
||||
assertEquals(Status.Code.INVALID_ARGUMENT, exception.getStatus().getCode());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getExpiringProfileKeyCredentialInvalidArgument() {
|
||||
return Stream.of(
|
||||
// Credential type unspecified
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, CredentialType.CREDENTIAL_TYPE_UNSPECIFIED, false),
|
||||
// Illegal identity type
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_PNI, CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY, false),
|
||||
// Artificially fails zero knowledge verification
|
||||
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, CredentialType.CREDENTIAL_TYPE_EXPIRING_PROFILE_KEY, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.signal.libsignal.zkgroup.InvalidInputException;
|
|||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
import org.whispersystems.textsecuregcm.tests.util.MockRedisFuture;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||
|
||||
@Timeout(value = 10, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
|
||||
|
@ -62,12 +62,12 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileInCache() throws InvalidInputException {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(uuid)).serialize();
|
||||
when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(String.format(
|
||||
"{\"version\": \"someversion\", \"name\": \"%s\", \"avatar\": \"someavatar\", \"commitment\":\"%s\"}",
|
||||
ProfileHelper.encodeToBase64(name),
|
||||
ProfileHelper.encodeToBase64(commitment)));
|
||||
ProfileTestHelper.encodeToBase64(name),
|
||||
ProfileTestHelper.encodeToBase64(commitment)));
|
||||
|
||||
Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion");
|
||||
|
||||
|
@ -84,13 +84,13 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileAsyncInCache() throws InvalidInputException {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(uuid)).serialize();
|
||||
|
||||
when(asyncCommands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(
|
||||
MockRedisFuture.completedFuture(String.format("{\"version\": \"someversion\", \"name\": \"%s\", \"avatar\": \"someavatar\", \"commitment\":\"%s\"}",
|
||||
ProfileHelper.encodeToBase64(name),
|
||||
ProfileHelper.encodeToBase64(commitment))));
|
||||
ProfileTestHelper.encodeToBase64(name),
|
||||
ProfileTestHelper.encodeToBase64(commitment))));
|
||||
|
||||
Optional<VersionedProfile> profile = profilesManager.getAsync(uuid, "someversion").join();
|
||||
|
||||
|
@ -107,7 +107,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileNotInCache() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
@ -130,7 +130,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileAsyncNotInCache() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
@ -154,7 +154,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileBrokenCache() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
@ -177,7 +177,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testGetProfileAsyncBrokenCache() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
@ -201,7 +201,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testSet() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
@ -217,7 +217,7 @@ public class ProfilesManagerTest {
|
|||
@Test
|
||||
public void testSetAsync() {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.junit.jupiter.params.provider.MethodSource;
|
|||
import org.signal.libsignal.protocol.ServiceId;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||
|
@ -44,10 +44,10 @@ public class ProfilesTest {
|
|||
Tables.PROFILES.tableName());
|
||||
final byte[] commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final String version = "someVersion";
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] validAboutEmoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] validAbout = ProfileHelper.generateRandomByteArray(156);
|
||||
final String avatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] validAboutEmoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] validAbout = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
|
||||
validProfile = new VersionedProfile(version, name, avatar, validAboutEmoji, validAbout, null, commitment);
|
||||
}
|
||||
|
@ -87,12 +87,12 @@ public class ProfilesTest {
|
|||
profiles.deleteAll(ACI);
|
||||
|
||||
final String version = "someVersion";
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final String differentAvatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] differentEmoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] differentAbout = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final String differentAvatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] differentEmoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] differentAbout = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
VersionedProfile updatedProfile = new VersionedProfile(version, name, differentAvatar,
|
||||
differentEmoji, differentAbout, paymentAddress, commitment);
|
||||
|
@ -112,8 +112,8 @@ public class ProfilesTest {
|
|||
@Test
|
||||
void testSetGetNullOptionalFields() throws InvalidInputException {
|
||||
final String version = "someVersion";
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] commitment = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] commitment = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
VersionedProfile profile = new VersionedProfile(version, name, null, null, null, null,
|
||||
commitment);
|
||||
|
@ -143,11 +143,11 @@ public class ProfilesTest {
|
|||
assertThat(retrieved.get().aboutEmoji()).isEqualTo(validProfile.aboutEmoji());
|
||||
assertThat(retrieved.get().paymentAddress()).isNull();
|
||||
|
||||
final byte[] differentName = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] differentEmoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] differentAbout = ProfileHelper.generateRandomByteArray(156);
|
||||
final String differentAvatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] differentCommitment = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] differentName = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] differentEmoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] differentAbout = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final String differentAvatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
final byte[] differentCommitment = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
VersionedProfile updated = new VersionedProfile(validProfile.version(), differentName, differentAvatar, differentEmoji, differentAbout, null,
|
||||
differentCommitment);
|
||||
|
@ -170,17 +170,17 @@ public class ProfilesTest {
|
|||
final String versionOne = "versionOne";
|
||||
final String versionTwo = "versionTwo";
|
||||
|
||||
final byte[] nameOne = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] nameTwo = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] nameOne = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] nameTwo = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
final String avatarOne = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarTwo = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarOne = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarTwo = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
|
||||
final byte[] aboutEmoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] aboutEmoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
|
||||
final byte[] commitmentOne = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentTwo = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentOne = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentTwo = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null,
|
||||
null, commitmentOne);
|
||||
|
@ -223,17 +223,17 @@ public class ProfilesTest {
|
|||
final String versionOne = "versionOne";
|
||||
final String versionTwo = "versionTwo";
|
||||
|
||||
final byte[] nameOne = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] nameTwo = ProfileHelper.generateRandomByteArray(81);
|
||||
final byte[] nameOne = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final byte[] nameTwo = ProfileTestHelper.generateRandomByteArray(81);
|
||||
|
||||
final byte[] aboutEmoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] aboutEmoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
|
||||
final String avatarOne = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarTwo = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarOne = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
final String avatarTwo = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);
|
||||
|
||||
final byte[] commitmentOne = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentTwo = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentOne = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] commitmentTwo = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null,
|
||||
null, commitmentOne);
|
||||
|
@ -261,12 +261,12 @@ public class ProfilesTest {
|
|||
|
||||
private static Stream<Arguments> buildUpdateExpression() throws InvalidInputException {
|
||||
final String version = "someVersion";
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final String avatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);;
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);;
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
|
@ -303,12 +303,12 @@ public class ProfilesTest {
|
|||
|
||||
private static Stream<Arguments> buildUpdateExpressionAttributeValues() throws InvalidInputException {
|
||||
final String version = "someVersion";
|
||||
final byte[] name = ProfileHelper.generateRandomByteArray(81);
|
||||
final String avatar = "profiles/" + ProfileHelper.generateRandomBase64FromByteArray(16);;
|
||||
final byte[] emoji = ProfileHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
final byte[] name = ProfileTestHelper.generateRandomByteArray(81);
|
||||
final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16);;
|
||||
final byte[] emoji = ProfileTestHelper.generateRandomByteArray(60);
|
||||
final byte[] about = ProfileTestHelper.generateRandomByteArray(156);
|
||||
final byte[] paymentAddress = ProfileTestHelper.generateRandomByteArray(582);
|
||||
final byte[] commitment = new ProfileKey(ProfileTestHelper.generateRandomByteArray(32)).getCommitment(new ServiceId.Aci(ACI)).serialize();
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.whispersystems.textsecuregcm.tests.util;
|
|||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
public class ProfileHelper {
|
||||
public class ProfileTestHelper {
|
||||
public static String generateRandomBase64FromByteArray(final int byteArrayLength) {
|
||||
return encodeToBase64(generateRandomByteArray(byteArrayLength));
|
||||
}
|
Loading…
Reference in New Issue