Skip to content

Commit

Permalink
feat(e2ei): migrate mls-conversation after enrolling E2EI certificate…
Browse files Browse the repository at this point in the history
… (WPB-2928) (#2164)

* feat(e2ei): rotate mls-conversation after enrolling E2EI certificate

* rename functions

* fix detekt

* add tests and fix missing imorts

* cover rotateKeysAndMigrationConversation with test in MLSConversationRepository

* fix detekt
  • Loading branch information
mchenani authored Oct 25, 2023
1 parent 898cc8a commit b8dfcb5
Show file tree
Hide file tree
Showing 12 changed files with 509 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ class MLSClientImpl(
TODO("Not yet implemented")
}

override suspend fun e2eiRotateAll(enrollment: E2EIClient, certificateChain: CertificateChain, newMLSKeyPackageCount: UInt) {
override suspend fun e2eiRotateAll(
enrollment: E2EIClient,
certificateChain: CertificateChain,
newMLSKeyPackageCount: UInt
): RotateBundle {
TODO("Not yet implemented")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,13 @@ class MLSClientImpl(
enrollment: E2EIClient,
certificateChain: CertificateChain,
newMLSKeyPackageCount: UInt
) {
coreCrypto.e2eiRotateAll(
(enrollment as E2EIClientImpl).wireE2eIdentity,
certificateChain,
newMLSKeyPackageCount
): RotateBundle {
return toRotateBundle(
coreCrypto.e2eiRotateAll(
(enrollment as E2EIClientImpl).wireE2eIdentity,
certificateChain,
newMLSKeyPackageCount
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ interface MLSClient {
/**
* Generate new keypackages after E2EI certificate issued
*/
suspend fun e2eiRotateAll(enrollment: E2EIClient, certificateChain: CertificateChain, newMLSKeyPackageCount: UInt)
suspend fun e2eiRotateAll(enrollment: E2EIClient, certificateChain: CertificateChain, newMLSKeyPackageCount: UInt): RotateBundle

/**
* Conversation E2EI Verification Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class MLSClientImpl : MLSClient {
enrollment: E2EIClient,
certificateChain: CertificateChain,
newMLSKeyPackageCount: UInt
) {
): RotateBundle {
TODO("Not yet implemented")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.E2EIClient
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.MLSFailure
Expand All @@ -43,6 +44,8 @@ import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.flatMapLeft
import com.wire.kalium.logic.functional.flatten
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.foldToEitherWhileRight
import com.wire.kalium.logic.functional.map
import com.wire.kalium.logic.functional.onFailure
import com.wire.kalium.logic.functional.onSuccess
Expand Down Expand Up @@ -108,6 +111,11 @@ interface MLSConversationRepository {
suspend fun observeProposalTimers(): Flow<ProposalTimer>
suspend fun observeEpochChanges(): Flow<GroupID>
suspend fun getConversationVerificationStatus(groupID: GroupID): Either<CoreFailure, Conversation.VerificationStatus>
suspend fun rotateKeysAndMigrateConversations(
clientId: ClientId,
e2eiClient: E2EIClient,
certificateChain: String
): Either<CoreFailure, Unit>
}

private enum class CommitStrategy {
Expand Down Expand Up @@ -514,6 +522,33 @@ internal class MLSConversationDataSource(
wrapMLSRequest { mlsClient.isGroupVerified(idMapper.toCryptoModel(groupID)) }
}.map { it.toModel() }

override suspend fun rotateKeysAndMigrateConversations(
clientId: ClientId,
e2eiClient: E2EIClient,
certificateChain: String
) = mlsClientProvider.getMLSClient().flatMap { mlsClient ->
wrapMLSRequest {
mlsClient.e2eiRotateAll(e2eiClient, certificateChain, 10U)
}.map { rotateBundle ->
// todo: make below API calls atomic when the backend does it in one request
// todo: store keypackages to drop, later drop them again
kaliumLogger.w("drop old key packages after conversations migration")
keyPackageRepository.deleteKeyPackages(clientId, rotateBundle.keyPackageRefsToRemove).flatMapLeft {
return Either.Left(it)
}

kaliumLogger.w("upload new key packages including x509 certificate")
keyPackageRepository.uploadKeyPackages(clientId, rotateBundle.newKeyPackages).flatMapLeft {
return Either.Left(it)
}

kaliumLogger.w("send migration commits after key rotations")
rotateBundle.commits.map {
sendCommitBundle(GroupID(it.key), it.value)
}.foldToEitherWhileRight(Unit) { value, _ -> value }.fold({ return Either.Left(it) }, { })
}
}

private suspend fun retryOnCommitFailure(
groupID: GroupID,
retryOnClientMismatch: Boolean = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.wire.kalium.cryptography.NewAcmeOrder
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.client.E2EIClientProvider
import com.wire.kalium.logic.data.client.MLSClientProvider
import com.wire.kalium.logic.data.conversation.MLSConversationRepository
import com.wire.kalium.logic.feature.CurrentClientIdProvider
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
Expand Down Expand Up @@ -59,15 +60,16 @@ interface E2EIRepository {
suspend fun finalize(location: String, prevNonce: String): Either<CoreFailure, Pair<ACMEResponse, String>>
suspend fun checkOrderRequest(location: String, prevNonce: String): Either<CoreFailure, Pair<ACMEResponse, String>>
suspend fun certificateRequest(location: String, prevNonce: String): Either<CoreFailure, ACMEResponse>
suspend fun initMLSClientWithCertificate(certificateChain: String)
suspend fun rotateKeysAndMigrateConversations(certificateChain: String): Either<CoreFailure, Unit>
}

class E2EIRepositoryImpl(
private val e2EIApi: E2EIApi,
private val acmeApi: ACMEApi,
private val e2EIClientProvider: E2EIClientProvider,
private val mlsClientProvider: MLSClientProvider,
private val currentClientIdProvider: CurrentClientIdProvider
private val currentClientIdProvider: CurrentClientIdProvider,
private val mlsConversationRepository: MLSConversationRepository
) : E2EIRepository {

override suspend fun loadACMEDirectories(): Either<CoreFailure, AcmeDirectory> = wrapApiRequest {
Expand Down Expand Up @@ -191,11 +193,11 @@ class E2EIRepositoryImpl(
}.map { it }
}

override suspend fun initMLSClientWithCertificate(certificateChain: String) {
override suspend fun rotateKeysAndMigrateConversations(certificateChain: String) =
e2EIClientProvider.getE2EIClient().flatMap { e2eiClient ->
mlsClientProvider.getMLSClient().map {
it.e2eiMlsInitOnly(e2eiClient, certificateChain)
currentClientIdProvider().flatMap { clientId ->
mlsConversationRepository.rotateKeysAndMigrateConversations(clientId, e2eiClient, certificateChain)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ interface KeyPackageRepository {

suspend fun uploadNewKeyPackages(clientId: ClientId, amount: Int = 100): Either<CoreFailure, Unit>

suspend fun uploadKeyPackages(clientId: ClientId, keyPackages: List<ByteArray>): Either<CoreFailure, Unit>

suspend fun deleteKeyPackages(clientId: ClientId, keyPackages: List<ByteArray>): Either<CoreFailure, Unit>

suspend fun getAvailableKeyPackageCount(clientId: ClientId): Either<NetworkFailure, KeyPackageCountDTO>

suspend fun validKeyPackageCount(clientId: ClientId): Either<CoreFailure, Int>
Expand Down Expand Up @@ -87,6 +91,22 @@ class KeyPackageDataSource(
}
}

override suspend fun uploadKeyPackages(
clientId: ClientId,
keyPackages: List<ByteArray>
): Either<CoreFailure, Unit> =
wrapApiRequest {
keyPackageApi.uploadKeyPackages(clientId.value, keyPackages.map { it.encodeBase64() })
}

override suspend fun deleteKeyPackages(
clientId: ClientId,
keyPackages: List<ByteArray>
): Either<CoreFailure, Unit> =
wrapApiRequest {
keyPackageApi.deleteKeyPackages(clientId.value, keyPackages.map { it.encodeBase64() })
}

override suspend fun validKeyPackageCount(clientId: ClientId): Either<CoreFailure, Int> =
mlsClientProvider.getMLSClient(clientId).flatMap { mlsClient ->
wrapMLSRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ class UserSessionScope internal constructor(
globalScope.unboundNetworkContainer.acmeApi,
e2EIClientProvider,
mlsClientProvider,
clientIdProvider
clientIdProvider,
mlsConversationRepository
)

private val e2EIClientProvider: E2EIClientProvider by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.wire.kalium.logic.E2EIFailure
import com.wire.kalium.logic.data.e2ei.E2EIRepository
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.onFailure

/**
* Issue an E2EI certificate and re-initiate the MLSClient
Expand Down Expand Up @@ -111,8 +112,10 @@ class EnrollE2EIUseCaseImpl internal constructor(
return E2EIEnrollmentResult.Failed(E2EIEnrollmentResult.E2EIStep.Certificate, it).toEitherLeft()
}, { it })

// TODO(fix): init after fixing the MLS client initialization mechanism
// TODO(revert): e2EIRepository.initMLSClientWithCertificate(certificateRequest.response.decodeToString())
e2EIRepository.rotateKeysAndMigrateConversations(certificateRequest.response.decodeToString()).onFailure {
return E2EIEnrollmentResult.Failed(E2EIEnrollmentResult.E2EIStep.ConversationMigration, it).toEitherLeft()
}

return Either.Right(E2EIEnrollmentResult.Success(certificateRequest.response.decodeToString()))
}

Expand All @@ -132,6 +135,7 @@ sealed interface E2EIEnrollmentResult {
OIDCChallenge,
CheckOrderRequest,
FinalizeRequest,
ConversationMigration,
Certificate
}

Expand Down
Loading

0 comments on commit b8dfcb5

Please sign in to comment.