diff --git a/service/config/sample.yml b/service/config/sample.yml index 05a33af39..e28e7d9e1 100644 --- a/service/config/sample.yml +++ b/service/config/sample.yml @@ -282,6 +282,9 @@ zkConfig: serverPublic: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA== +genericZkConfig: + serverSecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyzAA== + appConfig: application: example environment: example diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java index 4ed1d3465..397ff61f7 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java @@ -28,6 +28,7 @@ import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguratio import org.whispersystems.textsecuregcm.configuration.DynamoDbTables; import org.whispersystems.textsecuregcm.configuration.FcmConfiguration; import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration; +import org.whispersystems.textsecuregcm.configuration.GenericZkConfig; import org.whispersystems.textsecuregcm.configuration.HCaptchaConfiguration; import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration; import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration; @@ -224,6 +225,11 @@ public class WhisperServerConfiguration extends Configuration { @JsonProperty private ZkConfig zkConfig; + @Valid + @NotNull + @JsonProperty + private GenericZkConfig genericZkConfig; + @Valid @NotNull @JsonProperty @@ -413,6 +419,10 @@ public class WhisperServerConfiguration extends Configuration { return zkConfig; } + public GenericZkConfig getGenericZkConfig() { + return genericZkConfig; + } + public RemoteConfigConfiguration getRemoteConfigConfiguration() { return remoteConfig; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 0a9c1fb2e..2242a5d33 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -57,6 +57,7 @@ import org.signal.event.AdminEventLogger; import org.signal.event.GoogleCloudAdminEventLogger; import org.signal.i18n.HeaderControlledResourceBundleLookup; import org.signal.libsignal.zkgroup.ServerSecretParams; +import org.signal.libsignal.zkgroup.GenericServerSecretParams; import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations; import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations; import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation; @@ -620,6 +621,7 @@ public class WhisperServerService extends Application credentials = new ArrayList<>(); + final List callLinkAuthCredentials = new ArrayList<>(); Instant redemption = redemptionStart; UUID aci = auth.getAccount().getUuid(); UUID pni = auth.getAccount().getPhoneNumberIdentifier(); + while (!redemption.isAfter(redemptionEnd)) { credentials.add(new GroupCredentials.GroupCredential( serverZkAuthOperations.issueAuthCredentialWithPni(aci, pni, redemption).serialize(), (int) redemption.getEpochSecond())); + callLinkAuthCredentials.add(new GroupCredentials.CallLinkAuthCredential( + CallLinkAuthCredentialResponse.issueCredential(aci, redemption, genericServerSecretParams).serialize(), + redemption.getEpochSecond())); + redemption = redemption.plus(Duration.ofDays(1)); } - return new GroupCredentials(credentials, pni); + + return new GroupCredentials(credentials, callLinkAuthCredentials, pni); } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/GroupCredentials.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/GroupCredentials.java index 1348c18f6..e0e8c3ff4 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/GroupCredentials.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/GroupCredentials.java @@ -9,8 +9,11 @@ import java.util.List; import java.util.UUID; import javax.annotation.Nullable; -public record GroupCredentials(List credentials, @Nullable UUID pni) { +public record GroupCredentials(List credentials, List callLinkAuthCredentials, @Nullable UUID pni) { public record GroupCredential(byte[] credential, long redemptionTime) { } + + public record CallLinkAuthCredential(byte[] credential, long redemptionTime) { + } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java index d27cd71b8..b80edf19e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java @@ -32,10 +32,12 @@ 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.GenericServerSecretParams; import org.signal.libsignal.zkgroup.ServerSecretParams; import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse; import org.signal.libsignal.zkgroup.auth.ClientZkAuthOperations; import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations; +import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.CertificateGenerator; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; @@ -60,6 +62,8 @@ class CertificateControllerTest { private static final String signingKey = "ABOxG29xrfq4E7IrW11Eg7+HBbtba9iiS0500YoBjn4="; private static final ServerSecretParams serverSecretParams = ServerSecretParams.generate(); + + private static final GenericServerSecretParams genericServerSecretParams = GenericServerSecretParams.generate(); private static final CertificateGenerator certificateGenerator; private static final ServerZkAuthOperations serverZkAuthOperations; private static final Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); @@ -81,7 +85,7 @@ class CertificateControllerTest { ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class))) .setMapper(SystemMapper.jsonMapper()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) - .addResource(new CertificateController(certificateGenerator, serverZkAuthOperations, clock)) + .addResource(new CertificateController(certificateGenerator, serverZkAuthOperations, genericServerSecretParams, clock)) .build(); @Test @@ -230,8 +234,11 @@ class CertificateControllerTest { .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()); @@ -243,6 +250,11 @@ class CertificateControllerTest { (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()); + }); } @Test @@ -259,6 +271,7 @@ class CertificateControllerTest { assertEquals(AuthHelper.VALID_PNI, credentials.pni()); assertEquals(8, credentials.credentials().size()); + assertEquals(8, credentials.callLinkAuthCredentials().size()); final ClientZkAuthOperations clientZkAuthOperations = new ClientZkAuthOperations(serverSecretParams.getPublicParams()); @@ -266,6 +279,7 @@ class CertificateControllerTest { for (int i = 0; i < 8; i++) { final Instant redemptionTime = startOfDay.plus(Duration.ofDays(i)); assertEquals(redemptionTime.getEpochSecond(), credentials.credentials().get(i).redemptionTime()); + assertEquals(redemptionTime.getEpochSecond(), credentials.callLinkAuthCredentials().get(i).redemptionTime()); final int index = i; @@ -276,6 +290,11 @@ class CertificateControllerTest { redemptionTime.getEpochSecond(), new AuthCredentialWithPniResponse(credentials.credentials().get(index).credential())); }); + + assertDoesNotThrow(() -> { + new CallLinkAuthCredentialResponse(credentials.callLinkAuthCredentials().get(index).credential()) + .receive(AuthHelper.VALID_UUID, redemptionTime, genericServerSecretParams.getPublicParams()); + }); } }