From 83984f7bb35436bd4204a25df330e526456b22da Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 19 Oct 2023 09:45:36 +0200 Subject: [PATCH 01/10] feat(mls-e2ei): adapt remaining e2ei functions from CoreCrypto --- cryptography/build.gradle.kts | 3 + .../wire/kalium/cryptography/MLSClientImpl.kt | 10 +- .../MLSClientImpl.kt | 50 +++++++- .../com/wire/kalium/cryptography/IDs.kt | 30 ++++- .../com/wire/kalium/cryptography/MLSClient.kt | 31 ++++- .../kalium/cryptography/E2EIClientTest.kt | 113 +++++++++++++----- .../wire/kalium/cryptography/MLSClientImpl.kt | 10 +- 7 files changed, 209 insertions(+), 38 deletions(-) diff --git a/cryptography/build.gradle.kts b/cryptography/build.gradle.kts index 659e89e494f..3e14280cf62 100644 --- a/cryptography/build.gradle.kts +++ b/cryptography/build.gradle.kts @@ -67,6 +67,9 @@ kotlin { // Libsodium implementation(libs.libsodiumBindingsMP) + + // UUIDs + implementation(libs.benAsherUUID) } } val commonTest by getting { diff --git a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt index 661b0aa9df1..809c350af64 100644 --- a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt +++ b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt @@ -190,14 +190,22 @@ class MLSClientImpl( TODO("Not yet implemented") } + override suspend fun isE2EIEnabled(): Boolean { + TODO("Not yet implemented") + } + override suspend fun e2eiRotateAll(enrollment: E2EIClient, certificateChain: CertificateChain, newMLSKeyPackageCount: UInt) { TODO("Not yet implemented") } - override suspend fun isGroupVerified(groupId: MLSGroupId): Boolean { + override suspend fun isGroupVerified(groupId: MLSGroupId): E2EIConversationState { TODO("Not supported on apple devices") } + override suspend fun getUserIdentities(groupId: MLSGroupId, clients: List): List { + TODO("Not yet implemented") + } + companion object { fun toUByteList(value: ByteArray): List = value.asUByteArray().asList() fun toUByteList(value: String): List = value.encodeToByteArray().asUByteArray().asList() diff --git a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt index c8541794a7a..44375f0dd53 100644 --- a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt +++ b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt @@ -248,6 +248,10 @@ class MLSClientImpl( coreCrypto.e2eiMlsInitOnly((enrollment as E2EIClientImpl).wireE2eIdentity, certificateChain) } + override suspend fun isE2EIEnabled(): Boolean { + return coreCrypto.e2eiIsEnabled(defaultCiphersuite) + } + override suspend fun e2eiRotateAll( enrollment: E2EIClient, certificateChain: CertificateChain, @@ -260,8 +264,17 @@ class MLSClientImpl( ) } - override suspend fun isGroupVerified(groupId: MLSGroupId): Boolean = - coreCrypto.e2eiConversationState(groupId.decodeBase64Bytes()) != E2eiConversationState.DEGRADED + override suspend fun isGroupVerified(groupId: MLSGroupId): E2EIConversationState = + toE2EIConversationState(coreCrypto.e2eiConversationState(groupId.decodeBase64Bytes())) + + override suspend fun getUserIdentities(groupId: MLSGroupId, clients: List): List { + val clientIds = clients.map { + it.toString().encodeToByteArray() + } + return coreCrypto.getUserIdentities(groupId.decodeBase64Bytes(), clientIds).map { + toIdentity(it) + } + } companion object { fun toUByteList(value: ByteArray): List = value.asUByteArray().asList() @@ -286,6 +299,28 @@ class MLSClientImpl( toGroupInfoBundle(value.groupInfo) ) + fun toRotateBundle(value: com.wire.crypto.RotateBundle) = RotateBundle( + value.commits.map { (groupId, commitBundle) -> + toGroupId(groupId) to toCommitBundle(commitBundle) + }.toMap(), + value.newKeyPackages, + value.keyPackageRefsToRemove + ) + + fun toIdentity(value: com.wire.crypto.WireIdentity) = WireIdentity( + value.clientId, + value.handle, + value.displayName, + value.domain, + value.certificate + ) + + // TODO: remove later, when CoreCrypto return the groupId instead of Hex value + fun toGroupId(value: String): MLSGroupId { + val x = value.chunked(2).map { it.toInt(16).toByte() }.toByteArray() + return toByteArray(toUByteList(x)).encodeBase64() + } + fun toGroupInfoBundle(value: com.wire.crypto.GroupInfoBundle) = GroupInfoBundle( toEncryptionType(value.encryptionType), toRatchetTreeType(value.ratchetTreeType), @@ -303,13 +338,20 @@ class MLSClientImpl( MlsRatchetTreeType.BY_REF -> RatchetTreeType.BY_REF } + fun toE2EIConversationState(value: com.wire.crypto.E2eiConversationState) = when (value) { + E2eiConversationState.VERIFIED -> E2EIConversationState.VERIFIED + //TODO: this value is wrong on CoreCrypto, it will be renamed to NOT_VERIFIED + E2eiConversationState.DEGRADED -> E2EIConversationState.NOT_VERIFIED + E2eiConversationState.NOT_ENABLED -> E2EIConversationState.NOT_ENABLED + } + fun toDecryptedMessageBundle(value: DecryptedMessage) = DecryptedMessageBundle( value.message, value.commitDelay?.toLong(), value.senderClientId?.let { CryptoQualifiedClientId.fromEncodedString(String(it)) }, value.hasEpochChanged, value.identity?.let { - E2EIdentity(it.clientId, it.handle, it.displayName, it.domain) + WireIdentity(it.clientId, it.handle, it.displayName, it.domain, it.certificate) } ) @@ -319,7 +361,7 @@ class MLSClientImpl( value.senderClientId?.let { CryptoQualifiedClientId.fromEncodedString(String(it)) }, value.hasEpochChanged, value.identity?.let { - E2EIdentity(it.clientId, it.handle, it.displayName, it.domain) + WireIdentity(it.clientId, it.handle, it.displayName, it.domain, it.certificate) } ) } diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt index 58d3a1023bb..4a2d54517d7 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt @@ -20,6 +20,9 @@ package com.wire.kalium.cryptography +import com.benasher44.uuid.uuidFrom +import io.ktor.util.encodeBase64 + typealias MLSGroupId = String data class CryptoClientId(val value: String) { @@ -70,16 +73,37 @@ data class CryptoQualifiedClientId( } } -data class E2EIdentity( +data class WireIdentity( var clientId: String, var handle: String, var displayName: String, - var domain: String + var domain: String, + var certificate: String ) data class E2EIQualifiedClientId( val value: String, val userId: CryptoQualifiedID ) { - override fun toString() = "${userId.value}:${value}@${userId.domain}" + override fun toString():String { + val sourceUUID = uuidFrom(userId.value) + + // Convert the UUID to bytes + val uuidBytes = ByteArray(16) + val mostSigBits = sourceUUID.mostSignificantBits + val leastSigBits = sourceUUID.leastSignificantBits + + for (i in 0..7) { + uuidBytes[i] = ((mostSigBits shr (56 - i * 8)) and 0xFF).toByte() + uuidBytes[i + 8] = ((leastSigBits shr (56 - i * 8)) and 0xFF).toByte() + } + + // Base64url encode the UUID bytes without padding + val base64UrlEncoded = uuidBytes.encodeBase64().removeSuffix("==") + + // Print the base64url-encoded UUID without padding + println("Base64URL Encoded UUID (without padding): $base64UrlEncoded") + + return "${base64UrlEncoded}:${value}@${userId.domain}" + } } diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt index f9256e2bbf6..c7620a863c1 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt @@ -38,6 +38,10 @@ enum class RatchetTreeType { BY_REF } +enum class E2EIConversationState { + VERIFIED, NOT_VERIFIED, NOT_ENABLED; +} + open class GroupInfoBundle( var encryptionType: GroupInfoEncryptionType, var ratchetTreeType: RatchetTreeType, @@ -50,12 +54,18 @@ open class CommitBundle( val groupInfoBundle: GroupInfoBundle ) +open class RotateBundle( + var commits: Map, + var newKeyPackages: List, + var keyPackageRefsToRemove: List +) + class DecryptedMessageBundle( val message: ByteArray?, val commitDelay: Long?, val senderClientId: CryptoQualifiedClientId?, val hasEpochChanged: Boolean, - val identity: E2EIdentity? + val identity: WireIdentity? ) @JvmInline @@ -306,6 +316,13 @@ interface MLSClient { */ suspend fun e2eiMlsInitOnly(enrollment: E2EIClient, certificateChain: CertificateChain) + /** + * The E2EI State for the current MLS Client + * + * @return the E2EI state for the current MLS Client + */ + suspend fun isE2EIEnabled(): Boolean + /** * Generate new keypackages after E2EI certificate issued */ @@ -316,5 +333,15 @@ interface MLSClient { * * @return the conversation verification status */ - suspend fun isGroupVerified(groupId: MLSGroupId): Boolean + suspend fun isGroupVerified(groupId: MLSGroupId): E2EIConversationState + + /** + * Get the identity of given clients in the given conversation + * + * @param clients a list of E2EIClientId of the requested clients + * @param groupId MLS group ID for an existing conversation + * + * @return the exist identities for requested clients + */ + suspend fun getUserIdentities(groupId: MLSGroupId, clients: List):List } diff --git a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt index 642bd05a865..06c1c723ce1 100644 --- a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt +++ b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt @@ -34,15 +34,16 @@ class E2EIClientTest : BaseMLSClientTest() { } - private suspend fun createE2EIClient(user: SampleUser): E2EIClient { - return createMLSClient(user.qualifiedClientId).newAcmeEnrollment( + private suspend fun createE2EIClient(user: SampleUser, mlsClient: MLSClient): E2EIClient { + return mlsClient.newAcmeEnrollment( user.e2eiCryptoId, user.name, user.handle ) } @Test - fun givenClient_whenPassingAcmeDirectoryResponse_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenPassingAcmeDirectoryResponse_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) val expectedDirectory = AcmeDirectory( "https://balderdash.hogwash.work:9000/acme/wire/new-nonce", "https://balderdash.hogwash.work:9000/acme/wire/new-account", @@ -53,23 +54,26 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingGetNewAccountRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingGetNewAccountRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) assertTrue(e2eiClient.getNewAccountRequest(NONCE).isNotEmpty()) } @Test - fun givenClient_whenCallingGetNewOrderRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingGetNewOrderRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) assertTrue(e2eiClient.getNewOrderRequest(NONCE).isNotEmpty()) } @Test - fun givenClient_whenCallingGetNewAuthzRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingGetNewAuthzRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -77,8 +81,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingCreateDpopToken_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingCreateDpopToken_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -87,8 +92,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingGetNewDpopChallengeRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingGetNewDpopChallengeRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -98,8 +104,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingGetNewOidcChallengeRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingGetNewOidcChallengeRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -109,8 +116,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingCheckOrderRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingCheckOrderRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -122,8 +130,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingFinalizeRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingFinalizeRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -136,8 +145,9 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenClient_whenCallingCertificateRequest_ReturnNonEmptyResult() = runTest { - val e2eiClient = createE2EIClient(ALICE1) + fun givenE2EIClient_whenCallingCertificateRequest_ReturnNonEmptyResult() = runTest { + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -150,10 +160,33 @@ class E2EIClientTest : BaseMLSClientTest() { assertTrue(e2eiClient.certificateRequest(NONCE).isNotEmpty()) } + @Test + fun givenE2EIClient_whenRotatingAllConversations_ReturnCorrectRotateBundles() = runTest { + val newKeyPackagesCount = 10U + val mlsClient = createMLSClient(ALICE1.qualifiedClientId) + val e2eiClient = createE2EIClient(ALICE1, mlsClient) + e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) + e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) + e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) + e2eiClient.setAuthzResponse(AUTHZ_API_RESPONSE) + e2eiClient.createDpopToken(NONCE) + e2eiClient.setChallengeResponse(DPOP_CHALLENGE_RESPONSE) + e2eiClient.setChallengeResponse(OIDC_CHALLENGE_RESPONSE) + e2eiClient.checkOrderResponse(ORDER_RESPONSE) + e2eiClient.finalizeResponse(FINALIZE_RESPONSE) + e2eiClient.certificateRequest(NONCE) + val rotateBundles = mlsClient.e2eiRotateAll(e2eiClient, CERTIFICATE, newKeyPackagesCount) + val x = 1 + } + + // userIdentities + // isVerified + + companion object { val ALICE1 = SampleUser( - CryptoQualifiedID("t6wRpI8BRSeviBwwiFp5MQ", "wire.com"), CryptoClientId("fb4b58152e20"), "Alice", "Alice_wire" + CryptoQualifiedID("837655f7-b448-465a-b4b2-93f0919b38f0", "elna.wire.link"), CryptoClientId("fb4b58152e20"), "Mojtaba Chenani", "mojtaba_wire" ) val ACME_DIRECTORY_API_RESPONSE = """ @@ -187,7 +220,7 @@ class E2EIClientTest : BaseMLSClientTest() { "identifiers":[ { "type":"wireapp-id", - "value":"{\"name\":\"mojtaba chenani staging\",\"domain\":\"staging.zinfra.io\",\"client-id\":\"im:wireapp=ZTE1Mjc0MzEyNDQ0NGJhY2E1NWZmNjA2ZTk1MjIyMzM/4eedbfe16d25bbf3@staging.zinfra.io\",\"handle\":\"im:wireapp=mjstaging\"}" + "value":"{\"name\":\"Mojtaba Chenani\",\"domain\":\"elna.wire.link\",\"client-id\":\"im:wireapp=IG9YvzuWQIKUaRk12F5CIQ/953218e68a63641f@elna.wire.link\",\"handle\":\"im:wireapp=mojtaba_wire\"}" } ], "notBefore":"2023-05-07T12:00:50.1666Z", @@ -219,7 +252,7 @@ class E2EIClientTest : BaseMLSClientTest() { "expires": "3000-06-07T10:22:49Z", "identifier": { "type": "wireapp-id", - "value": "{\"name\":\"Mojtaba Chenani\",\"domain\":\"anta.wire.link\",\"client-id\":\"im:wireapp=ZTk5ZjQ1NGNmNjdiNDQwMjhhNGUwYWVjZmNjZjBmMDU/89f1c4056c99edcb@anta.wire.link\",\"handle\":\"im:wireapp=mojtaba_wire\"}" + "value":"{\"name\":\"Mojtaba Chenani\",\"domain\":\"elna.wire.link\",\"client-id\":\"im:wireapp=IG9YvzuWQIKUaRk12F5CIQ/953218e68a63641f@elna.wire.link\",\"handle\":\"im:wireapp=mojtaba_wire\"}" }, "status": "pending", "wildcard": false @@ -259,7 +292,7 @@ class E2EIClientTest : BaseMLSClientTest() { "identifiers": [ { "type": "wireapp-id", - "value": "{\"name\":\"Smith, Alice M (QA)\",\"domain\":\"example.com\",\"client-id\":\"impp:wireapp=NjJiYTRjMTIyODJjNDY5YmE5NGZmMjhhNjFkODA0Njk/d2ba2c1a57588ee4@example.com\",\"handle\":\"impp:wireapp=alice.smith.qa@example.com\"}" + "value":"{\"name\":\"Mojtaba Chenani\",\"domain\":\"elna.wire.link\",\"client-id\":\"im:wireapp=IG9YvzuWQIKUaRk12F5CIQ/953218e68a63641f@elna.wire.link\",\"handle\":\"im:wireapp=mojtaba_wire\"}" } ], "authorizations": [ @@ -277,7 +310,7 @@ class E2EIClientTest : BaseMLSClientTest() { "identifiers": [ { "type": "wireapp-id", - "value": "{\"name\":\"Smith, Alice M (QA)\",\"domain\":\"example.com\",\"client-id\":\"impp:wireapp=NjJiYTRjMTIyODJjNDY5YmE5NGZmMjhhNjFkODA0Njk/d2ba2c1a57588ee4@example.com\",\"handle\":\"impp:wireapp=alice.smith.qa@example.com\"}" + "value":"{\"name\":\"Mojtaba Chenani\",\"domain\":\"elna.wire.link\",\"client-id\":\"im:wireapp=IG9YvzuWQIKUaRk12F5CIQ/953218e68a63641f@elna.wire.link\",\"handle\":\"im:wireapp=mojtaba_wire\"}" } ], "authorizations": [ @@ -287,5 +320,31 @@ class E2EIClientTest : BaseMLSClientTest() { "notBefore": "2013-02-09T14:59:20.442908Z", "notAfter": "3000-02-09T15:59:20.442908Z" }""".toByteArray() + val CERTIFICATE ="""-----BEGIN CERTIFICATE----- + MIICNDCCAdqgAwIBAgIQYIj2PpdPLqtQXMmx0TmXujAKBggqhkjOPQQDAjAuMQ0w + CwYDVQQKEwR3aXJlMR0wGwYDVQQDExR3aXJlIEludGVybWVkaWF0ZSBDQTAeFw0y + MzEwMDIxNTIyMjJaFw0yMzEyMzExNTIyMjJaMDMxFzAVBgNVBAoTDmVsbmEud2ly + ZS5saW5rMRgwFgYDVQQDEw9Nb2p0YWJhIENoZW5hbmkwKjAFBgMrZXADIQAonK3u + cLIUnWP+8iG2GdabCWmzfiHTgXMncNx/r064LKOCAQIwgf8wDgYDVR0PAQH/BAQD + AgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUhVb7 + GEXzaKxm3yiNEV0DOd78LBcwHwYDVR0jBBgwFoAUADMwoCFunlLkYO2SQbOAUOIL + VCowZQYDVR0RBF4wXIZBaW06d2lyZWFwcD1JRzlZdnp1V1FJS1VhUmsxMkY1Q0lR + Lzk1MzIxOGU2OGE2MzY0MWZAZWxuYS53aXJlLmxpbmuGF2ltOndpcmVhcHA9bW9q + dGFiYV93aXJlMCcGDCsGAQQBgqRkxihAAQQXMBUCAQYEDmdvb2dsZS1hbmRyb2lk + BAAwCgYIKoZIzj0EAwIDSAAwRQIhAJORy8WUjP8spjxlCCNOCAQrPIUbl6BTQGtv + FhJqP3UrAiAC4mbuQ6BlVmiovCzqP1YbiaGimvBEm/XTwtWJE6wM0A== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBuDCCAV6gAwIBAgIQUJ8AHZqe79OeFVEPkdtQrDAKBggqhkjOPQQDAjAmMQ0w + CwYDVQQKEwR3aXJlMRUwEwYDVQQDEwx3aXJlIFJvb3QgQ0EwHhcNMjMwNDE3MDkw + ODQxWhcNMzMwNDE0MDkwODQxWjAuMQ0wCwYDVQQKEwR3aXJlMR0wGwYDVQQDExR3 + aXJlIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB9p + iYVv5ik10pwkOGdwVI6F6a8YKk9Ro/CqahPcTfefhOhL/M5RxzWmi2oW75mW6WKr + tG94D45Ur6yfNclLspmjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG + AQH/AgEAMB0GA1UdDgQWBBQAMzCgIW6eUuRg7ZJBs4BQ4gtUKjAfBgNVHSMEGDAW + gBR40ZJlSIKIjEI/4ZMwgV3X5CB7tDAKBggqhkjOPQQDAgNIADBFAiEA5VT2B38E + 9EunvJiLRCG9baeeMq4Yn1LwOT10cXdUIIICIEnDUrd2XW69YnUIPF3bEHln3oKt + wje0yUIA61GMpqNz + -----END CERTIFICATE-----""" } } diff --git a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt index c1bcbbf9e7b..d8ee164a4e0 100644 --- a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt +++ b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/MLSClientImpl.kt @@ -128,6 +128,10 @@ class MLSClientImpl : MLSClient { TODO("Not yet implemented") } + override suspend fun isE2EIEnabled(): Boolean { + TODO("Not yet implemented") + } + override suspend fun e2eiRotateAll( enrollment: E2EIClient, certificateChain: CertificateChain, @@ -136,7 +140,11 @@ class MLSClientImpl : MLSClient { TODO("Not yet implemented") } - override suspend fun isGroupVerified(groupId: MLSGroupId): Boolean { + override suspend fun isGroupVerified(groupId: MLSGroupId): E2EIConversationState { TODO("Not supported on js") } + + override suspend fun getUserIdentities(groupId: MLSGroupId, clients: List): List { + TODO("Not yet implemented") + } } From d3c5af2adbdde2a11e279a1119f302f503f4a391 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 19 Oct 2023 11:10:05 +0200 Subject: [PATCH 02/10] remove logs --- .../src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt index 4a2d54517d7..38aa6fe781d 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt @@ -101,9 +101,6 @@ data class E2EIQualifiedClientId( // Base64url encode the UUID bytes without padding val base64UrlEncoded = uuidBytes.encodeBase64().removeSuffix("==") - // Print the base64url-encoded UUID without padding - println("Base64URL Encoded UUID (without padding): $base64UrlEncoded") - return "${base64UrlEncoded}:${value}@${userId.domain}" } } From efc65cbdf3a438f656b446f9ec1dd64babc9bb93 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 19 Oct 2023 11:50:57 +0200 Subject: [PATCH 03/10] fix E2EIQualifiedIDTest --- .../com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt index fbfa0eaa2bc..31ecf04718a 100644 --- a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt +++ b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt @@ -31,10 +31,11 @@ class E2EIQualifiedIDTest { companion object { private const val CLIENT_ID = "client_id" - private const val USER_ID = "user_id" + private const val USER_ID = "41d2b365-f4a9-4ba1-bddf-5afb8aca6786" private const val DOMAIN = "domain" + private const val ENCODED_USER_ID = "QdKzZfSpS6G931r7ispnhg" - val ENCODED_E2EI_QUALIFIED_CLIENT_ID = "${USER_ID}:$CLIENT_ID@$DOMAIN" + val ENCODED_E2EI_QUALIFIED_CLIENT_ID = "${ENCODED_USER_ID}:$CLIENT_ID@$DOMAIN" val E2EI_QUALIFIED_CLIENT_ID = E2EIQualifiedClientId(CLIENT_ID, CryptoQualifiedID(USER_ID, DOMAIN)) } From c1070ab424d6751617e708fad2e6115d0a8dffea Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 19 Oct 2023 13:25:09 +0200 Subject: [PATCH 04/10] remove unneeded codes --- .../MLSClientImpl.kt | 6 +-- .../kalium/cryptography/E2EIClientTest.kt | 49 ------------------- .../cryptography/E2EIQualifiedIDTest.kt | 2 +- 3 files changed, 4 insertions(+), 53 deletions(-) diff --git a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt index 44375f0dd53..d2653990679 100644 --- a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt +++ b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt @@ -316,9 +316,9 @@ class MLSClientImpl( ) // TODO: remove later, when CoreCrypto return the groupId instead of Hex value - fun toGroupId(value: String): MLSGroupId { - val x = value.chunked(2).map { it.toInt(16).toByte() }.toByteArray() - return toByteArray(toUByteList(x)).encodeBase64() + fun toGroupId(hexValue: String): MLSGroupId { + val byteArrayValue = hexValue.chunked(2).map { it.toInt(16).toByte() }.toByteArray() + return toByteArray(toUByteList(byteArrayValue)).encodeBase64() } fun toGroupInfoBundle(value: com.wire.crypto.GroupInfoBundle) = GroupInfoBundle( diff --git a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt index 06c1c723ce1..b375ccd7678 100644 --- a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt +++ b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt @@ -160,29 +160,6 @@ class E2EIClientTest : BaseMLSClientTest() { assertTrue(e2eiClient.certificateRequest(NONCE).isNotEmpty()) } - @Test - fun givenE2EIClient_whenRotatingAllConversations_ReturnCorrectRotateBundles() = runTest { - val newKeyPackagesCount = 10U - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) - e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) - e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) - e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) - e2eiClient.setAuthzResponse(AUTHZ_API_RESPONSE) - e2eiClient.createDpopToken(NONCE) - e2eiClient.setChallengeResponse(DPOP_CHALLENGE_RESPONSE) - e2eiClient.setChallengeResponse(OIDC_CHALLENGE_RESPONSE) - e2eiClient.checkOrderResponse(ORDER_RESPONSE) - e2eiClient.finalizeResponse(FINALIZE_RESPONSE) - e2eiClient.certificateRequest(NONCE) - val rotateBundles = mlsClient.e2eiRotateAll(e2eiClient, CERTIFICATE, newKeyPackagesCount) - val x = 1 - } - - // userIdentities - // isVerified - - companion object { val ALICE1 = SampleUser( @@ -320,31 +297,5 @@ class E2EIClientTest : BaseMLSClientTest() { "notBefore": "2013-02-09T14:59:20.442908Z", "notAfter": "3000-02-09T15:59:20.442908Z" }""".toByteArray() - val CERTIFICATE ="""-----BEGIN CERTIFICATE----- - MIICNDCCAdqgAwIBAgIQYIj2PpdPLqtQXMmx0TmXujAKBggqhkjOPQQDAjAuMQ0w - CwYDVQQKEwR3aXJlMR0wGwYDVQQDExR3aXJlIEludGVybWVkaWF0ZSBDQTAeFw0y - MzEwMDIxNTIyMjJaFw0yMzEyMzExNTIyMjJaMDMxFzAVBgNVBAoTDmVsbmEud2ly - ZS5saW5rMRgwFgYDVQQDEw9Nb2p0YWJhIENoZW5hbmkwKjAFBgMrZXADIQAonK3u - cLIUnWP+8iG2GdabCWmzfiHTgXMncNx/r064LKOCAQIwgf8wDgYDVR0PAQH/BAQD - AgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUhVb7 - GEXzaKxm3yiNEV0DOd78LBcwHwYDVR0jBBgwFoAUADMwoCFunlLkYO2SQbOAUOIL - VCowZQYDVR0RBF4wXIZBaW06d2lyZWFwcD1JRzlZdnp1V1FJS1VhUmsxMkY1Q0lR - Lzk1MzIxOGU2OGE2MzY0MWZAZWxuYS53aXJlLmxpbmuGF2ltOndpcmVhcHA9bW9q - dGFiYV93aXJlMCcGDCsGAQQBgqRkxihAAQQXMBUCAQYEDmdvb2dsZS1hbmRyb2lk - BAAwCgYIKoZIzj0EAwIDSAAwRQIhAJORy8WUjP8spjxlCCNOCAQrPIUbl6BTQGtv - FhJqP3UrAiAC4mbuQ6BlVmiovCzqP1YbiaGimvBEm/XTwtWJE6wM0A== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - MIIBuDCCAV6gAwIBAgIQUJ8AHZqe79OeFVEPkdtQrDAKBggqhkjOPQQDAjAmMQ0w - CwYDVQQKEwR3aXJlMRUwEwYDVQQDEwx3aXJlIFJvb3QgQ0EwHhcNMjMwNDE3MDkw - ODQxWhcNMzMwNDE0MDkwODQxWjAuMQ0wCwYDVQQKEwR3aXJlMR0wGwYDVQQDExR3 - aXJlIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB9p - iYVv5ik10pwkOGdwVI6F6a8YKk9Ro/CqahPcTfefhOhL/M5RxzWmi2oW75mW6WKr - tG94D45Ur6yfNclLspmjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG - AQH/AgEAMB0GA1UdDgQWBBQAMzCgIW6eUuRg7ZJBs4BQ4gtUKjAfBgNVHSMEGDAW - gBR40ZJlSIKIjEI/4ZMwgV3X5CB7tDAKBggqhkjOPQQDAgNIADBFAiEA5VT2B38E - 9EunvJiLRCG9baeeMq4Yn1LwOT10cXdUIIICIEnDUrd2XW69YnUIPF3bEHln3oKt - wje0yUIA61GMpqNz - -----END CERTIFICATE-----""" } } diff --git a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt index 31ecf04718a..7dd45484244 100644 --- a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt +++ b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIQualifiedIDTest.kt @@ -35,7 +35,7 @@ class E2EIQualifiedIDTest { private const val DOMAIN = "domain" private const val ENCODED_USER_ID = "QdKzZfSpS6G931r7ispnhg" - val ENCODED_E2EI_QUALIFIED_CLIENT_ID = "${ENCODED_USER_ID}:$CLIENT_ID@$DOMAIN" + const val ENCODED_E2EI_QUALIFIED_CLIENT_ID = "${ENCODED_USER_ID}:$CLIENT_ID@$DOMAIN" val E2EI_QUALIFIED_CLIENT_ID = E2EIQualifiedClientId(CLIENT_ID, CryptoQualifiedID(USER_ID, DOMAIN)) } From 54ccd565d0ed092799cd5eaf6e50d27362dc21e9 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 19 Oct 2023 13:27:45 +0200 Subject: [PATCH 05/10] undo unneeded changes --- .../kalium/cryptography/E2EIClientTest.kt | 54 ++++++++----------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt index b375ccd7678..0eb904b54b1 100644 --- a/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt +++ b/cryptography/src/commonTest/kotlin/com/wire/kalium/cryptography/E2EIClientTest.kt @@ -34,16 +34,15 @@ class E2EIClientTest : BaseMLSClientTest() { } - private suspend fun createE2EIClient(user: SampleUser, mlsClient: MLSClient): E2EIClient { - return mlsClient.newAcmeEnrollment( + private suspend fun createE2EIClient(user: SampleUser): E2EIClient { + return createMLSClient(user.qualifiedClientId).newAcmeEnrollment( user.e2eiCryptoId, user.name, user.handle ) } @Test - fun givenE2EIClient_whenPassingAcmeDirectoryResponse_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenPassingAcmeDirectoryResponse_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) val expectedDirectory = AcmeDirectory( "https://balderdash.hogwash.work:9000/acme/wire/new-nonce", "https://balderdash.hogwash.work:9000/acme/wire/new-account", @@ -54,26 +53,23 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingGetNewAccountRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingGetNewAccountRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) assertTrue(e2eiClient.getNewAccountRequest(NONCE).isNotEmpty()) } @Test - fun givenE2EIClient_whenCallingGetNewOrderRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingGetNewOrderRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) assertTrue(e2eiClient.getNewOrderRequest(NONCE).isNotEmpty()) } @Test - fun givenE2EIClient_whenCallingGetNewAuthzRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingGetNewAuthzRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -81,9 +77,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingCreateDpopToken_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingCreateDpopToken_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -92,9 +87,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingGetNewDpopChallengeRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingGetNewDpopChallengeRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -104,9 +98,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingGetNewOidcChallengeRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingGetNewOidcChallengeRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -116,9 +109,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingCheckOrderRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingCheckOrderRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -130,9 +122,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingFinalizeRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingFinalizeRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) @@ -145,9 +136,8 @@ class E2EIClientTest : BaseMLSClientTest() { } @Test - fun givenE2EIClient_whenCallingCertificateRequest_ReturnNonEmptyResult() = runTest { - val mlsClient = createMLSClient(ALICE1.qualifiedClientId) - val e2eiClient = createE2EIClient(ALICE1, mlsClient) + fun givenClient_whenCallingCertificateRequest_ReturnNonEmptyResult() = runTest { + val e2eiClient = createE2EIClient(ALICE1) e2eiClient.directoryResponse(ACME_DIRECTORY_API_RESPONSE) e2eiClient.setAccountResponse(NEW_ACCOUNT_API_RESPONSE) e2eiClient.setOrderResponse(NEW_ORDER_API_RESPONSE) From 001256139fbe8e0537c19f78913d02ecd6238d4a Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Fri, 20 Oct 2023 09:30:50 +0200 Subject: [PATCH 06/10] fix Detekt --- .../MLSClientImpl.kt | 22 +++++++++++-------- .../com/wire/kalium/cryptography/IDs.kt | 3 ++- .../com/wire/kalium/cryptography/MLSClient.kt | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt index d2653990679..335b15b6461 100644 --- a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt +++ b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt @@ -87,10 +87,12 @@ class MLSClientImpl( } override suspend fun joinByExternalCommit(publicGroupState: ByteArray): CommitBundle { - return toCommitBundle(coreCrypto.joinByExternalCommit( - publicGroupState, - defaultGroupConfiguration, - MlsCredentialType.BASIC) + return toCommitBundle( + coreCrypto.joinByExternalCommit( + publicGroupState, + defaultGroupConfiguration, + MlsCredentialType.BASIC + ) ) } @@ -137,9 +139,11 @@ class MLSClientImpl( message ) - val messageBundle = listOf(toDecryptedMessageBundle( - decryptedMessage - )) + val messageBundle = listOf( + toDecryptedMessageBundle( + decryptedMessage + ) + ) val bufferedMessages = decryptedMessage.bufferedMessages?.map { toDecryptedMessageBundle(it) } ?: emptyList() @@ -316,6 +320,7 @@ class MLSClientImpl( ) // TODO: remove later, when CoreCrypto return the groupId instead of Hex value + @Suppress("MagicNumber") fun toGroupId(hexValue: String): MLSGroupId { val byteArrayValue = hexValue.chunked(2).map { it.toInt(16).toByte() }.toByteArray() return toByteArray(toUByteList(byteArrayValue)).encodeBase64() @@ -340,7 +345,7 @@ class MLSClientImpl( fun toE2EIConversationState(value: com.wire.crypto.E2eiConversationState) = when (value) { E2eiConversationState.VERIFIED -> E2EIConversationState.VERIFIED - //TODO: this value is wrong on CoreCrypto, it will be renamed to NOT_VERIFIED + // TODO: this value is wrong on CoreCrypto, it will be renamed to NOT_VERIFIED E2eiConversationState.DEGRADED -> E2EIConversationState.NOT_VERIFIED E2eiConversationState.NOT_ENABLED -> E2EIConversationState.NOT_ENABLED } @@ -365,5 +370,4 @@ class MLSClientImpl( } ) } - } diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt index 38aa6fe781d..d0e5c92aad2 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt @@ -81,11 +81,12 @@ data class WireIdentity( var certificate: String ) +@Suppress("MagicNumber") data class E2EIQualifiedClientId( val value: String, val userId: CryptoQualifiedID ) { - override fun toString():String { + override fun toString(): String { val sourceUUID = uuidFrom(userId.value) // Convert the UUID to bytes diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt index c7620a863c1..3e62eefb721 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/MLSClient.kt @@ -343,5 +343,5 @@ interface MLSClient { * * @return the exist identities for requested clients */ - suspend fun getUserIdentities(groupId: MLSGroupId, clients: List):List + suspend fun getUserIdentities(groupId: MLSGroupId, clients: List): List } From 39e3792b9a9265d123d62037bfba39cf6f8e9a3a Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Fri, 20 Oct 2023 09:53:23 +0200 Subject: [PATCH 07/10] fix broken functions --- .../data/conversation/MLSConversationRepository.kt | 8 ++++++-- .../conversation/MLSConversationRepositoryTest.kt | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt index 56a74c31064..29d956b41bd 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt @@ -21,6 +21,7 @@ package com.wire.kalium.logic.data.conversation import com.wire.kalium.cryptography.CommitBundle import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.CryptoQualifiedID +import com.wire.kalium.cryptography.E2EIConversationState import com.wire.kalium.logger.obfuscateId import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.MLSFailure @@ -513,8 +514,11 @@ internal class MLSConversationDataSource( mlsClientProvider.getMLSClient().flatMap { mlsClient -> wrapMLSRequest { mlsClient.isGroupVerified(idMapper.toCryptoModel(groupID)) } }.map { - if (it) Conversation.VerificationStatus.VERIFIED - else Conversation.VerificationStatus.NOT_VERIFIED + when (it) { + E2EIConversationState.VERIFIED -> Conversation.VerificationStatus.VERIFIED + E2EIConversationState.NOT_VERIFIED -> Conversation.VerificationStatus.NOT_VERIFIED + E2EIConversationState.NOT_ENABLED -> Conversation.VerificationStatus.NOT_VERIFIED + } } private suspend fun retryOnCommitFailure( diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt index 936ca5f44f1..34dedd9a9c6 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt @@ -19,6 +19,7 @@ package com.wire.kalium.logic.data.conversation import com.wire.kalium.cryptography.CommitBundle +import com.wire.kalium.cryptography.E2EIConversationState import com.wire.kalium.cryptography.GroupInfoBundle import com.wire.kalium.cryptography.GroupInfoEncryptionType import com.wire.kalium.cryptography.MLSClient @@ -1030,7 +1031,7 @@ class MLSConversationRepositoryTest { fun givenVerifiedConversation_whenGetGroupVerify_thenVerifiedReturned() = runTest { val (arrangement, mlsConversationRepository) = Arrangement() .withGetMLSClientSuccessful() - .withGetGroupVerifyReturn(true) + .withGetGroupVerifyReturn(E2EIConversationState.VERIFIED) .arrange() assertEquals( @@ -1048,7 +1049,7 @@ class MLSConversationRepositoryTest { fun givenNotVerifiedConversation_whenGetGroupVerify_thenNotVerifiedReturned() = runTest { val (arrangement, mlsConversationRepository) = Arrangement() .withGetMLSClientSuccessful() - .withGetGroupVerifyReturn(false) + .withGetGroupVerifyReturn(E2EIConversationState.NOT_VERIFIED) .arrange() assertEquals( @@ -1067,7 +1068,7 @@ class MLSConversationRepositoryTest { val failure = CoreFailure.Unknown(RuntimeException("Error!")) val (arrangement, mlsConversationRepository) = Arrangement() .withGetMLSClientFailed(failure) - .withGetGroupVerifyReturn(false) + .withGetGroupVerifyReturn(E2EIConversationState.NOT_VERIFIED) .arrange() assertEquals( @@ -1290,11 +1291,11 @@ class MLSConversationRepositoryTest { .thenReturn(Either.Right(Unit)) } - fun withGetGroupVerifyReturn(isVerified: Boolean) = apply { + fun withGetGroupVerifyReturn(verificationStatus: E2EIConversationState) = apply { given(mlsClient) .suspendFunction(mlsClient::isGroupVerified) .whenInvokedWith(anything()) - .thenReturn(isVerified) + .thenReturn(verificationStatus) } fun arrange() = this to MLSConversationDataSource( From 7ea780bb6961c77f60b6dca4e4aac2174b333ccc Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Fri, 20 Oct 2023 09:58:59 +0200 Subject: [PATCH 08/10] add mapper for E2EIConversationState --- .../data/conversation/ConversationMapper.kt | 7 +++++++ .../conversation/MLSConversationRepository.kt | 8 +------- .../MLSConversationRepositoryTest.kt | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt index e59dde28bd7..fb04ea7c3af 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt @@ -18,6 +18,7 @@ package com.wire.kalium.logic.data.conversation +import com.wire.kalium.cryptography.E2EIConversationState import com.wire.kalium.logic.data.connection.ConnectionStatusMapper import com.wire.kalium.logic.data.id.IdMapper import com.wire.kalium.logic.data.id.NetworkQualifiedId @@ -545,3 +546,9 @@ internal fun Protocol.toModel(): Conversation.Protocol = when (this) { Protocol.MIXED -> Conversation.Protocol.MIXED Protocol.MLS -> Conversation.Protocol.MLS } + +internal fun E2EIConversationState.toModel(): Conversation.VerificationStatus = when (this) { + E2EIConversationState.VERIFIED -> Conversation.VerificationStatus.VERIFIED + E2EIConversationState.NOT_VERIFIED -> Conversation.VerificationStatus.NOT_VERIFIED + E2EIConversationState.NOT_ENABLED -> Conversation.VerificationStatus.NOT_VERIFIED +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt index 29d956b41bd..4ae6584f742 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt @@ -513,13 +513,7 @@ internal class MLSConversationDataSource( override suspend fun getConversationVerificationStatus(groupID: GroupID): Either = mlsClientProvider.getMLSClient().flatMap { mlsClient -> wrapMLSRequest { mlsClient.isGroupVerified(idMapper.toCryptoModel(groupID)) } - }.map { - when (it) { - E2EIConversationState.VERIFIED -> Conversation.VerificationStatus.VERIFIED - E2EIConversationState.NOT_VERIFIED -> Conversation.VerificationStatus.NOT_VERIFIED - E2EIConversationState.NOT_ENABLED -> Conversation.VerificationStatus.NOT_VERIFIED - } - } + }.map { it.toModel() } private suspend fun retryOnCommitFailure( groupID: GroupID, diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt index 34dedd9a9c6..8fef1218627 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt @@ -1063,6 +1063,24 @@ class MLSConversationRepositoryTest { .wasInvoked(once) } + @Test + fun givenNotEnabledE2EIForConversation_whenGetGroupVerify_thenNotVerifiedReturned() = runTest { + val (arrangement, mlsConversationRepository) = Arrangement() + .withGetMLSClientSuccessful() + .withGetGroupVerifyReturn(E2EIConversationState.NOT_ENABLED) + .arrange() + + assertEquals( + Either.Right(Conversation.VerificationStatus.NOT_VERIFIED), + mlsConversationRepository.getConversationVerificationStatus(Arrangement.GROUP_ID) + ) + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::isGroupVerified) + .with(any()) + .wasInvoked(once) + } + @Test fun givenNoMLSClient_whenGetGroupVerify_thenErrorReturned() = runTest { val failure = CoreFailure.Unknown(RuntimeException("Error!")) From db6430b285cbb21717200e186fc154aa397a31c2 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Fri, 20 Oct 2023 10:06:02 +0200 Subject: [PATCH 09/10] fix detekt --- .../wire/kalium/logic/data/conversation/ConversationMapper.kt | 3 +-- .../logic/data/conversation/MLSConversationRepository.kt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt index fb04ea7c3af..5066b6170d7 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ - +@file:Suppress("TooManyFunctions") package com.wire.kalium.logic.data.conversation import com.wire.kalium.cryptography.E2EIConversationState @@ -57,7 +57,6 @@ import kotlinx.datetime.toInstant import kotlin.time.DurationUnit import kotlin.time.toDuration -@Suppress("TooManyFunctions") interface ConversationMapper { fun fromApiModelToDaoModel(apiModel: ConversationResponse, mlsGroupState: GroupState?, selfUserTeamId: TeamId?): ConversationEntity fun fromDaoModel(daoModel: ConversationViewEntity): Conversation diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt index 4ae6584f742..ce6b115943c 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt @@ -21,7 +21,6 @@ package com.wire.kalium.logic.data.conversation import com.wire.kalium.cryptography.CommitBundle import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.CryptoQualifiedID -import com.wire.kalium.cryptography.E2EIConversationState import com.wire.kalium.logger.obfuscateId import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.MLSFailure From 86bffd1ed4bb65c7f5dd8467bc8e1cb4b43dc782 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Fri, 20 Oct 2023 17:22:08 +0200 Subject: [PATCH 10/10] feat(e2ei): initiate e2ei client based on the e2eiState in MLSClient --- .../logic/data/client/E2EIClientProvider.kt | 27 +++- .../kalium/logic/data/e2ei/E2EIRepository.kt | 28 ++-- .../kalium/logic/data/user/UserRepository.kt | 2 +- .../kalium/logic/feature/UserSessionScope.kt | 8 +- .../logic/client/E2EIClientProviderTest.kt | 142 ++++++++++++++++++ .../logic/data/e2ei/E2EIRepositoryTest.kt | 14 +- .../wire/kalium/logic/framework/TestUser.kt | 2 +- .../provider/E2EIClientProviderArrangement.kt | 102 +++++++++++++ 8 files changed, 285 insertions(+), 40 deletions(-) create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/client/E2EIClientProviderTest.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/provider/E2EIClientProviderArrangement.kt diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/E2EIClientProvider.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/E2EIClientProvider.kt index 2be10426cfa..92c17cc4cb7 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/E2EIClientProvider.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/E2EIClientProvider.kt @@ -30,21 +30,22 @@ import com.wire.kalium.logic.feature.CurrentClientIdProvider import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.kaliumLogger import com.wire.kalium.util.KaliumDispatcher import com.wire.kalium.util.KaliumDispatcherImpl import kotlinx.coroutines.withContext -interface E2EClientProvider { +interface E2EIClientProvider { suspend fun getE2EIClient(clientId: ClientId? = null): Either } -internal class E2EIClientProviderImpl( +internal class EI2EIClientProviderImpl( private val userId: UserId, private val currentClientIdProvider: CurrentClientIdProvider, private val mlsClientProvider: MLSClientProvider, private val userRepository: UserRepository, private val dispatchers: KaliumDispatcher = KaliumDispatcherImpl -) : E2EClientProvider { +) : E2EIClientProvider { private var e2EIClient: E2EIClient? = null @@ -62,11 +63,21 @@ internal class E2EIClientProviderImpl( } ?: run { getSelfUserInfo().flatMap { selfUser -> mlsClientProvider.getMLSClient(currentClientId).flatMap { - val newE2EIClient = it.newAcmeEnrollment( - e2eiClientId, - selfUser.first, - selfUser.second - ) + val newE2EIClient = if (it.isE2EIEnabled()) { + kaliumLogger.e("initial E2EI client for MLS client without e2ei") + it.e2eiNewRotateEnrollment( + e2eiClientId, + selfUser.first, + selfUser.second + ) + } else { + kaliumLogger.e("initial E2EI client for mls client that already has e2ei enabled") + it.e2eiNewActivationEnrollment( + e2eiClientId, + selfUser.first, + selfUser.second + ) + } e2EIClient = newE2EIClient Either.Right(newE2EIClient) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepository.kt index 8498c7c96ee..fc01a914709 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepository.kt @@ -24,7 +24,7 @@ import com.wire.kalium.cryptography.AcmeDirectory import com.wire.kalium.cryptography.NewAcmeAuthz import com.wire.kalium.cryptography.NewAcmeOrder import com.wire.kalium.logic.CoreFailure -import com.wire.kalium.logic.data.client.E2EClientProvider +import com.wire.kalium.logic.data.client.E2EIClientProvider import com.wire.kalium.logic.data.client.MLSClientProvider import com.wire.kalium.logic.feature.CurrentClientIdProvider import com.wire.kalium.logic.functional.Either @@ -65,7 +65,7 @@ interface E2EIRepository { class E2EIRepositoryImpl( private val e2EIApi: E2EIApi, private val acmeApi: ACMEApi, - private val e2EClientProvider: E2EClientProvider, + private val e2EIClientProvider: E2EIClientProvider, private val mlsClientProvider: MLSClientProvider, private val currentClientIdProvider: CurrentClientIdProvider ) : E2EIRepository { @@ -73,7 +73,7 @@ class E2EIRepositoryImpl( override suspend fun loadACMEDirectories(): Either = wrapApiRequest { acmeApi.getACMEDirectories() }.flatMap { directories -> - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> wrapE2EIRequest { e2eiClient.directoryResponse(Json.encodeToString(directories).encodeToByteArray()) } @@ -85,7 +85,7 @@ class E2EIRepositoryImpl( } override suspend fun createNewAccount(prevNonce: String, createAccountEndpoint: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val accountRequest = e2eiClient.getNewAccountRequest(prevNonce) wrapApiRequest { acmeApi.sendACMERequest(createAccountEndpoint, accountRequest) @@ -96,7 +96,7 @@ class E2EIRepositoryImpl( } override suspend fun createNewOrder(prevNonce: String, createOrderEndpoint: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val orderRequest = e2eiClient.getNewOrderRequest(prevNonce) wrapApiRequest { acmeApi.sendACMERequest(createOrderEndpoint, orderRequest) @@ -107,7 +107,7 @@ class E2EIRepositoryImpl( } override suspend fun createAuthz(prevNonce: String, authzEndpoint: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val authzRequest = e2eiClient.getNewAuthzRequest(authzEndpoint, prevNonce) wrapApiRequest { acmeApi.sendACMERequest(authzEndpoint, authzRequest) @@ -129,12 +129,12 @@ class E2EIRepositoryImpl( } } - override suspend fun getDPoPToken(wireNonce: String) = e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + override suspend fun getDPoPToken(wireNonce: String) = e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> Either.Right(e2eiClient.createDpopToken(wireNonce)) } override suspend fun validateDPoPChallenge(accessToken: String, prevNonce: String, acmeChallenge: AcmeChallenge) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val challengeRequest = e2eiClient.getNewDpopChallengeRequest(accessToken, prevNonce) wrapApiRequest { acmeApi.sendChallengeRequest(acmeChallenge.url, challengeRequest) @@ -145,7 +145,7 @@ class E2EIRepositoryImpl( } override suspend fun validateOIDCChallenge(idToken: String, prevNonce: String, acmeChallenge: AcmeChallenge) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val challengeRequest = e2eiClient.getNewOidcChallengeRequest(idToken, prevNonce) wrapApiRequest { acmeApi.sendChallengeRequest(acmeChallenge.url, challengeRequest) @@ -156,13 +156,13 @@ class E2EIRepositoryImpl( } override suspend fun validateChallenge(challengeResponse: ChallengeResponse) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> e2eiClient.setChallengeResponse(Json.encodeToString(challengeResponse).encodeToByteArray()) Either.Right(Unit) } override suspend fun checkOrderRequest(location: String, prevNonce: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val checkOrderRequest = e2eiClient.checkOrderRequest(location, prevNonce) wrapApiRequest { acmeApi.sendACMERequest(location, checkOrderRequest) @@ -173,7 +173,7 @@ class E2EIRepositoryImpl( } override suspend fun finalize(location: String, prevNonce: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val finalizeRequest = e2eiClient.finalizeRequest(prevNonce) wrapApiRequest { acmeApi.sendACMERequest(location, finalizeRequest) @@ -184,7 +184,7 @@ class E2EIRepositoryImpl( } override suspend fun certificateRequest(location: String, prevNonce: String) = - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> val certificateRequest = e2eiClient.certificateRequest(prevNonce) wrapApiRequest { acmeApi.sendACMERequest(location, certificateRequest) @@ -192,7 +192,7 @@ class E2EIRepositoryImpl( } override suspend fun initMLSClientWithCertificate(certificateChain: String) { - e2EClientProvider.getE2EIClient().flatMap { e2eiClient -> + e2EIClientProvider.getE2EIClient().flatMap { e2eiClient -> mlsClientProvider.getMLSClient().map { it.e2eiMlsInitOnly(e2eiClient, certificateChain) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt index 3d366244990..f1b6eb31fc6 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt @@ -77,7 +77,7 @@ import kotlinx.serialization.json.Json import kotlin.time.Duration.Companion.minutes @Suppress("TooManyFunctions") -internal interface UserRepository { +interface UserRepository { suspend fun fetchSelfUser(): Either /** diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index db2cdd1dde4..004a920be70 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -44,8 +44,8 @@ import com.wire.kalium.logic.data.call.VideoStateCheckerImpl import com.wire.kalium.logic.data.call.mapper.CallMapper import com.wire.kalium.logic.data.client.ClientDataSource import com.wire.kalium.logic.data.client.ClientRepository -import com.wire.kalium.logic.data.client.E2EClientProvider -import com.wire.kalium.logic.data.client.E2EIClientProviderImpl +import com.wire.kalium.logic.data.client.E2EIClientProvider +import com.wire.kalium.logic.data.client.EI2EIClientProviderImpl import com.wire.kalium.logic.data.client.MLSClientProvider import com.wire.kalium.logic.data.client.MLSClientProviderImpl import com.wire.kalium.logic.data.client.remote.ClientRemoteDataSource @@ -595,8 +595,8 @@ class UserSessionScope internal constructor( clientIdProvider ) - private val e2EIClientProvider: E2EClientProvider by lazy { - E2EIClientProviderImpl( + private val e2EIClientProvider: E2EIClientProvider by lazy { + EI2EIClientProviderImpl( userId = userId, currentClientIdProvider = clientIdProvider, mlsClientProvider = mlsClientProvider, diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/client/E2EIClientProviderTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/client/E2EIClientProviderTest.kt new file mode 100644 index 00000000000..5a593830c3f --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/client/E2EIClientProviderTest.kt @@ -0,0 +1,142 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.client + +import com.wire.kalium.logic.data.client.E2EIClientProvider +import com.wire.kalium.logic.data.client.EI2EIClientProviderImpl +import com.wire.kalium.logic.framework.TestClient +import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.util.arrangement.provider.E2EIClientProviderArrangement +import com.wire.kalium.logic.util.arrangement.provider.E2EIClientProviderArrangementImpl +import com.wire.kalium.logic.util.shouldFail +import com.wire.kalium.logic.util.shouldSucceed +import io.mockative.any +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class E2EIClientProviderTest { + @Test + fun givenMLSClientWithoutE2EI_whenGettingE2EIClient_callsNewRotateEnrollment() = runTest{ + val (arrangement, e2eiClientProvider) = Arrangement() + .arrange { + withGetMLSClientSuccessful() + withE2EINewActivationEnrollmentSuccessful() + withSelfUser(TestUser.SELF) + withE2EIEnabled(false) + } + + e2eiClientProvider.getE2EIClient(TestClient.CLIENT_ID).shouldSucceed() + + verify(arrangement.userRepository) + .suspendFunction(arrangement.userRepository::getSelfUser) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewActivationEnrollment) + .with(any(), any(), any()) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewRotateEnrollment) + .with(any(),any(),any()) + .wasNotInvoked() + } + + @Test + fun givenMLSClientWithE2EI_whenGettingE2EIClient_callsNewActivationEnrollment() = runTest{ + val (arrangement, e2eiClientProvider) = Arrangement() + .arrange { + withGetMLSClientSuccessful() + withE2EINewRotationEnrollmentSuccessful() + withSelfUser(TestUser.SELF) + withE2EIEnabled(true) + } + + e2eiClientProvider.getE2EIClient(TestClient.CLIENT_ID).shouldSucceed() + + verify(arrangement.userRepository) + .suspendFunction(arrangement.userRepository::getSelfUser) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClientProvider) + .suspendFunction(arrangement.mlsClientProvider::getMLSClient) + .with(any()) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewRotateEnrollment) + .with(any(),any(),any()) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewActivationEnrollment) + .with(any(), any(), any()) + .wasNotInvoked() + } + + @Test + fun givenSelfUserNotFound_whenGettingE2EIClient_ReturnsError() = runTest{ + val (arrangement, e2eiClientProvider) = Arrangement() + .arrange { + withGetMLSClientSuccessful() + withE2EINewRotationEnrollmentSuccessful() + withSelfUser(null) + withE2EIEnabled(true) + } + + e2eiClientProvider.getE2EIClient(TestClient.CLIENT_ID).shouldFail() + + verify(arrangement.userRepository) + .suspendFunction(arrangement.userRepository::getSelfUser) + .wasInvoked(exactly = once) + + verify(arrangement.mlsClientProvider) + .suspendFunction(arrangement.mlsClientProvider::getMLSClient) + .with(any()) + .wasNotInvoked() + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewRotateEnrollment) + .with(any(),any(),any()) + .wasNotInvoked() + + verify(arrangement.mlsClient) + .suspendFunction(arrangement.mlsClient::e2eiNewActivationEnrollment) + .with(any(), any(), any()) + .wasNotInvoked() + } + + private class Arrangement : + E2EIClientProviderArrangement by E2EIClientProviderArrangementImpl() { + private lateinit var e2eiClientProvider: E2EIClientProvider + + fun arrange(block: Arrangement.() -> Unit): Pair { + apply(block) + e2eiClientProvider = EI2EIClientProviderImpl( + TestUser.USER_ID, + currentClientIdProvider, + mlsClientProvider, + userRepository + ) + + return this to e2eiClientProvider + } + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepositoryTest.kt index da3d3b5cb46..e4ba7558118 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepositoryTest.kt @@ -18,23 +18,18 @@ package com.wire.kalium.logic.data.e2ei import com.wire.kalium.cryptography.* -import com.wire.kalium.cryptography.utils.generateRandomAES256Key -import com.wire.kalium.logic.data.client.E2EClientProvider +import com.wire.kalium.logic.data.client.E2EIClientProvider import com.wire.kalium.logic.data.client.MLSClientProvider import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.ACME_CHALLENGE -import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.ACME_DIRECTORIES -import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.ACME_DIRECTORIES_RESPONSE import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.RANDOM_ACCESS_TOKEN import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.RANDOM_ID_TOKEN import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.RANDOM_NONCE import com.wire.kalium.logic.data.e2ei.E2EIRepositoryTest.Arrangement.Companion.RANDOM_URL -import com.wire.kalium.logic.data.featureConfig.* import com.wire.kalium.logic.feature.CurrentClientIdProvider import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.util.shouldFail import com.wire.kalium.logic.util.shouldSucceed import com.wire.kalium.network.api.base.authenticated.e2ei.E2EIApi -import com.wire.kalium.network.api.base.authenticated.featureConfigs.* import com.wire.kalium.network.api.base.model.ErrorResponse import com.wire.kalium.network.api.base.unbound.acme.ACMEApi import com.wire.kalium.network.api.base.unbound.acme.ACMEResponse @@ -42,14 +37,9 @@ import com.wire.kalium.network.api.base.unbound.acme.AcmeDirectoriesResponse import com.wire.kalium.network.api.base.unbound.acme.ChallengeResponse import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.utils.NetworkResponse -import com.wire.kalium.util.serialization.toJsonElement -import io.ktor.util.* import io.mockative.* import kotlinx.coroutines.test.runTest -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import kotlin.test.Test -import kotlin.test.assertEquals class E2EIRepositoryTest { @Test @@ -768,7 +758,7 @@ class E2EIRepositoryTest { val acmeApi: ACMEApi = mock(classOf()) @Mock - val e2eiClientProvider: E2EClientProvider = mock(classOf()) + val e2eiClientProvider: E2EIClientProvider = mock(classOf()) @Mock val e2eiClient = mock(classOf()) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestUser.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestUser.kt index 31ad4feb87d..ed6f09619e9 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestUser.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestUser.kt @@ -44,7 +44,7 @@ import com.wire.kalium.persistence.dao.UserEntity import com.wire.kalium.persistence.dao.UserTypeEntity object TestUser { - private const val value = "value" + private const val value = "41d2b365-f4a9-4ba1-bddf-5afb8aca6786" private const val domain = "domain" val USER_ID = UserId(value, domain) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/provider/E2EIClientProviderArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/provider/E2EIClientProviderArrangement.kt new file mode 100644 index 00000000000..4e957dc223e --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/provider/E2EIClientProviderArrangement.kt @@ -0,0 +1,102 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.util.arrangement.provider + +import com.wire.kalium.cryptography.E2EIClient +import com.wire.kalium.cryptography.E2EIQualifiedClientId +import com.wire.kalium.cryptography.MLSClient +import com.wire.kalium.logic.data.client.MLSClientProvider +import com.wire.kalium.logic.data.user.SelfUser +import com.wire.kalium.logic.data.user.UserRepository +import com.wire.kalium.logic.feature.CurrentClientIdProvider +import com.wire.kalium.logic.functional.Either +import io.mockative.Mock +import io.mockative.anything +import io.mockative.classOf +import io.mockative.given +import io.mockative.mock + +interface E2EIClientProviderArrangement { + @Mock + val mlsClientProvider: MLSClientProvider + + @Mock + val mlsClient: MLSClient + + @Mock + val e2eiClient: E2EIClient + + @Mock + val userRepository: UserRepository + + @Mock + val currentClientIdProvider: CurrentClientIdProvider + + fun withGetMLSClientSuccessful() + + fun withE2EINewActivationEnrollmentSuccessful() + + fun withE2EINewRotationEnrollmentSuccessful() + + + fun withE2EIEnabled(isEnabled: Boolean) + + fun withSelfUser(selfUser: SelfUser?) +} + +class E2EIClientProviderArrangementImpl : E2EIClientProviderArrangement { + override val mlsClientProvider: MLSClientProvider = mock(classOf()) + override val mlsClient: MLSClient = mock(classOf()) + override val e2eiClient: E2EIClient = mock(classOf()) + override val userRepository: UserRepository = mock(classOf()) + override val currentClientIdProvider = mock(classOf()) + override fun withGetMLSClientSuccessful() { + given(mlsClientProvider) + .suspendFunction(mlsClientProvider::getMLSClient) + .whenInvokedWith(anything()) + .then { Either.Right(mlsClient) } + } + + override fun withE2EINewActivationEnrollmentSuccessful() { + given(mlsClient) + .suspendFunction(mlsClient::e2eiNewActivationEnrollment) + .whenInvokedWith(anything(), anything(), anything()) + .thenReturn(e2eiClient) + } + override fun withE2EINewRotationEnrollmentSuccessful() { + given(mlsClient) + .suspendFunction(mlsClient::e2eiNewRotateEnrollment) + .whenInvokedWith(anything(), anything(), anything()) + .thenReturn(e2eiClient) + } + + override fun withE2EIEnabled(isEnabled: Boolean) { + given(mlsClient) + .suspendFunction(mlsClient::isE2EIEnabled) + .whenInvoked() + .thenReturn(isEnabled) + } + + override fun withSelfUser(selfUser: SelfUser?) { + given(userRepository) + .suspendFunction(userRepository::getSelfUser) + .whenInvoked() + .thenReturn(selfUser) + } + +}