From c0c07ab46636f2e8f6464677679fdff18788167b Mon Sep 17 00:00:00 2001 From: spoonman01 Date: Tue, 10 Dec 2024 15:21:24 +0100 Subject: [PATCH] feat(set and use any mls ciphersuite) #WPB-14877 * Adapt to set and use in all calls any ciphersuite instead of only the default one * Change API contract of isMlsEnabled, now returning the full FeatureConfig * Move some MLS response DTO from Helium * Bump to 1.8.1 --- pom.xml | 2 +- src/main/java/com/wire/xenon/WireAPI.java | 17 +++++------ src/main/java/com/wire/xenon/WireClient.java | 7 ----- .../java/com/wire/xenon/WireClientBase.java | 14 ++++----- .../xenon/backend/models/FeatureConfig.java | 19 ++++++++++++ .../backend/models/MlsConfigResponse.java | 23 ++++++++++++++ .../xenon/backend/models/MlsResponse.java | 25 ++++++++++++++++ .../wire/xenon/crypto/mls/CryptoMlsClient.kt | 30 +++++++++++++++---- .../java/com/wire/xenon/MlsClientTest.java | 8 ++--- 9 files changed, 110 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/wire/xenon/backend/models/FeatureConfig.java create mode 100644 src/main/java/com/wire/xenon/backend/models/MlsConfigResponse.java create mode 100644 src/main/java/com/wire/xenon/backend/models/MlsResponse.java diff --git a/pom.xml b/pom.xml index 8569297..cc720f0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.wire xenon - 1.8.0 + 1.8.1 Xenon Base Wire Bots Library diff --git a/src/main/java/com/wire/xenon/WireAPI.java b/src/main/java/com/wire/xenon/WireAPI.java index c0339de..136c2a1 100644 --- a/src/main/java/com/wire/xenon/WireAPI.java +++ b/src/main/java/com/wire/xenon/WireAPI.java @@ -2,10 +2,7 @@ import com.wire.xenon.assets.IAsset; import com.wire.xenon.backend.KeyPackageUpdate; -import com.wire.xenon.backend.models.ClientUpdate; -import com.wire.xenon.backend.models.Conversation; -import com.wire.xenon.backend.models.QualifiedId; -import com.wire.xenon.backend.models.User; +import com.wire.xenon.backend.models.*; import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.models.AssetKey; import com.wire.xenon.models.otr.*; @@ -56,15 +53,15 @@ public interface WireAPI { void acceptConnection(QualifiedId user) throws Exception; - boolean isMlsEnabled(); // Calls GET /mls/public-keys and GET /feature-configs, checking if MLS is enabled on the backend + FeatureConfig getFeatureConfig(); - void uploadClientPublicKey(String clientId, ClientUpdate clientUpdate); // Calls PUT /clients/{clientId} + void uploadClientPublicKey(String clientId, ClientUpdate clientUpdate); - void uploadClientKeyPackages(String clientId, KeyPackageUpdate keyPackageUpdate); // Calls POST /mls/key-packages/self/{client} + void uploadClientKeyPackages(String clientId, KeyPackageUpdate keyPackageUpdate); - byte[] getConversationGroupInfo(QualifiedId conversationId); // Calls GET /conversations/{cnv_domain}/{cnv}/groupinfo returns a response with type message/mls, should be returned as byte array + byte[] getConversationGroupInfo(QualifiedId conversationId); - void commitMlsBundle(byte[] commitBundle); // Calls POST /mls/commit-bundles, we care only if it is successful, no need to return anything + void commitMlsBundle(byte[] commitBundle); - List getUserConversations(); // Calls POST /conversations/list-ids (paginated) to get all the user's id, then calls POST /conversations/list passing the ids. Returns lists of conversations + List getUserConversations(); } diff --git a/src/main/java/com/wire/xenon/WireClient.java b/src/main/java/com/wire/xenon/WireClient.java index a430191..fdb4356 100644 --- a/src/main/java/com/wire/xenon/WireClient.java +++ b/src/main/java/com/wire/xenon/WireClient.java @@ -225,13 +225,6 @@ public interface WireClient extends Closeable { */ ArrayList getAvailablePrekeys(); - /** - * Checks if CryptoBox is closed - * - * @return True if crypto box is closed - */ - boolean isClosed(); - /** * Download publicly available profile picture for the given asset key. This asset is not encrypted * diff --git a/src/main/java/com/wire/xenon/WireClientBase.java b/src/main/java/com/wire/xenon/WireClientBase.java index 5a243cf..5117a6d 100644 --- a/src/main/java/com/wire/xenon/WireClientBase.java +++ b/src/main/java/com/wire/xenon/WireClientBase.java @@ -58,14 +58,12 @@ public QualifiedId getConversationId() { @Override public void close() throws IOException { - crypto.close(); - cryptoMlsClient.close(); - } - - @Override - public boolean isClosed() { - // This method is unused, no need to add Core-Crypto handling. - return crypto.isClosed(); + if (crypto != null) { + crypto.close(); + } + if (cryptoMlsClient != null) { + cryptoMlsClient.close(); + } } /** diff --git a/src/main/java/com/wire/xenon/backend/models/FeatureConfig.java b/src/main/java/com/wire/xenon/backend/models/FeatureConfig.java new file mode 100644 index 0000000..efc47c8 --- /dev/null +++ b/src/main/java/com/wire/xenon/backend/models/FeatureConfig.java @@ -0,0 +1,19 @@ +package com.wire.xenon.backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FeatureConfig { + + @JsonProperty("mls") + public MlsResponse mls; + + public static FeatureConfig disabledMls() { + FeatureConfig featureConfig = new FeatureConfig(); + featureConfig.mls = MlsResponse.disabledMlsResponse(); + return featureConfig; + } +} diff --git a/src/main/java/com/wire/xenon/backend/models/MlsConfigResponse.java b/src/main/java/com/wire/xenon/backend/models/MlsConfigResponse.java new file mode 100644 index 0000000..e936bb4 --- /dev/null +++ b/src/main/java/com/wire/xenon/backend/models/MlsConfigResponse.java @@ -0,0 +1,23 @@ +package com.wire.xenon.backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MlsConfigResponse { + @JsonProperty + public List allowedCipherSuites; + + @JsonProperty + public Integer defaultCipherSuite; + + @JsonProperty + public String defaultProtocol; + + @JsonProperty + public List supportedProtocols; +} diff --git a/src/main/java/com/wire/xenon/backend/models/MlsResponse.java b/src/main/java/com/wire/xenon/backend/models/MlsResponse.java new file mode 100644 index 0000000..f8906f3 --- /dev/null +++ b/src/main/java/com/wire/xenon/backend/models/MlsResponse.java @@ -0,0 +1,25 @@ +package com.wire.xenon.backend.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MlsResponse { + @JsonProperty("config") + public MlsConfigResponse config; + + @JsonProperty + public String status; + + public static MlsResponse disabledMlsResponse() { + MlsResponse mlsResponse = new MlsResponse(); + mlsResponse.status = "disabled"; + return mlsResponse; + } + + public boolean isMlsStatusEnabled() { + return status != null && status.equals("enabled"); + } +} diff --git a/src/main/kotlin/com/wire/xenon/crypto/mls/CryptoMlsClient.kt b/src/main/kotlin/com/wire/xenon/crypto/mls/CryptoMlsClient.kt index 6617ed9..2370eb8 100644 --- a/src/main/kotlin/com/wire/xenon/crypto/mls/CryptoMlsClient.kt +++ b/src/main/kotlin/com/wire/xenon/crypto/mls/CryptoMlsClient.kt @@ -2,6 +2,7 @@ package com.wire.xenon.crypto.mls import com.wire.crypto.CoreCrypto import com.wire.crypto.CoreCryptoCallbacks +import com.wire.crypto.client.Ciphersuite import com.wire.crypto.client.Ciphersuites import com.wire.crypto.client.ClientId import com.wire.crypto.client.CommitBundle @@ -20,14 +21,17 @@ import java.io.File import java.nio.file.Paths import java.util.* -class CryptoMlsClient(private val clientId: String, private val userId: QualifiedId, clientDatabaseKey: String) : Closeable { +class CryptoMlsClient (private val clientId: String, private val userId: QualifiedId, private val ciphersuite: Int, clientDatabaseKey: String) : Closeable { private var coreCrypto: CoreCrypto private var mlsClient: MLSClient private companion object { private const val KEYSTORE_NAME = "keystore" + private const val DEFAULT_CIPHERSUITE_IDENTIFIER = 1 } + constructor(clientId: String, userId: QualifiedId, clientDatabaseKey: String) : this(clientId, userId, DEFAULT_CIPHERSUITE_IDENTIFIER, clientDatabaseKey) + init { runBlocking { val clientDirectoryPath = getDirectoryPath() @@ -41,7 +45,7 @@ class CryptoMlsClient(private val clientId: String, private val userId: Qualifie ) coreCrypto.setCallbacks(callbacks = CoreCryptoCallbacks()) mlsClient = MLSClient(cc = coreCrypto).apply { - mlsInit(id = ClientId(getCoreCryptoId()), Ciphersuites.DEFAULT) + mlsInit(id = ClientId(getCoreCryptoId()), Ciphersuites(setOf(getMlsCipherSuiteName(ciphersuite)))) } } } @@ -69,12 +73,15 @@ class CryptoMlsClient(private val clientId: String, private val userId: Qualifie } fun getPublicKey(): ByteArray { - val publicKey = runBlocking { mlsClient.getPublicKey() } + val publicKey = runBlocking { mlsClient.getPublicKey(getMlsCipherSuiteName(ciphersuite)) } return publicKey.value } fun generateKeyPackages(amount: Int): List { - val keyPackages = runBlocking { mlsClient.generateKeyPackages(amount.toUInt()) } + val keyPackages = runBlocking { mlsClient.generateKeyPackages( + amount = amount.toUInt(), + ciphersuite = getMlsCipherSuiteName(ciphersuite) + ) } return keyPackages.map { it.value } } @@ -88,7 +95,7 @@ class CryptoMlsClient(private val clientId: String, private val userId: Qualifie } fun validKeyPackageCount(): Long { - val packageCount = runBlocking { mlsClient.validKeyPackageCount() } + val packageCount = runBlocking { mlsClient.validKeyPackageCount(getMlsCipherSuiteName(ciphersuite)) } return packageCount.toLong() } @@ -169,6 +176,19 @@ class CryptoMlsClient(private val clientId: String, private val userId: Qualifie } } } + + private fun getMlsCipherSuiteName(code: Int): Ciphersuite { + return when (code) { + DEFAULT_CIPHERSUITE_IDENTIFIER -> Ciphersuite.DEFAULT + 2 -> Ciphersuite.MLS_128_DHKEMP256_AES128GCM_SHA256_P256 + 3 -> Ciphersuite.MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 + 4 -> Ciphersuite.MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448 + 5 -> Ciphersuite.MLS_256_DHKEMP521_AES256GCM_SHA512_P521 + 6 -> Ciphersuite.MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 + 7 -> Ciphersuite.MLS_256_DHKEMP384_AES256GCM_SHA384_P384 + else -> Ciphersuite.DEFAULT + } + } } /** diff --git a/src/test/java/com/wire/xenon/MlsClientTest.java b/src/test/java/com/wire/xenon/MlsClientTest.java index 40e1998..274f380 100644 --- a/src/test/java/com/wire/xenon/MlsClientTest.java +++ b/src/test/java/com/wire/xenon/MlsClientTest.java @@ -23,11 +23,11 @@ public class MlsClientTest { public void testMlsClientInitialization() { QualifiedId user1 = new QualifiedId(UUID.randomUUID(), "wire.com"); String client1 = "alice1_" + UUID.randomUUID(); - CryptoMlsClient mlsClient = new CryptoMlsClient(client1, user1, "pwd"); + CryptoMlsClient mlsClient = new CryptoMlsClient(client1, user1, 2, "pwd"); assert mlsClient != null; mlsClient.close(); - CryptoMlsClient mlsSameClient = new CryptoMlsClient(client1, user1, "pwd"); + CryptoMlsClient mlsSameClient = new CryptoMlsClient(client1, user1, 2, "pwd"); assert mlsSameClient != null; assert mlsSameClient.getId().equals(mlsClient.getId()); @@ -100,7 +100,7 @@ public void testMlsClientsEncryptAndDecrypt() throws IOException { byte[] groupInfo = inputStream.readAllBytes(); // Create a new client and join the conversation - CryptoMlsClient mlsClient = new CryptoMlsClient(client1, user1, "pwd"); + CryptoMlsClient mlsClient = new CryptoMlsClient(client1, user1, 1, "pwd"); assert !mlsClient.conversationExists(groupIdBase64); final byte[] commitBundle = mlsClient.createJoinConversationRequest(groupInfo); assert commitBundle.length > groupInfo.length; @@ -110,7 +110,7 @@ public void testMlsClientsEncryptAndDecrypt() throws IOException { // Create a second client and make the first client invite the second one QualifiedId user2 = new QualifiedId(UUID.randomUUID(), "wire.com"); String client2 = "bob1_" + UUID.randomUUID(); - CryptoMlsClient mlsClient2 = new CryptoMlsClient(client2, user2, "pwd"); + CryptoMlsClient mlsClient2 = new CryptoMlsClient(client2, user2, 1, "pwd"); assert !mlsClient2.conversationExists(groupIdBase64); final List keyPackages = mlsClient2.generateKeyPackages(1); final byte[] welcome = mlsClient.addMemberToConversation(groupIdBase64, keyPackages);