mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-29 13:16:01 +01:00
Add support for endpoint checking prekey consistency.
This commit is contained in:
committed by
Alex Hart
parent
09b0f15294
commit
e1067e30de
@@ -30,6 +30,7 @@ import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||
import org.whispersystems.signalservice.api.keys.KeysApi;
|
||||
import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
|
||||
import org.whispersystems.signalservice.api.payments.CurrencyConversions;
|
||||
@@ -864,6 +865,10 @@ public class SignalServiceAccountManager {
|
||||
return ArchiveApi.create(pushServiceSocket, configuration.getBackupServerPublicParams(), credentials.getAci());
|
||||
}
|
||||
|
||||
public KeysApi getKeysApi() {
|
||||
return KeysApi.create(pushServiceSocket);
|
||||
}
|
||||
|
||||
public AuthCredentials getPaymentsAuthorization() throws IOException {
|
||||
return pushServiceSocket.getPaymentsAuthorization();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.keys
|
||||
|
||||
import org.signal.core.util.toByteArray
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.signal.libsignal.protocol.kem.KEMPublicKey
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
* Contains APIs for interacting with /keys endpoints on the service.
|
||||
*/
|
||||
class KeysApi(private val pushServiceSocket: PushServiceSocket) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(pushServiceSocket: PushServiceSocket): KeysApi {
|
||||
return KeysApi(pushServiceSocket)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if our local view of our repeated-use prekeys matches the server's view. It's an all-or-nothing match, and no details can be given beyond
|
||||
* whether or not everything perfectly matches or not.
|
||||
*
|
||||
* Status codes:
|
||||
* - 200: Everything matches
|
||||
* - 409: Something doesn't match
|
||||
*/
|
||||
fun checkRepeatedUseKeys(
|
||||
serviceIdType: ServiceIdType,
|
||||
identityKey: IdentityKey,
|
||||
signedPreKeyId: Int,
|
||||
signedPreKey: ECPublicKey,
|
||||
lastResortKyberKeyId: Int,
|
||||
lastResortKyberKey: KEMPublicKey
|
||||
): NetworkResult<Unit> {
|
||||
val digest: MessageDigest = MessageDigest.getInstance("SHA-256").apply {
|
||||
update(identityKey.serialize())
|
||||
update(signedPreKeyId.toLong().toByteArray())
|
||||
update(signedPreKey.serialize())
|
||||
update(lastResortKyberKeyId.toLong().toByteArray())
|
||||
update(lastResortKyberKey.serialize())
|
||||
}
|
||||
|
||||
return NetworkResult.fromFetch {
|
||||
pushServiceSocket.checkRepeatedUsePreKeys(serviceIdType, digest.digest())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import org.signal.core.util.Base64
|
||||
|
||||
/**
|
||||
* Deserializes any valid base64 (regardless of padding or url-safety) into a ByteArray.
|
||||
*/
|
||||
class ByteArrayDeserializerBase64 : JsonDeserializer<ByteArray>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): ByteArray {
|
||||
return Base64.decode(p.valueAsString)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.JsonSerializer
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import org.signal.core.util.Base64
|
||||
|
||||
/**
|
||||
* JSON serializer to encode a ByteArray as a base64 string without padding.
|
||||
*/
|
||||
class ByteArraySerializerBase64NoPadding : JsonSerializer<ByteArray>() {
|
||||
override fun serialize(value: ByteArray, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||
gen.writeString(Base64.encodeWithoutPadding(value))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.internal.push
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
|
||||
/**
|
||||
* Request body to check if our prekeys match what's on the service.
|
||||
*/
|
||||
class CheckRepeatedUsedPreKeysRequest(
|
||||
@JsonProperty
|
||||
val identityType: String,
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArraySerializerBase64NoPadding::class)
|
||||
val digest: ByteArray
|
||||
)
|
||||
@@ -231,6 +231,8 @@ public class PushServiceSocket {
|
||||
private static final String PREKEY_METADATA_PATH = "/v2/keys?identity=%s";
|
||||
private static final String PREKEY_PATH = "/v2/keys?identity=%s";
|
||||
private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s?pq=true";
|
||||
private static final String PREKEY_CHECK_PATH = "/v2/keys/check";
|
||||
|
||||
|
||||
private static final String PROVISIONING_CODE_PATH = "/v1/devices/provisioning/code";
|
||||
private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s";
|
||||
@@ -874,6 +876,17 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public void checkRepeatedUsePreKeys(ServiceIdType serviceIdType, byte[] digest) throws IOException {
|
||||
String body = JsonUtil.toJson(new CheckRepeatedUsedPreKeysRequest(serviceIdType.toString(), digest));
|
||||
|
||||
makeServiceRequest(PREKEY_CHECK_PATH, "POST", body, NO_HEADERS, (responseCode, body1) -> {
|
||||
// Must override this handling because otherwise code assumes a device mismatch error
|
||||
if (responseCode == 409) {
|
||||
throw new NonSuccessfulResponseCodeException(409);
|
||||
}
|
||||
}, Optional.empty());
|
||||
}
|
||||
|
||||
public void retrieveAttachment(int cdnNumber, SignalServiceAttachmentRemoteId cdnPath, File destination, long maxSizeBytes, ProgressListener listener)
|
||||
throws IOException, MissingConfigurationException
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user