Skip to content

Commit 541da14

Browse files
feat: Register Proteus and MLS Clients #WPB-18796
* Remove ClientId from DB and replace with only DeviceId * Add Factory create method for AppClientId * Check nullability for ENV variables * Change generateProteusPreKeys to have default values * Rename initialization methods to: initialized[Mls/Proteus]Client * Rework CapabilitiesRequest to be simpler and easier to read
1 parent 327833e commit 541da14

File tree

8 files changed

+110
-119
lines changed

8 files changed

+110
-119
lines changed

lib/src/main/kotlin/com/wire/integrations/jvm/config/Modules.kt

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import com.wire.integrations.jvm.service.WireApplicationManager
4242
import com.wire.integrations.jvm.service.WireTeamEventsListener
4343
import com.wire.integrations.jvm.utils.KtxSerializer
4444
import com.wire.integrations.jvm.utils.mls
45+
import com.wire.integrations.jvm.utils.obfuscateClientId
46+
import com.wire.integrations.jvm.utils.obfuscateId
4547
import com.wire.integrations.jvm.utils.xprotobuf
4648
import io.ktor.client.HttpClient
4749
import io.ktor.client.engine.cio.CIO
@@ -64,8 +66,6 @@ import org.zalando.logbook.client.LogbookClient
6466
import org.zalando.logbook.common.ExperimentalLogbookKtorApi
6567

6668
private const val WEBSOCKET_PING_INTERVAL_MILLIS = 20_000L
67-
private const val PROTEUS_PREKEYS_FROM_COUNT = 0
68-
private const val PROTEUS_PREKEYS_MAX_COUNT = 10
6969
private val logger = LoggerFactory.getLogger(object {}::class.java.`package`.name)
7070

7171
val sdkModule =
@@ -153,34 +153,43 @@ internal suspend fun getOrInitCryptoClient(
153153
.mlsFeatureResponse.mlsFeatureConfigResponse.defaultCipherSuite
154154

155155
val userId = System.getenv("WIRE_SDK_USER_ID")
156+
val userDomain = System.getenv("WIRE_SDK_ENVIRONMENT")
157+
158+
requireNotNull(userId)
159+
requireNotNull(userDomain)
160+
156161
val cryptoClient = CoreCryptoClient.create(
157162
userId = userId,
158163
ciphersuiteCode = mlsCipherSuiteCode
159164
)
160165

161-
val storedClientId = appStorage.getClientId()
162-
if (storedClientId != null) {
163-
logger.info("Loading MLS Client for: $storedClientId")
166+
val storedDeviceId = appStorage.getDeviceId()
167+
if (storedDeviceId != null) {
168+
logger.info("Loading MLS Client for: ${storedDeviceId.obfuscateClientId()}")
169+
val appClientId = AppClientId.create(
170+
userId = userId,
171+
deviceId = storedDeviceId,
172+
userDomain = userDomain
173+
)
164174
// App has a client, load MLS client
165175
cryptoClient.initializeMlsClient(
166-
appClientId = storedClientId,
176+
appClientId = appClientId,
167177
mlsTransport = mlsTransport
168178
)
169-
cryptoClient.setAppClientId(storedClientId)
170179
} else {
180+
val userPassword = System.getenv("WIRE_SDK_PASSWORD")
181+
requireNotNull(userPassword)
182+
171183
// App doesn't have a client, create one
172-
logger.info("Creating Proteus Client")
173-
cryptoClient.createProteusClient()
174-
val preKeys = cryptoClient.generateProteusPreKeys(
175-
from = PROTEUS_PREKEYS_FROM_COUNT,
176-
count = PROTEUS_PREKEYS_MAX_COUNT
177-
)
184+
logger.info("Initializing Proteus Client")
185+
cryptoClient.initializeProteusClient()
186+
val preKeys = cryptoClient.generateProteusPreKeys()
178187
val lastKey = cryptoClient.generateProteusLastPreKey()
179188

180189
val clientResponse = try {
181190
backendClient.registerClient(
182191
registerClientRequest = RegisterClientRequest(
183-
password = System.getenv("WIRE_SDK_PASSWORD"),
192+
password = userPassword,
184193
lastKey = lastKey.toApi(),
185194
preKeys = preKeys.map { it.toApi() },
186195
capabilities = RegisterClientRequest.DEFAULT_CAPABILITIES
@@ -193,11 +202,19 @@ internal suspend fun getOrInitCryptoClient(
193202
)
194203
}
195204

196-
val userDomain = System.getenv("WIRE_SDK_ENVIRONMENT")
197-
val appClientId = AppClientId(value = "$userId:${clientResponse.id}@$userDomain")
198-
appStorage.saveDeviceId(clientResponse.id)
205+
val deviceId = clientResponse.id
206+
val appClientId = AppClientId.create(
207+
userId = userId,
208+
deviceId = deviceId,
209+
userDomain = userDomain
210+
)
211+
appStorage.saveDeviceId(deviceId = deviceId)
199212

200-
logger.info("Creating MLS Client")
213+
logger.info(
214+
"Initializing MLS Client for {} on device: {}",
215+
userId.obfuscateId(),
216+
deviceId.obfuscateClientId()
217+
)
201218
cryptoClient.initializeMlsClient(
202219
appClientId = appClientId,
203220
mlsTransport = mlsTransport
@@ -212,9 +229,6 @@ internal suspend fun getOrInitCryptoClient(
212229
appClientId = appClientId,
213230
mlsKeyPackages = cryptoClient.mlsGenerateKeyPackages().map { it.value }
214231
)
215-
216-
cryptoClient.setAppClientId(appClientId)
217-
appStorage.saveClientId(appClientId.value)
218232
}
219233

220234
return cryptoClient

lib/src/main/kotlin/com/wire/integrations/jvm/crypto/CoreCryptoClient.kt

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,53 +31,13 @@ internal class CoreCryptoClient private constructor(
3131
private val ciphersuite: Ciphersuite,
3232
private var coreCrypto: CoreCrypto
3333
) : CryptoClient {
34-
companion object {
35-
private const val DEFAULT_CIPHERSUITE_IDENTIFIER = 1
36-
private const val KEYSTORE_NAME = "keystore"
37-
38-
suspend fun create(
39-
userId: String,
40-
ciphersuiteCode: Int = DEFAULT_CIPHERSUITE_IDENTIFIER
41-
): CoreCryptoClient {
42-
val clientDirectoryPath = "cryptography/$userId"
43-
val keystorePath = "$clientDirectoryPath/$KEYSTORE_NAME"
44-
val ciphersuite = getMlsCipherSuiteName(ciphersuiteCode)
34+
private var appClientId: AppClientId? = null
4535

46-
File(clientDirectoryPath).mkdirs()
47-
48-
val coreCrypto = CoreCrypto.invoke(
49-
keystore = keystorePath,
50-
databaseKey = IsolatedKoinContext.getCryptographyStoragePassword()
51-
?.let { DatabaseKey(it) }
52-
?: throw InvalidParameter("Cryptography password missing")
53-
)
54-
55-
return CoreCryptoClient(
56-
ciphersuite = ciphersuite,
57-
coreCrypto = coreCrypto
58-
)
59-
}
60-
61-
private fun getMlsCipherSuiteName(code: Int): Ciphersuite =
62-
when (code) {
63-
DEFAULT_CIPHERSUITE_IDENTIFIER -> Ciphersuite.DEFAULT
64-
2 -> Ciphersuite.MLS_128_DHKEMP256_AES128GCM_SHA256_P256
65-
3 -> Ciphersuite.MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519
66-
4 -> Ciphersuite.MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
67-
5 -> Ciphersuite.MLS_256_DHKEMP521_AES256GCM_SHA512_P521
68-
6 -> Ciphersuite.MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448
69-
7 -> Ciphersuite.MLS_256_DHKEMP384_AES256GCM_SHA384_P384
70-
else -> Ciphersuite.DEFAULT
71-
}
36+
private fun setAppClientId(appClientId: AppClientId) {
37+
this@CoreCryptoClient.appClientId = appClientId
7238
}
7339

74-
private var _appClientId: AppClientId? = null
75-
76-
override fun setAppClientId(appClientId: AppClientId) {
77-
_appClientId = appClientId
78-
}
79-
80-
override fun getAppClientId(): AppClientId? = _appClientId
40+
override fun getAppClientId(): AppClientId? = appClientId
8141

8242
override suspend fun encryptMls(
8343
mlsGroupId: MLSGroupId,
@@ -108,7 +68,7 @@ internal class CoreCryptoClient private constructor(
10868
return decryptedMessage.message
10969
}
11070

111-
override suspend fun createProteusClient() =
71+
override suspend fun initializeProteusClient() =
11272
coreCrypto.transaction {
11373
it.proteusInit()
11474
}
@@ -140,6 +100,8 @@ internal class CoreCryptoClient private constructor(
140100
}
141101

142102
coreCrypto.provideTransport(mlsTransport)
103+
104+
setAppClientId(appClientId = appClientId)
143105
}
144106

145107
override suspend fun mlsGetPublicKey(): MlsPublicKeys {
@@ -227,4 +189,44 @@ internal class CoreCryptoClient private constructor(
227189
override fun close() {
228190
runBlocking { coreCrypto.close() }
229191
}
192+
193+
companion object {
194+
private const val DEFAULT_CIPHERSUITE_IDENTIFIER = 1
195+
private const val KEYSTORE_NAME = "keystore"
196+
197+
suspend fun create(
198+
userId: String,
199+
ciphersuiteCode: Int = DEFAULT_CIPHERSUITE_IDENTIFIER
200+
): CoreCryptoClient {
201+
val clientDirectoryPath = "cryptography/$userId"
202+
val keystorePath = "$clientDirectoryPath/$KEYSTORE_NAME"
203+
val ciphersuite = getMlsCipherSuiteName(ciphersuiteCode)
204+
205+
File(clientDirectoryPath).mkdirs()
206+
207+
val coreCrypto = CoreCrypto.invoke(
208+
keystore = keystorePath,
209+
databaseKey = IsolatedKoinContext.getCryptographyStoragePassword()
210+
?.let { DatabaseKey(it) }
211+
?: throw InvalidParameter("Cryptography password missing")
212+
)
213+
214+
return CoreCryptoClient(
215+
ciphersuite = ciphersuite,
216+
coreCrypto = coreCrypto
217+
)
218+
}
219+
220+
private fun getMlsCipherSuiteName(code: Int): Ciphersuite =
221+
when (code) {
222+
DEFAULT_CIPHERSUITE_IDENTIFIER -> Ciphersuite.DEFAULT
223+
2 -> Ciphersuite.MLS_128_DHKEMP256_AES128GCM_SHA256_P256
224+
3 -> Ciphersuite.MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519
225+
4 -> Ciphersuite.MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
226+
5 -> Ciphersuite.MLS_256_DHKEMP521_AES256GCM_SHA512_P521
227+
6 -> Ciphersuite.MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448
228+
7 -> Ciphersuite.MLS_256_DHKEMP384_AES256GCM_SHA384_P384
229+
else -> Ciphersuite.DEFAULT
230+
}
231+
}
230232
}

lib/src/main/kotlin/com/wire/integrations/jvm/crypto/CryptoClient.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import com.wire.integrations.jvm.model.http.MlsPublicKeys
2626
import com.wire.integrations.jvm.model.http.client.PreKeyCrypto
2727

2828
internal interface CryptoClient : AutoCloseable {
29-
fun setAppClientId(appClientId: AppClientId)
30-
3129
fun getAppClientId(): AppClientId?
3230

3331
suspend fun encryptMls(
@@ -43,11 +41,11 @@ internal interface CryptoClient : AutoCloseable {
4341
/**
4442
* Proteus Configuration
4543
*/
46-
suspend fun createProteusClient()
44+
suspend fun initializeProteusClient()
4745

4846
suspend fun generateProteusPreKeys(
49-
from: Int,
50-
count: Int
47+
from: Int = PROTEUS_PREKEYS_FROM_COUNT,
48+
count: Int = PROTEUS_PREKEYS_MAX_COUNT
5149
): ArrayList<PreKeyCrypto>
5250

5351
suspend fun generateProteusLastPreKey(): PreKeyCrypto
@@ -99,5 +97,7 @@ internal interface CryptoClient : AutoCloseable {
9997

10098
companion object {
10199
const val DEFAULT_KEYPACKAGE_COUNT = 100u
100+
const val PROTEUS_PREKEYS_FROM_COUNT = 0
101+
const val PROTEUS_PREKEYS_MAX_COUNT = 10
102102
}
103103
}

lib/src/main/kotlin/com/wire/integrations/jvm/model/AppClientId.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,15 @@ import kotlinx.serialization.Serializable
2323
@Serializable
2424
value class AppClientId(val value: String) {
2525
override fun toString(): String = value.obfuscateClientId()
26+
27+
companion object {
28+
fun create(
29+
userId: String,
30+
deviceId: String,
31+
userDomain: String
32+
): AppClientId =
33+
AppClientId(
34+
value = "$userId:$deviceId@$userDomain"
35+
)
36+
}
2637
}

lib/src/main/kotlin/com/wire/integrations/jvm/model/http/client/CapabilitiesRequest.kt

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,14 @@
1616

1717
package com.wire.integrations.jvm.model.http.client
1818

19-
import kotlinx.serialization.KSerializer
19+
import kotlinx.serialization.SerialName
2020
import kotlinx.serialization.Serializable
21-
import kotlinx.serialization.SerializationException
22-
import kotlinx.serialization.descriptors.PrimitiveKind
23-
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
24-
import kotlinx.serialization.descriptors.SerialDescriptor
25-
import kotlinx.serialization.encoding.Decoder
26-
import kotlinx.serialization.encoding.Encoder
2721

28-
@Serializable(with = CapabilitiesRequestSerializer::class)
29-
enum class CapabilitiesRequest(val value: String) {
30-
LEGALHOLD_IMPLICIT_CONSENT("legalhold-implicit-consent"),
31-
CONSUMABLE_NOTIFICATIONS("consumable-notifications")
32-
}
33-
34-
object CapabilitiesRequestSerializer : KSerializer<CapabilitiesRequest> {
35-
override val descriptor: SerialDescriptor =
36-
PrimitiveSerialDescriptor(
37-
"CapabilitiesRequest",
38-
PrimitiveKind.STRING
39-
)
40-
41-
override fun serialize(
42-
encoder: Encoder,
43-
value: CapabilitiesRequest
44-
) {
45-
encoder.encodeString(value.value)
46-
}
22+
@Serializable
23+
enum class CapabilitiesRequest {
24+
@SerialName("legalhold-implicit-consent")
25+
LEGALHOLD_IMPLICIT_CONSENT,
4726

48-
override fun deserialize(decoder: Decoder): CapabilitiesRequest {
49-
val value = decoder.decodeString()
50-
return CapabilitiesRequest.entries.find { it.value == value }
51-
?: throw SerializationException("Unknown capability: $value")
52-
}
27+
@SerialName("consumable-notifications")
28+
CONSUMABLE_NOTIFICATIONS
5329
}

lib/src/main/kotlin/com/wire/integrations/jvm/persistence/AppSqlLiteStorage.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@ package com.wire.integrations.jvm.persistence
1919
import com.wire.integrations.jvm.App
2020
import com.wire.integrations.jvm.AppQueries
2121
import com.wire.integrations.jvm.AppsSdkDatabase
22-
import com.wire.integrations.jvm.model.AppClientId
2322
import com.wire.integrations.jvm.model.AppData
2423

2524
private const val DEVICE_ID = "device_id"
26-
private const val CLIENT_ID = "client_id"
2725

2826
class AppSqlLiteStorage(db: AppsSdkDatabase) : AppStorage {
2927
private val appQueries: AppQueries = db.appQueries
@@ -44,11 +42,6 @@ class AppSqlLiteStorage(db: AppsSdkDatabase) : AppStorage {
4442
override fun getById(key: String): AppData =
4543
appQueries.selectByKey(key).executeAsOne().let { appMapper(it) }
4644

47-
override fun getClientId(): AppClientId? =
48-
runCatching { AppClientId(getById(CLIENT_ID).value) }.getOrNull()
49-
50-
override fun saveClientId(appClientId: String) = save(CLIENT_ID, appClientId)
51-
5245
override fun getDeviceId(): String? = runCatching { getById(DEVICE_ID).value }.getOrNull()
5346

5447
override fun saveDeviceId(deviceId: String) = save(DEVICE_ID, deviceId)

lib/src/main/kotlin/com/wire/integrations/jvm/persistence/AppStorage.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
package com.wire.integrations.jvm.persistence
1717

18-
import com.wire.integrations.jvm.model.AppClientId
1918
import com.wire.integrations.jvm.model.AppData
2019

2120
interface AppStorage {
@@ -31,10 +30,6 @@ interface AppStorage {
3130

3231
fun getById(key: String): AppData
3332

34-
fun getClientId(): AppClientId?
35-
36-
fun saveClientId(appClientId: String)
37-
3833
fun getDeviceId(): String?
3934

4035
fun saveDeviceId(deviceId: String)

lib/src/test/kotlin/com/wire/integrations/jvm/utils/MockCoreCryptoClient.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ internal class MockCoreCryptoClient private constructor(
4242
private var coreCrypto: CoreCrypto
4343
) : CryptoClient {
4444
val conversationExist = mutableSetOf<MLSGroupId>()
45-
private var _appClientId: AppClientId? = null
45+
private var appClientId: AppClientId? = null
4646

47-
override fun setAppClientId(appClientId: AppClientId) {
48-
_appClientId = appClientId
47+
fun setAppClientId(appClientId: AppClientId) {
48+
this.appClientId = appClientId
4949
}
5050

51-
override fun getAppClientId(): AppClientId? = _appClientId
51+
override fun getAppClientId(): AppClientId? = appClientId
5252

53-
override suspend fun createProteusClient() {
53+
override suspend fun initializeProteusClient() {
5454
// Do nothing
5555
}
5656

0 commit comments

Comments
 (0)