Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading