Update libsignal to 0.23
This commit is contained in:
parent
e38911b2c5
commit
919cc7e5eb
2
pom.xml
2
pom.xml
|
@ -290,7 +290,7 @@
|
|||
<dependency>
|
||||
<groupId>org.signal</groupId>
|
||||
<artifactId>libsignal-server</artifactId>
|
||||
<version>0.22.0</version>
|
||||
<version>0.23.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.configuration;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
@ -37,7 +38,7 @@ public class UnidentifiedDeliveryConfiguration {
|
|||
return certificate;
|
||||
}
|
||||
|
||||
public ECPrivateKey getPrivateKey() {
|
||||
public ECPrivateKey getPrivateKey() throws InvalidKeyException {
|
||||
return Curve.decodePrivatePoint(privateKey);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,6 @@ import javax.validation.Valid;
|
|||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.ForbiddenException;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
|
@ -69,10 +67,8 @@ import org.apache.commons.lang3.StringUtils;
|
|||
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.PniCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -90,9 +86,7 @@ import org.whispersystems.textsecuregcm.entities.BatchIdentityCheckResponse;
|
|||
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.CredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.PniCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
||||
import org.whispersystems.textsecuregcm.entities.ProfileKeyCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.UserCapabilities;
|
||||
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
|
@ -137,14 +131,10 @@ public class ProfileController {
|
|||
@VisibleForTesting
|
||||
static final Duration EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION = Duration.ofDays(7);
|
||||
|
||||
private static final String PROFILE_KEY_CREDENTIAL_TYPE = "profileKey";
|
||||
private static final String PNI_CREDENTIAL_TYPE = "pni";
|
||||
private static final String EXPIRING_PROFILE_KEY_CREDENTIAL_TYPE = "expiringProfileKey";
|
||||
|
||||
private static final Counter VERSION_NOT_FOUND_COUNTER = Metrics.counter(name(ProfileController.class, "versionNotFound"));
|
||||
private static final String INVALID_ACCEPT_LANGUAGE_COUNTER_NAME = name(ProfileController.class, "invalidAcceptLanguage");
|
||||
private static final String GET_PROFILE_CREDENTIAL_COUNTER_NAME = name(ProfileController.class, "getProfileCredential");
|
||||
private static final String CREDENTIAL_TYPE_TAG_NAME = "credentialType";
|
||||
|
||||
public ProfileController(
|
||||
Clock clock,
|
||||
|
@ -272,56 +262,23 @@ public class ProfileController {
|
|||
@PathParam("uuid") UUID uuid,
|
||||
@PathParam("version") String version,
|
||||
@PathParam("credentialRequest") String credentialRequest,
|
||||
@QueryParam("credentialType") @DefaultValue(PROFILE_KEY_CREDENTIAL_TYPE) String credentialType)
|
||||
@QueryParam("credentialType") String credentialType)
|
||||
throws RateLimitExceededException {
|
||||
|
||||
if (!EXPIRING_PROFILE_KEY_CREDENTIAL_TYPE.equals(credentialType)) {
|
||||
throw new BadRequestException();
|
||||
}
|
||||
|
||||
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
|
||||
final Account targetAccount = verifyPermissionToReceiveAccountIdentityProfile(maybeRequester, accessKey, uuid);
|
||||
final boolean isSelf = isSelfProfileRequest(maybeRequester, uuid);
|
||||
|
||||
String credentialTypeTagValue = "unrecognized";
|
||||
|
||||
try {
|
||||
switch (credentialType) {
|
||||
case PROFILE_KEY_CREDENTIAL_TYPE -> {
|
||||
credentialTypeTagValue = PROFILE_KEY_CREDENTIAL_TYPE;
|
||||
|
||||
return buildProfileKeyCredentialProfileResponse(targetAccount,
|
||||
version,
|
||||
credentialRequest,
|
||||
isSelf,
|
||||
containerRequestContext);
|
||||
}
|
||||
|
||||
case PNI_CREDENTIAL_TYPE -> {
|
||||
credentialTypeTagValue = PNI_CREDENTIAL_TYPE;
|
||||
|
||||
if (!isSelf) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
return buildPniCredentialProfileResponse(targetAccount,
|
||||
version,
|
||||
credentialRequest,
|
||||
containerRequestContext);
|
||||
}
|
||||
|
||||
case EXPIRING_PROFILE_KEY_CREDENTIAL_TYPE -> {
|
||||
credentialTypeTagValue = EXPIRING_PROFILE_KEY_CREDENTIAL_TYPE;
|
||||
|
||||
return buildExpiringProfileKeyCredentialProfileResponse(targetAccount,
|
||||
version,
|
||||
credentialRequest,
|
||||
isSelf,
|
||||
Instant.now().plus(EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION).truncatedTo(ChronoUnit.DAYS),
|
||||
containerRequestContext);
|
||||
}
|
||||
|
||||
default -> throw new BadRequestException();
|
||||
}
|
||||
} finally {
|
||||
Metrics.counter(GET_PROFILE_CREDENTIAL_COUNTER_NAME, CREDENTIAL_TYPE_TAG_NAME, credentialTypeTagValue).increment();
|
||||
}
|
||||
return buildExpiringProfileKeyCredentialProfileResponse(targetAccount,
|
||||
version,
|
||||
credentialRequest,
|
||||
isSelf,
|
||||
Instant.now().plus(EXPIRING_PROFILE_KEY_CREDENTIAL_EXPIRATION).truncatedTo(ChronoUnit.DAYS),
|
||||
containerRequestContext);
|
||||
}
|
||||
|
||||
// Although clients should generally be using versioned profiles wherever possible, there are still a few lingering
|
||||
|
@ -443,35 +400,6 @@ public class ProfileController {
|
|||
});
|
||||
}
|
||||
|
||||
private ProfileKeyCredentialProfileResponse buildProfileKeyCredentialProfileResponse(final Account account,
|
||||
final String version,
|
||||
final String encodedCredentialRequest,
|
||||
final boolean isSelf,
|
||||
final ContainerRequestContext containerRequestContext) {
|
||||
|
||||
final ProfileKeyCredentialResponse profileKeyCredentialResponse = profilesManager.get(account.getUuid(), version)
|
||||
.map(profile -> getProfileCredential(encodedCredentialRequest, profile, account.getUuid()))
|
||||
.orElse(null);
|
||||
|
||||
return new ProfileKeyCredentialProfileResponse(
|
||||
buildVersionedProfileResponse(account, version, isSelf, containerRequestContext),
|
||||
profileKeyCredentialResponse);
|
||||
}
|
||||
|
||||
private PniCredentialProfileResponse buildPniCredentialProfileResponse(final Account account,
|
||||
final String version,
|
||||
final String encodedCredentialRequest,
|
||||
final ContainerRequestContext containerRequestContext) {
|
||||
|
||||
final PniCredentialResponse pniCredentialResponse = profilesManager.get(account.getUuid(), version)
|
||||
.map(profile -> getPniCredential(encodedCredentialRequest, profile, account.getUuid(), account.getPhoneNumberIdentifier()))
|
||||
.orElse(null);
|
||||
|
||||
return new PniCredentialProfileResponse(
|
||||
buildVersionedProfileResponse(account, version, true, containerRequestContext),
|
||||
pniCredentialResponse);
|
||||
}
|
||||
|
||||
private ExpiringProfileKeyCredentialProfileResponse buildExpiringProfileKeyCredentialProfileResponse(
|
||||
final Account account,
|
||||
final String version,
|
||||
|
@ -542,36 +470,6 @@ public class ProfileController {
|
|||
account.getPhoneNumberIdentifier());
|
||||
}
|
||||
|
||||
private ProfileKeyCredentialResponse getProfileCredential(final String encodedProfileCredentialRequest,
|
||||
final VersionedProfile profile,
|
||||
final UUID uuid) {
|
||||
try {
|
||||
final ProfileKeyCommitment commitment = new ProfileKeyCommitment(profile.getCommitment());
|
||||
final ProfileKeyCredentialRequest request = new ProfileKeyCredentialRequest(
|
||||
HexFormat.of().parseHex(encodedProfileCredentialRequest));
|
||||
|
||||
return zkProfileOperations.issueProfileKeyCredential(request, uuid, commitment);
|
||||
} catch (IllegalArgumentException | VerificationFailedException | InvalidInputException e) {
|
||||
throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).build());
|
||||
}
|
||||
}
|
||||
|
||||
private PniCredentialResponse getPniCredential(final String encodedCredentialRequest,
|
||||
final VersionedProfile profile,
|
||||
final UUID accountIdentifier,
|
||||
final UUID phoneNumberIdentifier) {
|
||||
|
||||
try {
|
||||
final ProfileKeyCommitment commitment = new ProfileKeyCommitment(profile.getCommitment());
|
||||
final ProfileKeyCredentialRequest request = new ProfileKeyCredentialRequest(
|
||||
HexFormat.of().parseHex(encodedCredentialRequest));
|
||||
|
||||
return zkProfileOperations.issuePniCredential(request, accountIdentifier, phoneNumberIdentifier, commitment);
|
||||
} catch (IllegalArgumentException | VerificationFailedException | InvalidInputException e) {
|
||||
throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).build());
|
||||
}
|
||||
}
|
||||
|
||||
private ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse(
|
||||
final String encodedCredentialRequest,
|
||||
final VersionedProfile profile,
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import javax.annotation.Nullable;
|
||||
import org.signal.libsignal.zkgroup.profiles.PniCredentialResponse;
|
||||
|
||||
public class PniCredentialProfileResponse extends CredentialProfileResponse {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = PniCredentialResponseAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = PniCredentialResponseAdapter.Deserializing.class)
|
||||
@Nullable
|
||||
private PniCredentialResponse pniCredential;
|
||||
|
||||
public PniCredentialProfileResponse() {
|
||||
}
|
||||
|
||||
public PniCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse,
|
||||
@Nullable final PniCredentialResponse pniCredential) {
|
||||
|
||||
super(versionedProfileResponse);
|
||||
this.pniCredential = pniCredential;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PniCredentialResponse getPniCredential() {
|
||||
return pniCredential;
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.profiles.PniCredentialResponse;
|
||||
|
||||
public class PniCredentialResponseAdapter {
|
||||
|
||||
public static class Serializing extends JsonSerializer<PniCredentialResponse> {
|
||||
@Override
|
||||
public void serialize(PniCredentialResponse response, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
|
||||
throws IOException {
|
||||
if (response == null) jsonGenerator.writeNull();
|
||||
else jsonGenerator.writeString(Base64.getEncoder().encodeToString(response.serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Deserializing extends JsonDeserializer<PniCredentialResponse> {
|
||||
@Override
|
||||
public PniCredentialResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
|
||||
throws IOException {
|
||||
try {
|
||||
return new PniCredentialResponse(Base64.getDecoder().decode(jsonParser.getValueAsString()));
|
||||
} catch (InvalidInputException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialResponse;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ProfileKeyCredentialProfileResponse extends CredentialProfileResponse {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ProfileKeyCredentialResponseAdapter.Deserializing.class)
|
||||
@Nullable
|
||||
private ProfileKeyCredentialResponse credential;
|
||||
|
||||
public ProfileKeyCredentialProfileResponse() {
|
||||
}
|
||||
|
||||
public ProfileKeyCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse,
|
||||
@Nullable final ProfileKeyCredentialResponse credential) {
|
||||
|
||||
super(versionedProfileResponse);
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProfileKeyCredentialResponse getCredential() {
|
||||
return credential;
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialResponse;
|
||||
|
||||
public class ProfileKeyCredentialResponseAdapter {
|
||||
|
||||
public static class Serializing extends JsonSerializer<ProfileKeyCredentialResponse> {
|
||||
@Override
|
||||
public void serialize(ProfileKeyCredentialResponse response, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
|
||||
throws IOException {
|
||||
if (response == null) jsonGenerator.writeNull();
|
||||
else jsonGenerator.writeString(Base64.getEncoder().encodeToString(response.serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Deserializing extends JsonDeserializer<ProfileKeyCredentialResponse> {
|
||||
@Override
|
||||
public ProfileKeyCredentialResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
|
||||
throws IOException {
|
||||
try {
|
||||
return new ProfileKeyCredentialResponse(Base64.getDecoder().decode(jsonParser.getValueAsString()));
|
||||
} catch (InvalidInputException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
|||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.Base64;
|
||||
import java.util.Set;
|
||||
|
@ -64,7 +63,7 @@ public class CertificateCommand extends Command {
|
|||
System.out.println("Private key: " + Base64.getEncoder().encodeToString(keyPair.getPrivateKey().serialize()));
|
||||
}
|
||||
|
||||
private void runCertificateCommand(Namespace namespace) throws IOException, InvalidKeyException {
|
||||
private void runCertificateCommand(Namespace namespace) throws InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException {
|
||||
if (namespace.getString("key") == null) {
|
||||
System.out.println("No key specified!");
|
||||
return;
|
||||
|
|
|
@ -25,7 +25,7 @@ class CertificateGeneratorTest {
|
|||
private static final String IDENTITY_KEY = "BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo";
|
||||
|
||||
@Test
|
||||
void testCreateFor() throws IOException, InvalidKeyException {
|
||||
void testCreateFor() throws IOException, InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException {
|
||||
final Account account = mock(Account.class);
|
||||
final Device device = mock(Device.class);
|
||||
final CertificateGenerator certificateGenerator = new CertificateGenerator(Base64.getDecoder().decode(SIGNING_CERTIFICATE), Curve.decodePrivatePoint(Base64.getDecoder().decode(SIGNING_KEY)), 1);
|
||||
|
|
|
@ -67,12 +67,10 @@ 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.PniCredentialResponse;
|
||||
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.ProfileKeyCredentialResponse;
|
||||
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
|
@ -88,9 +86,7 @@ import org.whispersystems.textsecuregcm.entities.BatchIdentityCheckRequest;
|
|||
import org.whispersystems.textsecuregcm.entities.BatchIdentityCheckResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.PniCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
||||
import org.whispersystems.textsecuregcm.entities.ProfileKeyCredentialProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
|
@ -868,6 +864,29 @@ class ProfileControllerTest {
|
|||
assertThat(profile.getPaymentAddress()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithExpiringProfileKeyCredentialVersionNotFound() throws VerificationFailedException {
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of("version"));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(any(), any())).thenReturn(Optional.empty());
|
||||
|
||||
final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, "version-that-does-not-exist", "credential-request"))
|
||||
.queryParam("credentialType", "expiringProfileKey")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(ExpiringProfileKeyCredentialProfileResponse.class);
|
||||
|
||||
assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID);
|
||||
assertThat(profile.getCredential()).isNull();
|
||||
|
||||
verify(zkProfileOperations, never()).issueExpiringProfileKeyCredential(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetProfileBadges() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);
|
||||
|
@ -963,196 +982,6 @@ class ProfileControllerTest {
|
|||
new AccountBadge("TEST3", Instant.ofEpochSecond(42 + 86400), false));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void testGetProfileWithProfileKeyCredential(final MultivaluedMap<String, Object> authHeaders)
|
||||
throws VerificationFailedException, InvalidInputException {
|
||||
final String version = "version";
|
||||
final byte[] unidentifiedAccessKey = "test-uak".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
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(AuthHelper.VALID_UUID);
|
||||
|
||||
final VersionedProfile versionedProfile = mock(VersionedProfile.class);
|
||||
when(versionedProfile.getCommitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
|
||||
final ProfileKeyCredentialRequestContext profileKeyCredentialRequestContext =
|
||||
clientZkProfile.createProfileKeyCredentialRequestContext(AuthHelper.VALID_UUID, profileKey);
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest = profileKeyCredentialRequestContext.getRequest();
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey));
|
||||
|
||||
final ProfileKeyCredentialResponse credentialResponse =
|
||||
serverZkProfile.issueProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
|
||||
when(zkProfileOperations.issueProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final ProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
|
||||
HexFormat.of().formatHex(credentialRequest.serialize())))
|
||||
.request()
|
||||
.headers(authHeaders)
|
||||
.get(ProfileKeyCredentialProfileResponse.class);
|
||||
|
||||
assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID);
|
||||
assertThat(profile.getCredential()).isEqualTo(credentialResponse);
|
||||
|
||||
verify(zkProfileOperations).issueProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment);
|
||||
verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any());
|
||||
|
||||
final ClientZkProfileOperations clientZkProfileCipher = new ClientZkProfileOperations(serverPublicParams);
|
||||
assertThatNoException().isThrownBy(() ->
|
||||
clientZkProfileCipher.receiveProfileKeyCredential(profileKeyCredentialRequestContext, profile.getCredential()));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testGetProfileWithProfileKeyCredential() {
|
||||
return Stream.of(
|
||||
Arguments.of(new MultivaluedHashMap<>(Map.of(OptionalAccess.UNIDENTIFIED, Base64.getEncoder().encodeToString(UNIDENTIFIED_ACCESS_KEY)))),
|
||||
Arguments.of(new MultivaluedHashMap<>(Map.of("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)))),
|
||||
Arguments.of(new MultivaluedHashMap<>(Map.of("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithProfileKeyCredentialVersionNotFound() throws VerificationFailedException {
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of("version"));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(any(), any())).thenReturn(Optional.empty());
|
||||
|
||||
final ProfileKeyCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, "version-that-does-not-exist", "credential-request"))
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(ProfileKeyCredentialProfileResponse.class);
|
||||
|
||||
assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID);
|
||||
assertThat(profile.getCredential()).isNull();
|
||||
|
||||
verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any());
|
||||
verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithPniCredential() throws InvalidInputException, VerificationFailedException {
|
||||
final String version = "version";
|
||||
|
||||
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(AuthHelper.VALID_UUID);
|
||||
|
||||
final VersionedProfile versionedProfile = mock(VersionedProfile.class);
|
||||
when(versionedProfile.getCommitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest =
|
||||
clientZkProfile.createPniCredentialRequestContext(AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKey)
|
||||
.getRequest();
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
|
||||
final PniCredentialResponse credentialResponse =
|
||||
serverZkProfile.issuePniCredential(credentialRequest, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKeyCommitment);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
|
||||
when(zkProfileOperations.issuePniCredential(credentialRequest, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKeyCommitment))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final PniCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
|
||||
HexFormat.of().formatHex(credentialRequest.serialize())))
|
||||
.queryParam("credentialType", "pni")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(PniCredentialProfileResponse.class);
|
||||
|
||||
assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID);
|
||||
assertThat(profile.getPniCredential()).isEqualTo(credentialResponse);
|
||||
|
||||
verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any());
|
||||
verify(zkProfileOperations).issuePniCredential(credentialRequest, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKeyCommitment);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithPniCredentialNotSelf() throws InvalidInputException, VerificationFailedException {
|
||||
final String version = "version";
|
||||
|
||||
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(AuthHelper.VALID_UUID);
|
||||
|
||||
final VersionedProfile versionedProfile = mock(VersionedProfile.class);
|
||||
when(versionedProfile.getCommitment()).thenReturn(profileKeyCommitment.serialize());
|
||||
|
||||
final ProfileKeyCredentialRequest credentialRequest =
|
||||
clientZkProfile.createProfileKeyCredentialRequestContext(AuthHelper.VALID_UUID, profileKey).getRequest();
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getPhoneNumberIdentifier()).thenReturn(AuthHelper.VALID_PNI);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
|
||||
final PniCredentialResponse credentialResponse =
|
||||
serverZkProfile.issuePniCredential(credentialRequest, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKeyCommitment);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile));
|
||||
when(zkProfileOperations.issuePniCredential(credentialRequest, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, profileKeyCommitment))
|
||||
.thenReturn(credentialResponse);
|
||||
|
||||
final Response response = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version,
|
||||
HexFormat.of().formatHex(credentialRequest.serialize())))
|
||||
.queryParam("credentialType", "pni")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.get();
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(403);
|
||||
|
||||
verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any());
|
||||
verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void testGetProfileWithExpiringProfileKeyCredential(final MultivaluedMap<String, Object> authHeaders)
|
||||
|
@ -1209,7 +1038,6 @@ class ProfileControllerTest {
|
|||
assertThat(profile.getCredential()).isEqualTo(credentialResponse);
|
||||
|
||||
verify(zkProfileOperations).issueExpiringProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment, expiration);
|
||||
verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any());
|
||||
|
||||
final ClientZkProfileOperations clientZkProfileCipher = new ClientZkProfileOperations(serverPublicParams);
|
||||
assertThatNoException().isThrownBy(() ->
|
||||
|
@ -1224,30 +1052,6 @@ class ProfileControllerTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProfileWithPniCredentialVersionNotFound() throws VerificationFailedException {
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID);
|
||||
when(account.getCurrentProfileVersion()).thenReturn(Optional.of("version"));
|
||||
when(account.isEnabled()).thenReturn(true);
|
||||
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
|
||||
when(profilesManager.get(any(), any())).thenReturn(Optional.empty());
|
||||
|
||||
final PniCredentialProfileResponse profile = resources.getJerseyTest()
|
||||
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, "version-that-does-not-exist", "credential-request"))
|
||||
.queryParam("credentialType", "pni")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||
.get(PniCredentialProfileResponse.class);
|
||||
|
||||
assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID);
|
||||
assertThat(profile.getPniCredential()).isNull();
|
||||
|
||||
verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any());
|
||||
verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetProfileBadgesMissingFromRequest() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse;
|
||||
|
@ -68,7 +69,7 @@ class CertificateControllerTest {
|
|||
certificateGenerator = new CertificateGenerator(Base64.getDecoder().decode(signingCertificate),
|
||||
Curve.decodePrivatePoint(Base64.getDecoder().decode(signingKey)), 1);
|
||||
serverZkAuthOperations = new ServerZkAuthOperations(serverSecretParams);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue