Group Send Endorsement support for unversioned profile fetch
This commit is contained in:
parent
9ef1fee172
commit
f0dcd8e07b
|
@ -798,7 +798,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
.addService(new KeysAnonymousGrpcService(accountsManager, keysManager, zkSecretParams, Clock.systemUTC()))
|
.addService(new KeysAnonymousGrpcService(accountsManager, keysManager, zkSecretParams, Clock.systemUTC()))
|
||||||
.addService(new PaymentsGrpcService(currencyManager))
|
.addService(new PaymentsGrpcService(currencyManager))
|
||||||
.addService(ExternalServiceCredentialsAnonymousGrpcService.create(accountsManager, config))
|
.addService(ExternalServiceCredentialsAnonymousGrpcService.create(accountsManager, config))
|
||||||
.addService(new ProfileAnonymousGrpcService(accountsManager, profilesManager, profileBadgeConverter, zkProfileOperations));
|
.addService(new ProfileAnonymousGrpcService(accountsManager, profilesManager, profileBadgeConverter, zkSecretParams));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -969,7 +969,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
|
||||||
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
|
||||||
config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
config.getCdnConfiguration().bucket(), zkSecretParams, zkProfileOperations, batchIdentityCheckExecutor),
|
||||||
new ProvisioningController(rateLimiters, provisioningManager),
|
new ProvisioningController(rateLimiters, provisioningManager),
|
||||||
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
|
||||||
rateLimiters),
|
rateLimiters),
|
||||||
|
|
|
@ -58,13 +58,17 @@ import org.glassfish.jersey.server.ManagedAsync;
|
||||||
import org.signal.libsignal.protocol.IdentityKey;
|
import org.signal.libsignal.protocol.IdentityKey;
|
||||||
import org.signal.libsignal.protocol.ServiceId;
|
import org.signal.libsignal.protocol.ServiceId;
|
||||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||||
|
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||||
|
import org.signal.libsignal.zkgroup.groupsend.GroupSendDerivedKeyPair;
|
||||||
|
import org.signal.libsignal.zkgroup.groupsend.GroupSendFullToken;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.Anonymous;
|
import org.whispersystems.textsecuregcm.auth.Anonymous;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.GroupSendTokenHeader;
|
||||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||||
|
@ -109,19 +113,20 @@ import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||||
public class ProfileController {
|
public class ProfileController {
|
||||||
private final Logger logger = LoggerFactory.getLogger(ProfileController.class);
|
private final Logger logger = LoggerFactory.getLogger(ProfileController.class);
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final ProfilesManager profilesManager;
|
private final ProfilesManager profilesManager;
|
||||||
private final AccountsManager accountsManager;
|
private final AccountsManager accountsManager;
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
private final ProfileBadgeConverter profileBadgeConverter;
|
private final ProfileBadgeConverter profileBadgeConverter;
|
||||||
private final Map<String, BadgeConfiguration> badgeConfigurationMap;
|
private final Map<String, BadgeConfiguration> badgeConfigurationMap;
|
||||||
|
|
||||||
private final PolicySigner policySigner;
|
private final PolicySigner policySigner;
|
||||||
private final PostPolicyGenerator policyGenerator;
|
private final PostPolicyGenerator policyGenerator;
|
||||||
|
private final ServerSecretParams serverSecretParams;
|
||||||
private final ServerZkProfileOperations zkProfileOperations;
|
private final ServerZkProfileOperations zkProfileOperations;
|
||||||
|
|
||||||
private final S3Client s3client;
|
private final S3Client s3client;
|
||||||
private final String bucket;
|
private final String bucket;
|
||||||
|
|
||||||
private final Executor batchIdentityCheckExecutor;
|
private final Executor batchIdentityCheckExecutor;
|
||||||
|
|
||||||
|
@ -142,21 +147,23 @@ public class ProfileController {
|
||||||
PostPolicyGenerator policyGenerator,
|
PostPolicyGenerator policyGenerator,
|
||||||
PolicySigner policySigner,
|
PolicySigner policySigner,
|
||||||
String bucket,
|
String bucket,
|
||||||
|
ServerSecretParams serverSecretParams,
|
||||||
ServerZkProfileOperations zkProfileOperations,
|
ServerZkProfileOperations zkProfileOperations,
|
||||||
Executor batchIdentityCheckExecutor) {
|
Executor batchIdentityCheckExecutor) {
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.accountsManager = accountsManager;
|
this.accountsManager = accountsManager;
|
||||||
this.profilesManager = profilesManager;
|
this.profilesManager = profilesManager;
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
this.profileBadgeConverter = profileBadgeConverter;
|
this.profileBadgeConverter = profileBadgeConverter;
|
||||||
this.badgeConfigurationMap = badgesConfiguration.getBadges().stream().collect(Collectors.toMap(
|
this.badgeConfigurationMap = badgesConfiguration.getBadges().stream().collect(Collectors.toMap(
|
||||||
BadgeConfiguration::getId, Function.identity()));
|
BadgeConfiguration::getId, Function.identity()));
|
||||||
|
this.serverSecretParams = serverSecretParams;
|
||||||
this.zkProfileOperations = zkProfileOperations;
|
this.zkProfileOperations = zkProfileOperations;
|
||||||
this.bucket = bucket;
|
this.bucket = bucket;
|
||||||
this.s3client = s3client;
|
this.s3client = s3client;
|
||||||
this.policyGenerator = policyGenerator;
|
this.policyGenerator = policyGenerator;
|
||||||
this.policySigner = policySigner;
|
this.policySigner = policySigner;
|
||||||
this.batchIdentityCheckExecutor = Preconditions.checkNotNull(batchIdentityCheckExecutor);
|
this.batchIdentityCheckExecutor = Preconditions.checkNotNull(batchIdentityCheckExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +289,7 @@ public class ProfileController {
|
||||||
public BaseProfileResponse getUnversionedProfile(
|
public BaseProfileResponse getUnversionedProfile(
|
||||||
@ReadOnly @Auth Optional<AuthenticatedAccount> auth,
|
@ReadOnly @Auth Optional<AuthenticatedAccount> auth,
|
||||||
@HeaderParam(HeaderUtils.UNIDENTIFIED_ACCESS_KEY) Optional<Anonymous> accessKey,
|
@HeaderParam(HeaderUtils.UNIDENTIFIED_ACCESS_KEY) Optional<Anonymous> accessKey,
|
||||||
|
@HeaderParam(HeaderUtils.GROUP_SEND_TOKEN) Optional<GroupSendTokenHeader> groupSendToken,
|
||||||
@Context ContainerRequestContext containerRequestContext,
|
@Context ContainerRequestContext containerRequestContext,
|
||||||
@HeaderParam(HttpHeaders.USER_AGENT) String userAgent,
|
@HeaderParam(HttpHeaders.USER_AGENT) String userAgent,
|
||||||
@PathParam("identifier") ServiceIdentifier identifier,
|
@PathParam("identifier") ServiceIdentifier identifier,
|
||||||
|
@ -290,8 +298,22 @@ public class ProfileController {
|
||||||
|
|
||||||
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
|
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
|
||||||
|
|
||||||
final Account targetAccount = verifyPermissionToReceiveProfile(
|
final Account targetAccount;
|
||||||
maybeRequester, accessKey.filter(ignored -> identifier.identityType() == IdentityType.ACI), identifier);
|
if (groupSendToken.isPresent()) {
|
||||||
|
if (accessKey.isPresent()) {
|
||||||
|
throw new BadRequestException("may not provide both group send token and unidentified access key");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final GroupSendFullToken token = groupSendToken.get().token();
|
||||||
|
token.verify(List.of(identifier.toLibsignal()), clock.instant(), GroupSendDerivedKeyPair.forExpiration(token.getExpiration(), serverSecretParams));
|
||||||
|
targetAccount = accountsManager.getByServiceIdentifier(identifier).orElseThrow(NotFoundException::new);
|
||||||
|
} catch (VerificationFailedException e) {
|
||||||
|
throw new NotAuthorizedException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetAccount = verifyPermissionToReceiveProfile(
|
||||||
|
maybeRequester, accessKey.filter(ignored -> identifier.identityType() == IdentityType.ACI), identifier);
|
||||||
|
}
|
||||||
return switch (identifier.identityType()) {
|
return switch (identifier.identityType()) {
|
||||||
case ACI -> {
|
case ACI -> {
|
||||||
yield buildBaseProfileResponseForAccountIdentity(targetAccount,
|
yield buildBaseProfileResponseForAccountIdentity(targetAccount,
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
package org.whispersystems.textsecuregcm.grpc;
|
package org.whispersystems.textsecuregcm.grpc;
|
||||||
|
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
|
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.signal.chat.profile.CredentialType;
|
import org.signal.chat.profile.CredentialType;
|
||||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialAnonymousRequest;
|
import org.signal.chat.profile.GetExpiringProfileKeyCredentialAnonymousRequest;
|
||||||
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
import org.signal.chat.profile.GetExpiringProfileKeyCredentialResponse;
|
||||||
|
@ -14,6 +18,7 @@ import org.signal.chat.profile.GetUnversionedProfileResponse;
|
||||||
import org.signal.chat.profile.GetVersionedProfileAnonymousRequest;
|
import org.signal.chat.profile.GetVersionedProfileAnonymousRequest;
|
||||||
import org.signal.chat.profile.GetVersionedProfileResponse;
|
import org.signal.chat.profile.GetVersionedProfileResponse;
|
||||||
import org.signal.chat.profile.ReactorProfileAnonymousGrpc;
|
import org.signal.chat.profile.ReactorProfileAnonymousGrpc;
|
||||||
|
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
|
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
|
||||||
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||||
|
@ -29,16 +34,18 @@ public class ProfileAnonymousGrpcService extends ReactorProfileAnonymousGrpc.Pro
|
||||||
private final ProfilesManager profilesManager;
|
private final ProfilesManager profilesManager;
|
||||||
private final ProfileBadgeConverter profileBadgeConverter;
|
private final ProfileBadgeConverter profileBadgeConverter;
|
||||||
private final ServerZkProfileOperations zkProfileOperations;
|
private final ServerZkProfileOperations zkProfileOperations;
|
||||||
|
private final GroupSendTokenUtil groupSendTokenUtil;
|
||||||
|
|
||||||
public ProfileAnonymousGrpcService(
|
public ProfileAnonymousGrpcService(
|
||||||
final AccountsManager accountsManager,
|
final AccountsManager accountsManager,
|
||||||
final ProfilesManager profilesManager,
|
final ProfilesManager profilesManager,
|
||||||
final ProfileBadgeConverter profileBadgeConverter,
|
final ProfileBadgeConverter profileBadgeConverter,
|
||||||
final ServerZkProfileOperations zkProfileOperations) {
|
final ServerSecretParams serverSecretParams) {
|
||||||
this.accountsManager = accountsManager;
|
this.accountsManager = accountsManager;
|
||||||
this.profilesManager = profilesManager;
|
this.profilesManager = profilesManager;
|
||||||
this.profileBadgeConverter = profileBadgeConverter;
|
this.profileBadgeConverter = profileBadgeConverter;
|
||||||
this.zkProfileOperations = zkProfileOperations;
|
this.zkProfileOperations = new ServerZkProfileOperations(serverSecretParams);
|
||||||
|
this.groupSendTokenUtil = new GroupSendTokenUtil(serverSecretParams, Clock.systemUTC());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,8 +58,18 @@ public class ProfileAnonymousGrpcService extends ReactorProfileAnonymousGrpc.Pro
|
||||||
throw Status.UNAUTHENTICATED.asRuntimeException();
|
throw Status.UNAUTHENTICATED.asRuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getTargetAccountAndValidateUnidentifiedAccess(targetIdentifier, request.getUnidentifiedAccessKey().toByteArray())
|
final Mono<Account> account = switch (request.getAuthenticationCase()) {
|
||||||
.map(targetAccount -> ProfileGrpcHelper.buildUnversionedProfileResponse(targetIdentifier,
|
case GROUP_SEND_TOKEN ->
|
||||||
|
groupSendTokenUtil.checkGroupSendToken(request.getGroupSendToken(), List.of(targetIdentifier))
|
||||||
|
.then(Mono.fromFuture(() -> accountsManager.getByServiceIdentifierAsync(targetIdentifier)))
|
||||||
|
.flatMap(Mono::justOrEmpty)
|
||||||
|
.switchIfEmpty(Mono.error(Status.NOT_FOUND.asException()));
|
||||||
|
case UNIDENTIFIED_ACCESS_KEY ->
|
||||||
|
getTargetAccountAndValidateUnidentifiedAccess(targetIdentifier, request.getUnidentifiedAccessKey().toByteArray());
|
||||||
|
default -> Mono.error(Status.INVALID_ARGUMENT.asException());
|
||||||
|
};
|
||||||
|
|
||||||
|
return account.map(targetAccount -> ProfileGrpcHelper.buildUnversionedProfileResponse(targetIdentifier,
|
||||||
null,
|
null,
|
||||||
targetAccount,
|
targetAccount,
|
||||||
profileBadgeConverter));
|
profileBadgeConverter));
|
||||||
|
|
|
@ -211,10 +211,18 @@ message GetUnversionedProfileAnonymousRequest {
|
||||||
* Contains the data necessary to request an unversioned profile.
|
* Contains the data necessary to request an unversioned profile.
|
||||||
*/
|
*/
|
||||||
GetUnversionedProfileRequest request = 1;
|
GetUnversionedProfileRequest request = 1;
|
||||||
/**
|
|
||||||
* The unidentified access key for the targeted account.
|
oneof authentication {
|
||||||
*/
|
/**
|
||||||
bytes unidentified_access_key = 2;
|
* The unidentified access key for the targeted account.
|
||||||
|
*/
|
||||||
|
bytes unidentified_access_key = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group send endorsement token for the targeted account.
|
||||||
|
*/
|
||||||
|
bytes group_send_token = 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetUnversionedProfileResponse {
|
message GetUnversionedProfileResponse {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.Invocation;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedHashMap;
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
@ -117,7 +118,7 @@ import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||||
class ProfileControllerTest {
|
class ProfileControllerTest {
|
||||||
|
|
||||||
private static final Clock clock = TestClock.pinned(Instant.ofEpochSecond(42));
|
private static final TestClock clock = TestClock.now();
|
||||||
private static final AccountsManager accountsManager = mock(AccountsManager.class);
|
private static final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
private static final ProfilesManager profilesManager = mock(ProfilesManager.class);
|
private static final ProfilesManager profilesManager = mock(ProfilesManager.class);
|
||||||
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
private static final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||||
|
@ -129,6 +130,7 @@ class ProfileControllerTest {
|
||||||
"accessKey");
|
"accessKey");
|
||||||
private static final PolicySigner policySigner = new PolicySigner("accessSecret", "us-west-1");
|
private static final PolicySigner policySigner = new PolicySigner("accessSecret", "us-west-1");
|
||||||
private static final ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class);
|
private static final ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class);
|
||||||
|
private static final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
||||||
|
|
||||||
private static final byte[] UNIDENTIFIED_ACCESS_KEY = "sixteenbytes1234".getBytes(StandardCharsets.UTF_8);
|
private static final byte[] UNIDENTIFIED_ACCESS_KEY = "sixteenbytes1234".getBytes(StandardCharsets.UTF_8);
|
||||||
private static final IdentityKey ACCOUNT_IDENTITY_KEY = new IdentityKey(Curve.generateKeyPair().getPublicKey());
|
private static final IdentityKey ACCOUNT_IDENTITY_KEY = new IdentityKey(Curve.generateKeyPair().getPublicKey());
|
||||||
|
@ -170,6 +172,7 @@ class ProfileControllerTest {
|
||||||
postPolicyGenerator,
|
postPolicyGenerator,
|
||||||
policySigner,
|
policySigner,
|
||||||
"profilesBucket",
|
"profilesBucket",
|
||||||
|
serverSecretParams,
|
||||||
zkProfileOperations,
|
zkProfileOperations,
|
||||||
Executors.newSingleThreadExecutor()))
|
Executors.newSingleThreadExecutor()))
|
||||||
.build();
|
.build();
|
||||||
|
@ -177,7 +180,7 @@ class ProfileControllerTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
reset(s3client);
|
reset(s3client);
|
||||||
|
clock.pin(Instant.ofEpochSecond(42));
|
||||||
AccountsHelper.setupMockUpdate(accountsManager);
|
AccountsHelper.setupMockUpdate(accountsManager);
|
||||||
|
|
||||||
dynamicPaymentsConfiguration = mock(DynamicPaymentsConfiguration.class);
|
dynamicPaymentsConfiguration = mock(DynamicPaymentsConfiguration.class);
|
||||||
|
@ -311,6 +314,65 @@ class ProfileControllerTest {
|
||||||
assertThat(response.getStatus()).isEqualTo(401);
|
assertThat(response.getStatus()).isEqualTo(401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource
|
||||||
|
void testProfileGetWithGroupSendEndorsement(
|
||||||
|
UUID target, UUID authorizedTarget, Duration timeLeft, boolean includeUak, int expectedResponse) throws Exception {
|
||||||
|
|
||||||
|
final Instant expiration = Instant.now().truncatedTo(ChronoUnit.DAYS);
|
||||||
|
clock.pin(expiration.minus(timeLeft));
|
||||||
|
|
||||||
|
Invocation.Builder builder = resources.getJerseyTest()
|
||||||
|
.target("/v1/profile/" + target)
|
||||||
|
.queryParam("pq", "true")
|
||||||
|
.request()
|
||||||
|
.header(
|
||||||
|
HeaderUtils.GROUP_SEND_TOKEN,
|
||||||
|
AuthHelper.validGroupSendTokenHeader(serverSecretParams, List.of(new AciServiceIdentifier(authorizedTarget)), expiration));
|
||||||
|
|
||||||
|
if (includeUak) {
|
||||||
|
builder = builder.header(HeaderUtils.UNIDENTIFIED_ACCESS_KEY, AuthHelper.getUnidentifiedAccessHeader(UNIDENTIFIED_ACCESS_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
Response response = builder.get();
|
||||||
|
assertThat(response.getStatus()).isEqualTo(expectedResponse);
|
||||||
|
|
||||||
|
if (expectedResponse == 200) {
|
||||||
|
final BaseProfileResponse profile = response.readEntity(BaseProfileResponse.class);
|
||||||
|
assertThat(profile.getIdentityKey()).isEqualTo(ACCOUNT_TWO_IDENTITY_KEY);
|
||||||
|
assertThat(profile.getBadges()).hasSize(1).element(0).has(new Condition<>(
|
||||||
|
badge -> "Test Badge".equals(badge.getName()), "has badge with expected name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(rateLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> testProfileGetWithGroupSendEndorsement() {
|
||||||
|
UUID notExistsUuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
// valid endorsement
|
||||||
|
Arguments.of(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_UUID_TWO, Duration.ofHours(1), false, 200),
|
||||||
|
|
||||||
|
// expired endorsement, not authorized
|
||||||
|
Arguments.of(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_UUID_TWO, Duration.ofHours(-1), false, 401),
|
||||||
|
|
||||||
|
// endorsement for the wrong recipient, not authorized
|
||||||
|
Arguments.of(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_UUID, Duration.ofHours(1), false, 401),
|
||||||
|
|
||||||
|
// expired endorsement for the wrong recipient, not authorized
|
||||||
|
Arguments.of(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_UUID, Duration.ofHours(-1), false, 401),
|
||||||
|
|
||||||
|
// valid endorsement for the right recipient but they aren't registered, not found
|
||||||
|
Arguments.of(notExistsUuid, notExistsUuid, Duration.ofHours(1), false, 404),
|
||||||
|
|
||||||
|
// expired endorsement for the right recipient but they aren't registered, not authorized (NOT not found)
|
||||||
|
Arguments.of(notExistsUuid, notExistsUuid, Duration.ofHours(-1), false, 401),
|
||||||
|
|
||||||
|
// valid endorsement but also a UAK, bad request
|
||||||
|
Arguments.of(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_UUID_TWO, Duration.ofHours(1), true, 400));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProfileGetByPni() throws RateLimitExceededException {
|
void testProfileGetByPni() throws RateLimitExceededException {
|
||||||
final BaseProfileResponse profile = resources.getJerseyTest()
|
final BaseProfileResponse profile = resources.getJerseyTest()
|
||||||
|
|
|
@ -19,6 +19,7 @@ import static org.whispersystems.textsecuregcm.grpc.GrpcTestUtils.assertStatusEx
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -73,7 +74,9 @@ import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||||
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
import org.whispersystems.textsecuregcm.tests.util.ProfileTestHelper;
|
||||||
|
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||||
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
|
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
|
||||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||||
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
|
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
|
||||||
|
@ -81,6 +84,8 @@ import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
|
||||||
|
|
||||||
public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileAnonymousGrpcService, ProfileAnonymousGrpc.ProfileAnonymousBlockingStub> {
|
public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileAnonymousGrpcService, ProfileAnonymousGrpc.ProfileAnonymousBlockingStub> {
|
||||||
|
|
||||||
|
private final ServerSecretParams SERVER_SECRET_PARAMS = ServerSecretParams.generate();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Account account;
|
private Account account;
|
||||||
|
|
||||||
|
@ -93,10 +98,6 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
@Mock
|
@Mock
|
||||||
private ProfileBadgeConverter profileBadgeConverter;
|
private ProfileBadgeConverter profileBadgeConverter;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ServerZkProfileOperations serverZkProfileOperations;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ProfileAnonymousGrpcService createServiceBeforeEachTest() {
|
protected ProfileAnonymousGrpcService createServiceBeforeEachTest() {
|
||||||
getMockRequestAttributesInterceptor().setAcceptLanguage(Locale.LanguageRange.parse("en-us"));
|
getMockRequestAttributesInterceptor().setAcceptLanguage(Locale.LanguageRange.parse("en-us"));
|
||||||
|
@ -111,12 +112,12 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
accountsManager,
|
accountsManager,
|
||||||
profilesManager,
|
profilesManager,
|
||||||
profileBadgeConverter,
|
profileBadgeConverter,
|
||||||
serverZkProfileOperations
|
SERVER_SECRET_PARAMS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getUnversionedProfile() {
|
void getUnversionedProfileUnidentifiedAccessKey() {
|
||||||
final UUID targetUuid = UUID.randomUUID();
|
final UUID targetUuid = UUID.randomUUID();
|
||||||
final org.whispersystems.textsecuregcm.identity.ServiceIdentifier serviceIdentifier = new AciServiceIdentifier(targetUuid);
|
final org.whispersystems.textsecuregcm.identity.ServiceIdentifier serviceIdentifier = new AciServiceIdentifier(targetUuid);
|
||||||
|
|
||||||
|
@ -169,9 +170,71 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
assertEquals(expectedResponse, response);
|
assertEquals(expectedResponse, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUnversionedProfileGroupSendEndorsement() throws Exception {
|
||||||
|
final UUID targetUuid = UUID.randomUUID();
|
||||||
|
final org.whispersystems.textsecuregcm.identity.ServiceIdentifier serviceIdentifier = new AciServiceIdentifier(targetUuid);
|
||||||
|
|
||||||
|
// Expiration must be on a day boundary; we want one in the future
|
||||||
|
final Instant expiration = Instant.now().plus(Duration.ofDays(1)).truncatedTo(ChronoUnit.DAYS);
|
||||||
|
final byte[] token = AuthHelper.validGroupSendToken(SERVER_SECRET_PARAMS, List.of(serviceIdentifier), expiration);
|
||||||
|
|
||||||
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
|
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
|
||||||
|
|
||||||
|
final List<Badge> badges = List.of(new Badge(
|
||||||
|
"TEST",
|
||||||
|
"other",
|
||||||
|
"Test Badge",
|
||||||
|
"This badge is in unit tests.",
|
||||||
|
List.of("l", "m", "h", "x", "xx", "xxx"),
|
||||||
|
"SVG",
|
||||||
|
List.of(
|
||||||
|
new BadgeSvg("sl", "sd"),
|
||||||
|
new BadgeSvg("ml", "md"),
|
||||||
|
new BadgeSvg("ll", "ld")))
|
||||||
|
);
|
||||||
|
|
||||||
|
when(account.getBadges()).thenReturn(Collections.emptyList());
|
||||||
|
when(profileBadgeConverter.convert(any(), any(), anyBoolean())).thenReturn(badges);
|
||||||
|
when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false);
|
||||||
|
when(account.getIdentityKey(org.whispersystems.textsecuregcm.identity.IdentityType.ACI)).thenReturn(identityKey);
|
||||||
|
when(accountsManager.getByServiceIdentifierAsync(serviceIdentifier)).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||||
|
|
||||||
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setGroupSendToken(ByteString.copyFrom(token))
|
||||||
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
|
.setServiceIdentifier(
|
||||||
|
ServiceIdentifierUtil.toGrpcServiceIdentifier(serviceIdentifier))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final GetUnversionedProfileResponse response = unauthenticatedServiceStub().getUnversionedProfile(request);
|
||||||
|
|
||||||
|
final GetUnversionedProfileResponse expectedResponse = GetUnversionedProfileResponse.newBuilder()
|
||||||
|
.setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
|
||||||
|
.setUnrestrictedUnidentifiedAccess(false)
|
||||||
|
.setCapabilities(ProfileGrpcHelper.buildUserCapabilities(new UserCapabilities()))
|
||||||
|
.addAllBadges(ProfileGrpcHelper.buildBadges(badges))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
verify(accountsManager).getByServiceIdentifierAsync(serviceIdentifier);
|
||||||
|
assertEquals(expectedResponse, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUnversionedProfileNoAuth() {
|
||||||
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
|
.setServiceIdentifier(ServiceIdentifierUtil.toGrpcServiceIdentifier(new AciServiceIdentifier(UUID.randomUUID()))))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertStatusException(Status.INVALID_ARGUMENT, () -> unauthenticatedServiceStub().getUnversionedProfile(request));
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource
|
@MethodSource
|
||||||
void getUnversionedProfileUnauthenticated(final IdentityType identityType, final boolean missingUnidentifiedAccessKey, final boolean accountNotFound) {
|
void getUnversionedProfileIncorrectUnidentifiedAccessKey(final IdentityType identityType, final boolean wrongUnidentifiedAccessKey, final boolean accountNotFound) {
|
||||||
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
||||||
|
|
||||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||||
|
@ -179,22 +242,21 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
when(accountsManager.getByServiceIdentifierAsync(any())).thenReturn(
|
when(accountsManager.getByServiceIdentifierAsync(any())).thenReturn(
|
||||||
CompletableFuture.completedFuture(accountNotFound ? Optional.empty() : Optional.of(account)));
|
CompletableFuture.completedFuture(accountNotFound ? Optional.empty() : Optional.of(account)));
|
||||||
|
|
||||||
final GetUnversionedProfileAnonymousRequest.Builder requestBuilder = GetUnversionedProfileAnonymousRequest.newBuilder()
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setUnidentifiedAccessKey(
|
||||||
|
ByteString.copyFrom(wrongUnidentifiedAccessKey
|
||||||
|
? new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]
|
||||||
|
: unidentifiedAccessKey))
|
||||||
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
.setServiceIdentifier(ServiceIdentifier.newBuilder()
|
.setServiceIdentifier(ServiceIdentifier.newBuilder()
|
||||||
.setIdentityType(identityType)
|
.setIdentityType(identityType)
|
||||||
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(UUID.randomUUID())))
|
.setUuid(ByteString.copyFrom(UUIDUtil.toBytes(UUID.randomUUID())))))
|
||||||
.build())
|
.build();
|
||||||
.build());
|
|
||||||
|
|
||||||
if (!missingUnidentifiedAccessKey) {
|
assertStatusException(Status.UNAUTHENTICATED, () -> unauthenticatedServiceStub().getUnversionedProfile(request));
|
||||||
requestBuilder.setUnidentifiedAccessKey(ByteString.copyFrom(unidentifiedAccessKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertStatusException(Status.UNAUTHENTICATED, () -> unauthenticatedServiceStub().getUnversionedProfile(requestBuilder.build()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<Arguments> getUnversionedProfileUnauthenticated() {
|
private static Stream<Arguments> getUnversionedProfileIncorrectUnidentifiedAccessKey() {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
Arguments.of(IdentityType.IDENTITY_TYPE_PNI, false, false),
|
Arguments.of(IdentityType.IDENTITY_TYPE_PNI, false, false),
|
||||||
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, true, false),
|
Arguments.of(IdentityType.IDENTITY_TYPE_ACI, true, false),
|
||||||
|
@ -202,6 +264,62 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUnversionedProfileExpiredGroupSendEndorsement() throws Exception {
|
||||||
|
final AciServiceIdentifier serviceIdentifier = new AciServiceIdentifier(UUID.randomUUID());
|
||||||
|
// Expirations must be on a day boundary; pick one in the recent past
|
||||||
|
final Instant expiration = Instant.now().truncatedTo(ChronoUnit.DAYS);
|
||||||
|
final byte[] token = AuthHelper.validGroupSendToken(SERVER_SECRET_PARAMS, List.of(serviceIdentifier), expiration);
|
||||||
|
|
||||||
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setGroupSendToken(ByteString.copyFrom(token))
|
||||||
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
|
.setServiceIdentifier(
|
||||||
|
ServiceIdentifierUtil.toGrpcServiceIdentifier(serviceIdentifier)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertStatusException(Status.UNAUTHENTICATED, () -> unauthenticatedServiceStub().getUnversionedProfile(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUnversionedProfileIncorrectGroupSendEndorsement() throws Exception {
|
||||||
|
final AciServiceIdentifier targetServiceIdentifier = new AciServiceIdentifier(UUID.randomUUID());
|
||||||
|
final AciServiceIdentifier authorizedServiceIdentifier = new AciServiceIdentifier(UUID.randomUUID());
|
||||||
|
|
||||||
|
// Expiration must be on a day boundary; we want one in the future
|
||||||
|
final Instant expiration = Instant.now().plus(Duration.ofDays(1)).truncatedTo(ChronoUnit.DAYS);
|
||||||
|
final byte[] token = AuthHelper.validGroupSendToken(SERVER_SECRET_PARAMS, List.of(authorizedServiceIdentifier), expiration);
|
||||||
|
|
||||||
|
when(accountsManager.getByServiceIdentifierAsync(any())).thenReturn(
|
||||||
|
CompletableFuture.completedFuture(Optional.empty()));
|
||||||
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setGroupSendToken(ByteString.copyFrom(token))
|
||||||
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
|
.setServiceIdentifier(
|
||||||
|
ServiceIdentifierUtil.toGrpcServiceIdentifier(targetServiceIdentifier)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertStatusException(Status.UNAUTHENTICATED, () -> unauthenticatedServiceStub().getUnversionedProfile(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUnversionedProfileGroupSendEndorsementAccountNotFound() throws Exception {
|
||||||
|
final AciServiceIdentifier serviceIdentifier = new AciServiceIdentifier(UUID.randomUUID());
|
||||||
|
|
||||||
|
// Expiration must be on a day boundary; we want one in the future
|
||||||
|
final Instant expiration = Instant.now().plus(Duration.ofDays(1)).truncatedTo(ChronoUnit.DAYS);
|
||||||
|
final byte[] token = AuthHelper.validGroupSendToken(SERVER_SECRET_PARAMS, List.of(serviceIdentifier), expiration);
|
||||||
|
|
||||||
|
when(accountsManager.getByServiceIdentifierAsync(any())).thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||||
|
final GetUnversionedProfileAnonymousRequest request = GetUnversionedProfileAnonymousRequest.newBuilder()
|
||||||
|
.setGroupSendToken(ByteString.copyFrom(token))
|
||||||
|
.setRequest(GetUnversionedProfileRequest.newBuilder()
|
||||||
|
.setServiceIdentifier(ServiceIdentifierUtil.toGrpcServiceIdentifier(serviceIdentifier)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertStatusException(Status.NOT_FOUND, () -> unauthenticatedServiceStub().getUnversionedProfile(request));
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource
|
@MethodSource
|
||||||
void getVersionedProfile(final String requestVersion,
|
void getVersionedProfile(final String requestVersion,
|
||||||
|
@ -343,11 +461,7 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
||||||
final UUID targetUuid = UUID.randomUUID();
|
final UUID targetUuid = UUID.randomUUID();
|
||||||
|
|
||||||
final ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
final ClientZkProfileOperations clientZkProfile = new ClientZkProfileOperations(SERVER_SECRET_PARAMS.getPublicParams());
|
||||||
final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams();
|
|
||||||
|
|
||||||
final ServerZkProfileOperations serverZkProfile = new ServerZkProfileOperations(serverSecretParams);
|
|
||||||
final ClientZkProfileOperations clientZkProfile = new ClientZkProfileOperations(serverPublicParams);
|
|
||||||
|
|
||||||
final byte[] profileKeyBytes = TestRandomUtil.nextBytes(32);
|
final byte[] profileKeyBytes = TestRandomUtil.nextBytes(32);
|
||||||
final ProfileKey profileKey = new ProfileKey(profileKeyBytes);
|
final ProfileKey profileKey = new ProfileKey(profileKeyBytes);
|
||||||
|
@ -368,12 +482,6 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
final Instant expiration = Instant.now().plus(org.whispersystems.textsecuregcm.util.ProfileHelper.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
final Instant expiration = Instant.now().plus(org.whispersystems.textsecuregcm.util.ProfileHelper.EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION)
|
||||||
.truncatedTo(ChronoUnit.DAYS);
|
.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()
|
final GetExpiringProfileKeyCredentialAnonymousRequest request = GetExpiringProfileKeyCredentialAnonymousRequest.newBuilder()
|
||||||
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
.setRequest(GetExpiringProfileKeyCredentialRequest.newBuilder()
|
||||||
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
.setAccountIdentifier(ServiceIdentifier.newBuilder()
|
||||||
|
@ -389,13 +497,8 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
|
|
||||||
final GetExpiringProfileKeyCredentialResponse response = unauthenticatedServiceStub().getExpiringProfileKeyCredential(request);
|
final GetExpiringProfileKeyCredentialResponse response = unauthenticatedServiceStub().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(() ->
|
assertThatNoException().isThrownBy(() ->
|
||||||
clientZkProfileCipher.receiveExpiringProfileKeyCredential(profileKeyCredentialRequestContext, new ExpiringProfileKeyCredentialResponse(response.getProfileKeyCredential().toByteArray())));
|
clientZkProfile.receiveExpiringProfileKeyCredential(profileKeyCredentialRequestContext, new ExpiringProfileKeyCredentialResponse(response.getProfileKeyCredential().toByteArray())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -471,10 +574,6 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
|
||||||
final UUID targetUuid = UUID.randomUUID();
|
final UUID targetUuid = UUID.randomUUID();
|
||||||
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
final byte[] unidentifiedAccessKey = TestRandomUtil.nextBytes(UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH);
|
||||||
|
|
||||||
if (throwZkVerificationException) {
|
|
||||||
when(serverZkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any())).thenThrow(new VerificationFailedException());
|
|
||||||
}
|
|
||||||
|
|
||||||
final VersionedProfile profile = mock(VersionedProfile.class);
|
final VersionedProfile profile = mock(VersionedProfile.class);
|
||||||
when(profile.commitment()).thenReturn("commitment".getBytes(StandardCharsets.UTF_8));
|
when(profile.commitment()).thenReturn("commitment".getBytes(StandardCharsets.UTF_8));
|
||||||
when(account.getUuid()).thenReturn(targetUuid);
|
when(account.getUuid()).thenReturn(targetUuid);
|
||||||
|
|
Loading…
Reference in New Issue