Skip to content

Commit

Permalink
feat: check Certificate Revocation List for current client (WPB-3243) (
Browse files Browse the repository at this point in the history
…#2570)

* feat: check Certificate Revocation List for current client (WPB-3243) (#2498)

* feat: check CRL for current client

* chore: resolve conflicts

* chore: unused imports

* chore: unused imports

* fix: url format

* chore: cleanup

* chore: cleanup

* chore: check if E2ei is enabled before running CheckRevocationListForCurrentClientUseCase

* chore: cleanup

* chore: empty commit

---------

Co-authored-by: Oussama Hassine <[email protected]>
  • Loading branch information
github-actions[bot] and ohassine authored Mar 1, 2024
1 parent 5c7e158 commit 78f34ed
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ interface UserConfigRepository {
suspend fun setLegalHoldChangeNotified(isNotified: Boolean): Either<StorageFailure, Unit>
suspend fun observeLegalHoldChangeNotified(): Flow<Either<StorageFailure, Boolean>>
suspend fun setShouldUpdateClientLegalHoldCapability(shouldUpdate: Boolean): Either<StorageFailure, Unit>
suspend fun shouldCheckCrlForCurrentClient(): Boolean
suspend fun setShouldCheckCrlForCurrentClient(shouldCheck: Boolean): Either<StorageFailure, Unit>
suspend fun shouldUpdateClientLegalHoldCapability(): Boolean
suspend fun setCRLExpirationTime(url: String, timestamp: ULong)
suspend fun getCRLExpirationTime(url: String): ULong?
Expand Down Expand Up @@ -450,6 +452,11 @@ internal class UserConfigDataSource internal constructor(
override suspend fun shouldUpdateClientLegalHoldCapability(): Boolean =
userConfigDAO.shouldUpdateClientLegalHoldCapability()

override suspend fun shouldCheckCrlForCurrentClient() = userConfigDAO.shouldCheckCrlForCurrentClient()

override suspend fun setShouldCheckCrlForCurrentClient(shouldCheck: Boolean): Either<StorageFailure, Unit> =
wrapStorageRequest { userConfigDAO.setShouldCheckCrlForCurrentClient(shouldCheck) }

override suspend fun setCRLExpirationTime(url: String, timestamp: ULong) {
userConfigDAO.setCRLExpirationTime(url, timestamp)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import com.wire.kalium.persistence.config.CRLUrlExpirationList
import com.wire.kalium.persistence.config.CRLWithExpiration
import com.wire.kalium.persistence.dao.MetadataDAO
import io.ktor.http.Url
import io.ktor.http.protocolWithAuthority
import io.ktor.http.authority

interface CertificateRevocationListRepository {

Expand All @@ -55,29 +55,29 @@ internal class CertificateRevocationListRepositoryDataSource(

override suspend fun addOrUpdateCRL(url: String, timestamp: ULong) {
val newCRLUrls = metadataDAO.getSerializable(CRL_LIST_KEY, CRLUrlExpirationList.serializer())
?.let { crlExpirationList ->
val crlWithExpiration = crlExpirationList.cRLWithExpirationList.find {
it.url == url
}
crlWithExpiration?.let { item ->
crlExpirationList.cRLWithExpirationList.map { current ->
if (current.url == url) {
return@map item.copy(expiration = timestamp)
} else {
return@map current
?.let { crlExpirationList ->
val crlWithExpiration = crlExpirationList.cRLWithExpirationList.find {
it.url == url
}
crlWithExpiration?.let { item ->
crlExpirationList.cRLWithExpirationList.map { current ->
if (current.url == url) {
return@map item.copy(expiration = timestamp)
} else {
return@map current
}
}
} ?: run {
// add new CRL
crlExpirationList.cRLWithExpirationList.plus(
CRLWithExpiration(url, timestamp)
)
}
} ?: run {
// add new CRL
crlExpirationList.cRLWithExpirationList.plus(
CRLWithExpiration(url, timestamp)
)
}

} ?: run {
// add new CRL
listOf(CRLWithExpiration(url, timestamp))
}
} ?: run {
// add new CRL
listOf(CRLWithExpiration(url, timestamp))
}
metadataDAO.putSerializable(
CRL_LIST_KEY,
CRLUrlExpirationList(newCRLUrls),
Expand All @@ -90,7 +90,7 @@ internal class CertificateRevocationListRepositoryDataSource(
.flatMap {
if (!it.isRequired) E2EIFailure.Disabled.left()
else if (it.discoverUrl == null) E2EIFailure.MissingDiscoveryUrl.left()
else Url(it.discoverUrl).protocolWithAuthority.right()
else Url(it.discoverUrl).authority.right()
}

override suspend fun getClientDomainCRL(url: String): Either<CoreFailure, ByteArray> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.getOrFail
import com.wire.kalium.logic.functional.left
import com.wire.kalium.logic.functional.map
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.logic.functional.right
import com.wire.kalium.logic.wrapApiRequest
Expand All @@ -46,7 +45,6 @@ import com.wire.kalium.network.api.base.unbound.acme.ACMEApi
import com.wire.kalium.network.api.base.unbound.acme.ACMEResponse
import com.wire.kalium.network.api.base.unbound.acme.ChallengeResponse
import io.ktor.http.Url
import io.ktor.http.protocolWithAuthority
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

Expand Down Expand Up @@ -90,8 +88,6 @@ interface E2EIRepository {
suspend fun getOAuthRefreshToken(): Either<E2EIFailure, String?>
suspend fun nukeE2EIClient()
suspend fun fetchFederationCertificates(): Either<E2EIFailure, Unit>
suspend fun getCurrentClientCrlUrl(): Either<E2EIFailure, String>
suspend fun getClientDomainCRL(url: String): Either<E2EIFailure, ByteArray>
fun discoveryUrl(): Either<E2EIFailure, Url>
}

Expand Down Expand Up @@ -376,12 +372,4 @@ class E2EIRepositoryImpl(
override suspend fun nukeE2EIClient() {
e2EIClientProvider.nuke()
}

override suspend fun getCurrentClientCrlUrl(): Either<E2EIFailure, String> =
discoveryUrl().map { it.protocolWithAuthority }

override suspend fun getClientDomainCRL(url: String): Either<E2EIFailure, ByteArray> =
wrapApiRequest {
acmeApi.getClientDomainCRL(url)
}.fold({ E2EIFailure.CRL(it).left() }, { it.right() })
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ import com.wire.kalium.logic.feature.e2ei.ACMECertificatesSyncWorker
import com.wire.kalium.logic.feature.e2ei.ACMECertificatesSyncWorkerImpl
import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorker
import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorkerImpl
import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListForCurrentClientUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListForCurrentClientUseCaseImpl
import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCaseImpl
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIUseCase
Expand Down Expand Up @@ -643,6 +645,13 @@ class UserSessionScope internal constructor(
mLSConversationsVerificationStatusesHandler = mlsConversationsVerificationStatusesHandler,
isE2EIEnabledUseCase = isE2EIEnabled
)
private val checkRevocationListForCurrentClient: CheckRevocationListForCurrentClientUseCase
get() = CheckRevocationListForCurrentClientUseCaseImpl(
checkRevocationList = checkRevocationList,
certificateRevocationListRepository = certificateRevocationListRepository,
userConfigRepository = userConfigRepository,
isE2EIEnabledUseCase = isE2EIEnabled
)

private val mlsConversationRepository: MLSConversationRepository
get() = MLSConversationDataSource(
Expand Down Expand Up @@ -2035,6 +2044,10 @@ class UserSessionScope internal constructor(
certificateRevocationListCheckWorker.execute()
}

launch {
checkRevocationListForCurrentClient.invoke()
}

launch {
avsSyncStateReporter.execute()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Wire
* Copyright (C) 2024 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.feature.e2ei.usecase

import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository
import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorker
import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase
import com.wire.kalium.logic.functional.onFailure
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.logic.kaliumLogger

/**
* Use case to check the revocation list for the current client and store it in the DB.
* This will run once as [CertificateRevocationListCheckWorker] is constantly checking the CRLs that are stored in DB.
*/
interface CheckRevocationListForCurrentClientUseCase {
suspend operator fun invoke()
}

internal class CheckRevocationListForCurrentClientUseCaseImpl(
private val checkRevocationList: CheckRevocationListUseCase,
private val certificateRevocationListRepository: CertificateRevocationListRepository,
private val userConfigRepository: UserConfigRepository,
private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase
) : CheckRevocationListForCurrentClientUseCase {
override suspend fun invoke() {
if (isE2EIEnabledUseCase() && userConfigRepository.shouldCheckCrlForCurrentClient()) {
certificateRevocationListRepository.getCurrentClientCrlUrl().onSuccess { url ->
kaliumLogger.i("Checking CRL for current client..")
checkRevocationList(url)
.onSuccess { expiration ->
kaliumLogger.i("Successfully checked CRL for current client..")
expiration?.let {
certificateRevocationListRepository.addOrUpdateCRL(url, it)
userConfigRepository.setShouldCheckCrlForCurrentClient(false)
}
}
.onFailure {
kaliumLogger.i("Failed to check CRL for current client..")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.map
import com.wire.kalium.logic.kaliumLogger

/**
* Use case to check if the CRL is expired and if so, register CRL and update conversation statuses if there is a change.
Expand All @@ -42,11 +43,14 @@ internal class CheckRevocationListUseCaseImpl(
private val mLSConversationsVerificationStatusesHandler: MLSConversationsVerificationStatusesHandler,
private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase
) : CheckRevocationListUseCase {
private val logger = kaliumLogger.withTextTag("CheckRevocationListUseCase")
override suspend fun invoke(url: String): Either<CoreFailure, ULong?> {
return if (isE2EIEnabledUseCase()) {
logger.i("getting client crl..")
certificateRevocationListRepository.getClientDomainCRL(url).flatMap {
currentClientIdProvider().flatMap { clientId ->
mlsClientProvider.getCoreCrypto(clientId).map { coreCrypto ->
logger.i("registering crl..")
coreCrypto.registerCrl(url, it).run {
if (dirty) {
mLSConversationsVerificationStatusesHandler()
Expand Down
Loading

0 comments on commit 78f34ed

Please sign in to comment.