mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-27 14:40:22 +00:00
Integrate call links create/update/read apis.
This commit is contained in:
committed by
Nicholas Tinsley
parent
4d6d31d624
commit
5a38143987
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user