Add tests for generating profile key credentials

This commit is contained in:
Jon Chambers 2021-11-18 11:02:47 -05:00 committed by Jon Chambers
parent 68412b3901
commit 20a71b7df2
3 changed files with 170 additions and 0 deletions

View File

@ -31,6 +31,7 @@ import javax.validation.valueextraction.Unwrapping;
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.PUT;
@ -287,6 +288,10 @@ public class ProfileController {
if (credentialRequest.isPresent() && credentialType.isPresent() && profile.isPresent() && requestAccount.isPresent()) {
if (PNI_CREDENTIAL_TYPE.equals(credentialType.get())) {
if (!isSelf) {
throw new ForbiddenException();
}
profileKeyCredentialResponse = null;
pniCredentialResponse = getPniCredential(credentialRequest.get(),
profile.get(),

View File

@ -140,4 +140,12 @@ public class Profile {
public List<Badge> getBadges() {
return badges;
}
public ProfileKeyCredentialResponse getCredential() {
return credential;
}
public PniCredentialResponse getPniCredential() {
return pniCredential;
}
}

View File

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.security.SecureRandom;
import java.time.Clock;
import java.time.Instant;
import java.util.Base64;
@ -32,6 +33,7 @@ import java.util.Optional;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.RandomStringUtils;
import org.assertj.core.api.Condition;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
@ -41,8 +43,15 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.ServerPublicParams;
import org.signal.zkgroup.ServerSecretParams;
import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
import org.signal.zkgroup.profiles.PniCredentialResponse;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCommitment;
import org.signal.zkgroup.profiles.ProfileKeyCredentialRequest;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
import org.signal.zkgroup.profiles.ServerZkProfileOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
@ -182,6 +191,7 @@ class ProfileControllerTest {
clearInvocations(accountsManager);
clearInvocations(usernameRateLimiter);
clearInvocations(profilesManager);
clearInvocations(zkProfileOperations);
}
@AfterEach
@ -737,6 +747,153 @@ class ProfileControllerTest {
new AccountBadge("TEST3", Instant.ofEpochSecond(42 + 86400), false));
}
@Test
void testGetProfileWithProfileKeyCredential() 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.getCurrentProfileVersion()).thenReturn(Optional.of(version));
when(account.isEnabled()).thenReturn(true);
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 Profile profile = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version, Hex.encodeHexString(credentialRequest.serialize())))
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(Profile.class);
assertThat(profile.getUuid()).isNull();
assertThat(profile.getCredential()).isEqualTo(credentialResponse);
assertThat(profile.getPniCredential()).isNull();
verify(zkProfileOperations).issueProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment);
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 Profile profile = resources.getJerseyTest()
.target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version, Hex.encodeHexString(credentialRequest.serialize())))
.queryParam("credentialType", "pni")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(Profile.class);
assertThat(profile.getUuid()).isNull();
assertThat(profile.getCredential()).isNull();
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, Hex.encodeHexString(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());
}
@Test
void testSetProfileBadgesMissingFromRequest() throws InvalidInputException {
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);