Integrate call links create/update/read apis.

This commit is contained in:
Alex Hart
2023-05-19 10:28:29 -03:00
committed by Nicholas Tinsley
parent 4d6d31d624
commit 5a38143987
60 changed files with 1986 additions and 191 deletions

View File

@@ -7,7 +7,14 @@ public class CredentialResponse {
@JsonProperty
private TemporalCredential[] credentials;
@JsonProperty
private TemporalCredential[] callLinkAuthCredentials;
public TemporalCredential[] getCredentials() {
return credentials;
}
public TemporalCredential[] getCallLinkAuthCredentials() {
return callLinkAuthCredentials;
}
}

View File

@@ -4,12 +4,11 @@ import com.google.protobuf.ByteString;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.VerificationFailedException;
import org.signal.libsignal.zkgroup.auth.AuthCredential;
import org.signal.libsignal.zkgroup.auth.AuthCredentialPresentation;
import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse;
import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPni;
import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse;
import org.signal.libsignal.zkgroup.auth.ClientZkAuthOperations;
import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse;
import org.signal.libsignal.zkgroup.groups.ClientZkGroupCipher;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.signal.storageservice.protos.groups.AvatarUploadAttributes;
@@ -29,8 +28,10 @@ import org.whispersystems.signalservice.internal.push.exceptions.ForbiddenExcept
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class GroupsV2Api {
@@ -46,7 +47,7 @@ public class GroupsV2Api {
/**
* Provides 7 days of credentials, which you should cache.
*/
public HashMap<Long, AuthCredentialWithPniResponse> getCredentials(long todaySeconds)
public CredentialResponseMaps getCredentials(long todaySeconds)
throws IOException
{
return parseCredentialResponse(socket.retrieveGroupsV2Credentials(todaySeconds));
@@ -174,10 +175,11 @@ public class GroupsV2Api {
return socket.getGroupExternalCredential(authorization);
}
private static HashMap<Long, AuthCredentialWithPniResponse> parseCredentialResponse(CredentialResponse credentialResponse)
private static CredentialResponseMaps parseCredentialResponse(CredentialResponse credentialResponse)
throws IOException
{
HashMap<Long, AuthCredentialWithPniResponse> credentials = new HashMap<>();
HashMap<Long, AuthCredentialWithPniResponse> credentials = new HashMap<>();
HashMap<Long, CallLinkAuthCredentialResponse> callLinkCredentials = new HashMap<>();
for (TemporalCredential credential : credentialResponse.getCredentials()) {
AuthCredentialWithPniResponse authCredentialWithPniResponse;
@@ -190,6 +192,44 @@ public class GroupsV2Api {
credentials.put(credential.getRedemptionTime(), authCredentialWithPniResponse);
}
return credentials;
for (TemporalCredential credential : credentialResponse.getCallLinkAuthCredentials()) {
CallLinkAuthCredentialResponse callLinkAuthCredentialResponse;
try {
callLinkAuthCredentialResponse = new CallLinkAuthCredentialResponse(credential.getCredential());
} catch (InvalidInputException e) {
throw new IOException(e);
}
callLinkCredentials.put(credential.getRedemptionTime(), callLinkAuthCredentialResponse);
}
return new CredentialResponseMaps(credentials, callLinkCredentials);
}
public static class CredentialResponseMaps {
private final Map<Long, AuthCredentialWithPniResponse> authCredentialWithPniResponseHashMap;
private final Map<Long, CallLinkAuthCredentialResponse> callLinkAuthCredentialResponseHashMap;
public CredentialResponseMaps(Map<Long, AuthCredentialWithPniResponse> authCredentialWithPniResponseHashMap,
Map<Long, CallLinkAuthCredentialResponse> callLinkAuthCredentialResponseHashMap)
{
this.authCredentialWithPniResponseHashMap = authCredentialWithPniResponseHashMap;
this.callLinkAuthCredentialResponseHashMap = callLinkAuthCredentialResponseHashMap;
}
public Map<Long, AuthCredentialWithPniResponse> getAuthCredentialWithPniResponseHashMap() {
return authCredentialWithPniResponseHashMap;
}
public Map<Long, CallLinkAuthCredentialResponse> getCallLinkAuthCredentialResponseHashMap() {
return callLinkAuthCredentialResponseHashMap;
}
public CredentialResponseMaps createUnmodifiableCopy() {
return new CredentialResponseMaps(
Map.copyOf(authCredentialWithPniResponseHashMap),
Map.copyOf(callLinkAuthCredentialResponseHashMap)
);
}
}
}

View File

@@ -8,7 +8,9 @@ package org.whispersystems.signalservice.api.messages.multidevice;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallEvent;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate;
import java.util.LinkedList;
import java.util.List;
@@ -34,6 +36,7 @@ public class SignalServiceSyncMessage {
private final Optional<OutgoingPaymentMessage> outgoingPaymentMessage;
private final Optional<List<ViewedMessage>> views;
private final Optional<CallEvent> callEvent;
private final Optional<CallLinkUpdate> callLinkUpdate;
private SignalServiceSyncMessage(Optional<SentTranscriptMessage> sent,
Optional<ContactsMessage> contacts,
@@ -50,7 +53,8 @@ public class SignalServiceSyncMessage {
Optional<MessageRequestResponseMessage> messageRequestResponse,
Optional<OutgoingPaymentMessage> outgoingPaymentMessage,
Optional<List<ViewedMessage>> views,
Optional<CallEvent> callEvent)
Optional<CallEvent> callEvent,
Optional<CallLinkUpdate> callLinkUpdate)
{
this.sent = sent;
this.contacts = contacts;
@@ -68,6 +72,7 @@ public class SignalServiceSyncMessage {
this.outgoingPaymentMessage = outgoingPaymentMessage;
this.views = views;
this.callEvent = callEvent;
this.callLinkUpdate = callLinkUpdate;
}
public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) {
@@ -86,6 +91,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -105,6 +111,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -124,6 +131,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -143,6 +151,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -162,6 +171,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -181,6 +191,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.of(views),
Optional.empty(),
Optional.empty());
}
@@ -200,6 +211,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -222,6 +234,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -241,6 +254,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -260,6 +274,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -279,6 +294,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -298,6 +314,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -317,6 +334,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -336,6 +354,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -355,6 +374,7 @@ public class SignalServiceSyncMessage {
Optional.of(messageRequestResponse),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -374,6 +394,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.of(outgoingPaymentMessage),
Optional.empty(),
Optional.empty(),
Optional.empty());
}
@@ -393,7 +414,28 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.of(callEvent));
Optional.of(callEvent),
Optional.empty());
}
public static SignalServiceSyncMessage forCallLinkUpdate(@Nonnull CallLinkUpdate callLinkUpdate) {
return new SignalServiceSyncMessage(Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.of(callLinkUpdate));
}
public static SignalServiceSyncMessage empty() {
@@ -412,6 +454,7 @@ public class SignalServiceSyncMessage {
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.api.services
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequest
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialResponse
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations
import org.whispersystems.signalservice.api.util.CredentialsProvider
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import org.whispersystems.signalservice.internal.push.PushServiceSocket
import java.io.IOException
class CallLinksService(
configuration: SignalServiceConfiguration,
credentialsProvider: CredentialsProvider,
signalAgent: String,
groupsV2Operations: GroupsV2Operations,
automaticNetworkRetry: Boolean
) {
private val pushServiceSocket = PushServiceSocket(
configuration,
credentialsProvider,
signalAgent,
groupsV2Operations.profileOperations,
automaticNetworkRetry
)
fun getCreateCallLinkAuthCredential(request: CreateCallLinkCredentialRequest): ServiceResponse<CreateCallLinkCredentialResponse> {
return try {
ServiceResponse.forResult(pushServiceSocket.getCallLinkAuthResponse(request), 200, "")
} catch (e: IOException) {
ServiceResponse.forUnknownError(e)
}
}
}

View File

@@ -17,5 +17,6 @@ class SignalServiceConfiguration(
val networkInterceptors: List<Interceptor>,
val dns: Optional<Dns>,
val signalProxy: Optional<SignalProxy>,
val zkGroupServerPublicParams: ByteArray
val zkGroupServerPublicParams: ByteArray,
val genericServerPublicParams: ByteArray
)

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.internal.push
import com.fasterxml.jackson.annotation.JsonCreator
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequest
import org.whispersystems.util.Base64
/**
* Request body to create a call link credential response.
*/
data class CreateCallLinkAuthRequest @JsonCreator constructor(
val createCallLinkCredentialRequest: String
) {
companion object {
@JvmStatic
fun create(createCallLinkCredentialRequest: CreateCallLinkCredentialRequest): CreateCallLinkAuthRequest {
return CreateCallLinkAuthRequest(
Base64.encodeBytes(createCallLinkCredentialRequest.serialize())
)
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.internal.push
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialResponse
import org.whispersystems.util.Base64
/**
* Response body for CreateCallLinkAuthResponse
*/
data class CreateCallLinkAuthResponse @JsonCreator constructor(
@JsonProperty("credential") val credential: String,
@JsonProperty("redemptionTime") val redemptionTime: Long
) {
val createCallLinkCredentialResponse: CreateCallLinkCredentialResponse
get() = CreateCallLinkCredentialResponse(Base64.decode(credential))
}

View File

@@ -22,6 +22,8 @@ import org.signal.libsignal.protocol.util.Pair;
import org.signal.libsignal.usernames.BaseUsernameException;
import org.signal.libsignal.usernames.Username;
import org.signal.libsignal.zkgroup.VerificationFailedException;
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequest;
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialResponse;
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
@@ -289,6 +291,7 @@ public class PushServiceSocket {
private static final String BACKUP_AUTH_CHECK = "/v1/backup/auth/check";
private static final String CALL_LINK_CREATION_AUTH = "/v1/call-link/create-auth";
private static final String SERVER_DELIVERED_TIMESTAMP_HEADER = "X-Signal-Timestamp";
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
@@ -1154,6 +1157,17 @@ public class PushServiceSocket {
}
}
public CreateCallLinkCredentialResponse getCallLinkAuthResponse(CreateCallLinkCredentialRequest request) throws IOException {
String payload = JsonUtil.toJson(CreateCallLinkAuthRequest.create(request));
String response = makeServiceRequest(
CALL_LINK_CREATION_AUTH,
"POST",
payload
);
return JsonUtil.fromJson(response, CreateCallLinkAuthResponse.class).getCreateCallLinkCredentialResponse();
}
private AuthCredentials getAuthCredentials(String authPath) throws IOException {
String response = makeServiceRequest(authPath, "GET", null);
AuthCredentials token = JsonUtil.fromJson(response, AuthCredentials.class);

View File

@@ -629,6 +629,11 @@ message SyncMessage {
optional Event event = 6;
}
message CallLinkUpdate {
optional bytes rootKey = 1;
optional bytes adminPassKey = 2;
}
optional Sent sent = 1;
optional Contacts contacts = 2;
optional Groups groups = 3;
@@ -648,6 +653,7 @@ message SyncMessage {
reserved /*pniIdentity*/ 17;
optional PniChangeNumber pniChangeNumber = 18;
optional CallEvent callEvent = 19;
optional CallLinkUpdate callLinkUpdate = 20;
}
message AttachmentPointer {