diff --git a/pom.xml b/pom.xml index 7610e7e4d..f4747425d 100644 --- a/pom.xml +++ b/pom.xml @@ -293,7 +293,7 @@ org.signal libsignal-server - 0.26.0 + 0.30.0 org.apache.logging.log4j diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallLinkController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallLinkController.java index 995c04872..22bcabb97 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallLinkController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CallLinkController.java @@ -4,6 +4,7 @@ import com.codahale.metrics.annotation.Timed; import io.dropwizard.auth.Auth; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.signal.libsignal.protocol.ServiceId; import org.signal.libsignal.zkgroup.GenericServerSecretParams; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequest; @@ -68,7 +69,7 @@ public class CallLinkController { } return new CreateCallLinkCredential( - createCallLinkCredentialRequest.issueCredential(auth.getAccount().getUuid(), truncatedDayTimestamp, genericServerSecretParams).serialize(), + createCallLinkCredentialRequest.issueCredential(new ServiceId.Aci(auth.getAccount().getUuid()), truncatedDayTimestamp, genericServerSecretParams).serialize(), truncatedDayTimestamp.getEpochSecond() ); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java index 2f28923c9..f716fd628 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java @@ -32,6 +32,8 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.signal.libsignal.protocol.ServiceId; +import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse; import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations; import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse; import org.signal.libsignal.zkgroup.GenericServerSecretParams; @@ -92,7 +94,8 @@ public class CertificateController { public GroupCredentials getGroupAuthenticationCredentials( @Auth AuthenticatedAccount auth, @QueryParam("redemptionStartSeconds") int startSeconds, - @QueryParam("redemptionEndSeconds") int endSeconds) { + @QueryParam("redemptionEndSeconds") int endSeconds, + @QueryParam("pniAsServiceId") boolean pniAsServiceId) { final Instant startOfDay = clock.instant().truncatedTo(ChronoUnit.DAYS); final Instant redemptionStart = Instant.ofEpochSecond(startSeconds); @@ -112,12 +115,18 @@ public class CertificateController { Instant redemption = redemptionStart; - UUID aci = auth.getAccount().getUuid(); - UUID pni = auth.getAccount().getPhoneNumberIdentifier(); + ServiceId.Aci aci = new ServiceId.Aci(auth.getAccount().getUuid()); + ServiceId.Pni pni = new ServiceId.Pni(auth.getAccount().getPhoneNumberIdentifier()); while (!redemption.isAfter(redemptionEnd)) { + AuthCredentialWithPniResponse authCredentialWithPni; + if (pniAsServiceId) { + authCredentialWithPni = serverZkAuthOperations.issueAuthCredentialWithPniAsServiceId(aci, pni, redemption); + } else { + authCredentialWithPni = serverZkAuthOperations.issueAuthCredentialWithPniAsAci(aci, pni, redemption); + } credentials.add(new GroupCredentials.GroupCredential( - serverZkAuthOperations.issueAuthCredentialWithPni(aci, pni, redemption).serialize(), + authCredentialWithPni.serialize(), (int) redemption.getEpochSecond())); callLinkAuthCredentials.add(new GroupCredentials.CallLinkAuthCredential( @@ -128,6 +137,6 @@ public class CertificateController { } - return new GroupCredentials(credentials, callLinkAuthCredentials, pni); + return new GroupCredentials(credentials, callLinkAuthCredentials, pni.getRawUUID()); } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index bb45bf98a..4bfa9cd83 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -65,6 +65,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.signal.libsignal.protocol.IdentityKey; +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; @@ -401,7 +402,7 @@ public class ProfileController { final ContainerRequestContext containerRequestContext) { final ExpiringProfileKeyCredentialResponse expiringProfileKeyCredentialResponse = profilesManager.get(account.getUuid(), version) - .map(profile -> getExpiringProfileKeyCredentialResponse(encodedCredentialRequest, profile, account.getUuid(), expiration)) + .map(profile -> getExpiringProfileKeyCredentialResponse(encodedCredentialRequest, profile, new ServiceId.Aci(account.getUuid()), expiration)) .orElse(null); return new ExpiringProfileKeyCredentialProfileResponse( @@ -465,7 +466,7 @@ public class ProfileController { private ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse( final String encodedCredentialRequest, final VersionedProfile profile, - final UUID accountIdentifier, + final ServiceId.Aci accountIdentifier, final Instant expiration) { try { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CertificateControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CertificateControllerTest.java index d67245501..fc1b8b0aa 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CertificateControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/CertificateControllerTest.java @@ -31,6 +31,7 @@ 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.ServiceId; import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.zkgroup.GenericServerSecretParams; import org.signal.libsignal.zkgroup.ServerSecretParams; @@ -218,7 +219,7 @@ class CertificateControllerTest { } @Test - void testGetSingleGroupCredential() { + void testGetSingleGroupCredentialWithPniAsAci() { final Instant startOfDay = clock.instant().truncatedTo(ChronoUnit.DAYS); final GroupCredentials credentials = resources.getJerseyTest() @@ -240,16 +241,53 @@ class CertificateControllerTest { new ClientZkAuthOperations(serverSecretParams.getPublicParams()); assertDoesNotThrow(() -> { - clientZkAuthOperations.receiveAuthCredentialWithPni( - AuthHelper.VALID_UUID, - AuthHelper.VALID_PNI, + clientZkAuthOperations.receiveAuthCredentialWithPniAsAci( + new ServiceId.Aci(AuthHelper.VALID_UUID), + new ServiceId.Pni(AuthHelper.VALID_PNI), (int) startOfDay.getEpochSecond(), new AuthCredentialWithPniResponse(credentials.credentials().get(0).credential())); }); assertDoesNotThrow(() -> { new CallLinkAuthCredentialResponse(credentials.callLinkAuthCredentials().get(0).credential()) - .receive(AuthHelper.VALID_UUID, startOfDay, genericServerSecretParams.getPublicParams()); + .receive(new ServiceId.Aci(AuthHelper.VALID_UUID), startOfDay, genericServerSecretParams.getPublicParams()); + }); + } + + @Test + void testGetSingleGroupCredentialWithPniAsServiceId() { + final Instant startOfDay = clock.instant().truncatedTo(ChronoUnit.DAYS); + + final GroupCredentials credentials = resources.getJerseyTest() + .target("/v1/certificate/auth/group") + .queryParam("redemptionStartSeconds", startOfDay.getEpochSecond()) + .queryParam("redemptionEndSeconds", startOfDay.getEpochSecond()) + .queryParam("pniAsServiceId", true) + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) + .get(GroupCredentials.class); + + assertEquals(1, credentials.credentials().size()); + assertEquals(1, credentials.callLinkAuthCredentials().size()); + + assertEquals(AuthHelper.VALID_PNI, credentials.pni()); + assertEquals(startOfDay.getEpochSecond(), credentials.credentials().get(0).redemptionTime()); + assertEquals(startOfDay.getEpochSecond(), credentials.callLinkAuthCredentials().get(0).redemptionTime()); + + final ClientZkAuthOperations clientZkAuthOperations = + new ClientZkAuthOperations(serverSecretParams.getPublicParams()); + + assertDoesNotThrow(() -> { + clientZkAuthOperations.receiveAuthCredentialWithPniAsServiceId( + new ServiceId.Aci(AuthHelper.VALID_UUID), + new ServiceId.Pni(AuthHelper.VALID_PNI), + (int) startOfDay.getEpochSecond(), + new AuthCredentialWithPniResponse(credentials.credentials().get(0).credential())); + }); + + assertDoesNotThrow(() -> { + new CallLinkAuthCredentialResponse(credentials.callLinkAuthCredentials().get(0).credential()) + .receive(new ServiceId.Aci(AuthHelper.VALID_UUID), startOfDay, genericServerSecretParams.getPublicParams()); }); } @@ -261,6 +299,7 @@ class CertificateControllerTest { .target("/v1/certificate/auth/group") .queryParam("redemptionStartSeconds", startOfDay.getEpochSecond()) .queryParam("redemptionEndSeconds", startOfDay.plus(Duration.ofDays(7)).getEpochSecond()) + .queryParam("pniAsServiceId", true) .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(GroupCredentials.class); @@ -280,16 +319,16 @@ class CertificateControllerTest { final int index = i; assertDoesNotThrow(() -> { - clientZkAuthOperations.receiveAuthCredentialWithPni( - AuthHelper.VALID_UUID, - AuthHelper.VALID_PNI, + clientZkAuthOperations.receiveAuthCredentialWithPniAsServiceId( + new ServiceId.Aci(AuthHelper.VALID_UUID), + new ServiceId.Pni(AuthHelper.VALID_PNI), redemptionTime.getEpochSecond(), new AuthCredentialWithPniResponse(credentials.credentials().get(index).credential())); }); assertDoesNotThrow(() -> { new CallLinkAuthCredentialResponse(credentials.callLinkAuthCredentials().get(index).credential()) - .receive(AuthHelper.VALID_UUID, redemptionTime, genericServerSecretParams.getPublicParams()); + .receive(new ServiceId.Aci(AuthHelper.VALID_UUID), redemptionTime, genericServerSecretParams.getPublicParams()); }); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java index 046e94f18..fa9effe74 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java @@ -61,6 +61,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.ServiceId; import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.ServerPublicParams; @@ -421,7 +422,7 @@ class ProfileControllerTest { @Test void testSetProfileWantAvatarUpload() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); final ProfileAvatarUploadAttributes uploadAttributes = resources.getJerseyTest() .target("/v1/profile/") @@ -449,7 +450,7 @@ class ProfileControllerTest { @Test void testSetProfileWantAvatarUploadWithBadProfileSize() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); try (final Response response = resources.getJerseyTest() .target("/v1/profile/") @@ -465,7 +466,7 @@ class ProfileControllerTest { @Test void testSetProfileWithoutAvatarUpload() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -497,7 +498,7 @@ class ProfileControllerTest { @Test void testSetProfileWithAvatarUploadAndPreviousAvatar() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); resources.getJerseyTest() .target("/v1/profile/") @@ -524,7 +525,7 @@ class ProfileControllerTest { @Test void testSetProfileClearPreviousAvatar() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); try (final Response response = resources.getJerseyTest() .target("/v1/profile/") @@ -554,7 +555,7 @@ class ProfileControllerTest { @Test void testSetProfileWithSameAvatar() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); try (final Response response = resources.getJerseyTest() .target("/v1/profile/") @@ -584,7 +585,7 @@ class ProfileControllerTest { @Test void testSetProfileClearPreviousAvatarDespiteSameAvatarFlagSet() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); try (final Response ignored = resources.getJerseyTest() .target("/v1/profile/") @@ -612,7 +613,7 @@ class ProfileControllerTest { @Test void testSetProfileWithSameAvatarDespiteNoPreviousAvatar() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); try (final Response response = resources.getJerseyTest() .target("/v1/profile/") @@ -642,7 +643,7 @@ class ProfileControllerTest { @Test void testSetProfileExtendedName() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); final String name = RandomStringUtils.randomAlphabetic(380); @@ -670,7 +671,7 @@ class ProfileControllerTest { @Test void testSetProfileEmojiAndBioText() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -709,7 +710,7 @@ class ProfileControllerTest { @Test void testSetProfilePaymentAddress() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -750,7 +751,7 @@ class ProfileControllerTest { when(dynamicPaymentsConfiguration.getDisallowedPrefixes()) .thenReturn(List.of(AuthHelper.VALID_NUMBER_TWO.substring(0, 3))); - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -779,7 +780,7 @@ class ProfileControllerTest { when(dynamicPaymentsConfiguration.getDisallowedPrefixes()) .thenReturn(List.of(AuthHelper.VALID_NUMBER_TWO.substring(0, 3))); - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -854,7 +855,7 @@ class ProfileControllerTest { @Test void testSetProfileUpdatesAccountCurrentVersion() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID_TWO)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -943,7 +944,7 @@ class ProfileControllerTest { @Test void testSetProfileBadges() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); @@ -1065,13 +1066,13 @@ class ProfileControllerTest { new SecureRandom().nextBytes(profileKeyBytes); final ProfileKey profileKey = new ProfileKey(profileKeyBytes); - final ProfileKeyCommitment profileKeyCommitment = profileKey.getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment profileKeyCommitment = profileKey.getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); final VersionedProfile versionedProfile = mock(VersionedProfile.class); when(versionedProfile.getCommitment()).thenReturn(profileKeyCommitment.serialize()); final ProfileKeyCredentialRequestContext profileKeyCredentialRequestContext = - clientZkProfile.createProfileKeyCredentialRequestContext(AuthHelper.VALID_UUID, profileKey); + clientZkProfile.createProfileKeyCredentialRequestContext(new ServiceId.Aci(AuthHelper.VALID_UUID), profileKey); final ProfileKeyCredentialRequest credentialRequest = profileKeyCredentialRequestContext.getRequest(); @@ -1085,11 +1086,11 @@ class ProfileControllerTest { .truncatedTo(ChronoUnit.DAYS); final ExpiringProfileKeyCredentialResponse credentialResponse = - serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment, expiration); + serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration); 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, AuthHelper.VALID_UUID, profileKeyCommitment, expiration)) + when(zkProfileOperations.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration)) .thenReturn(credentialResponse); final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest() @@ -1104,7 +1105,7 @@ class ProfileControllerTest { .isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID)); assertThat(profile.getCredential()).isEqualTo(credentialResponse); - verify(zkProfileOperations).issueExpiringProfileKeyCredential(credentialRequest, AuthHelper.VALID_UUID, profileKeyCommitment, expiration); + verify(zkProfileOperations).issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration); final ClientZkProfileOperations clientZkProfileCipher = new ClientZkProfileOperations(serverPublicParams); assertThatNoException().isThrownBy(() -> @@ -1121,7 +1122,7 @@ class ProfileControllerTest { @Test void testSetProfileBadgesMissingFromRequest() throws InvalidInputException { - final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); + final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);