Skip to content

Commit

Permalink
Merge pull request #115 from wireapp/WPB-14877
Browse files Browse the repository at this point in the history
feat(set and use any mls ciphersuite) #WPB-14877
  • Loading branch information
spoonman01 authored Dec 10, 2024
2 parents a6b99ca + c0c07ab commit 93898a3
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 35 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.wire</groupId>
<artifactId>xenon</artifactId>
<version>1.8.0</version>
<version>1.8.1</version>

<name>Xenon</name>
<description>Base Wire Bots Library</description>
Expand Down
17 changes: 7 additions & 10 deletions src/main/java/com/wire/xenon/WireAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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<Conversation> 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<Conversation> getUserConversations();
}
7 changes: 0 additions & 7 deletions src/main/java/com/wire/xenon/WireClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,6 @@ public interface WireClient extends Closeable {
*/
ArrayList<Integer> 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
*
Expand Down
14 changes: 6 additions & 8 deletions src/main/java/com/wire/xenon/WireClientBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

/**
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/wire/xenon/backend/models/FeatureConfig.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/wire/xenon/backend/models/MlsConfigResponse.java
Original file line number Diff line number Diff line change
@@ -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<Integer> allowedCipherSuites;

@JsonProperty
public Integer defaultCipherSuite;

@JsonProperty
public String defaultProtocol;

@JsonProperty
public List<String> supportedProtocols;
}
25 changes: 25 additions & 0 deletions src/main/java/com/wire/xenon/backend/models/MlsResponse.java
Original file line number Diff line number Diff line change
@@ -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");
}
}
30 changes: 25 additions & 5 deletions src/main/kotlin/com/wire/xenon/crypto/mls/CryptoMlsClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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))))
}
}
}
Expand Down Expand Up @@ -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<ByteArray> {
val keyPackages = runBlocking { mlsClient.generateKeyPackages(amount.toUInt()) }
val keyPackages = runBlocking { mlsClient.generateKeyPackages(
amount = amount.toUInt(),
ciphersuite = getMlsCipherSuiteName(ciphersuite)
) }
return keyPackages.map { it.value }
}

Expand All @@ -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()
}

Expand Down Expand Up @@ -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
}
}
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/test/java/com/wire/xenon/MlsClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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;
Expand All @@ -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<byte[]> keyPackages = mlsClient2.generateKeyPackages(1);
final byte[] welcome = mlsClient.addMemberToConversation(groupIdBase64, keyPackages);
Expand Down

0 comments on commit 93898a3

Please sign in to comment.