Browse Source

Move getAdditionalCertificates function to a dedicated class (no change in the implementation).

pull/2430/head
Benoit Marty 7 months ago
parent
commit
fd555b1070
  1. 57
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt
  2. 6
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
  3. 77
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/certificates/DefaultUserCertificatesProvider.kt
  4. 21
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/certificates/UserCertificatesProvider.kt
  5. 3
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
  6. 23
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/NoOpUserCertificatesProvider.kt

57
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
@ -27,9 +28,7 @@ import kotlinx.coroutines.withContext @@ -27,9 +28,7 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.ClientBuilder
import org.matrix.rustcomponents.sdk.Session
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
import java.io.File
import java.security.KeyStore
import javax.inject.Inject
class RustMatrixClientFactory @Inject constructor(
@ -39,6 +38,7 @@ class RustMatrixClientFactory @Inject constructor( @@ -39,6 +38,7 @@ class RustMatrixClientFactory @Inject constructor(
private val coroutineDispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore,
private val userAgentProvider: UserAgentProvider,
private val userCertificatesProvider: UserCertificatesProvider,
private val clock: SystemClock,
) {
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
@ -48,7 +48,7 @@ class RustMatrixClientFactory @Inject constructor( @@ -48,7 +48,7 @@ class RustMatrixClientFactory @Inject constructor(
.username(sessionData.userId)
.passphrase(sessionData.passphrase)
.userAgent(userAgentProvider.provide())
.addRootCertificates(getAdditionalCertificates())
.addRootCertificates(userCertificatesProvider.provides())
// FIXME Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376
.serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"))
.use { it.build() }
@ -71,57 +71,6 @@ class RustMatrixClientFactory @Inject constructor( @@ -71,57 +71,6 @@ class RustMatrixClientFactory @Inject constructor(
}
}
/**
* Get additional user-installed certificates from the `AndroidCAStore` `Keystore`.
*
* The Rust HTTP client doesn't include user-installed certificates in its internal certificate
* store. This means that whatever the user installs will be ignored.
*
* While most users don't need user-installed certificates some special deployments or debugging
* setups using a proxy might want to use them.
*
* @return A list of byte arrays where each byte array is a single user-installed certificate
* in encoded form.
*/
fun getAdditionalCertificates(): List<ByteArray> {
val certs = mutableListOf<ByteArray>()
// At least for API 34 the `AndroidCAStore` `Keystore` type contained user certificates as well.
// I have not found this to be documented anywhere.
val keyStore: KeyStore = KeyStore.getInstance("AndroidCAStore").apply {
load(null)
}
val aliases = keyStore.aliases()
while (aliases.hasMoreElements()) {
val alias = aliases.nextElement()
val entry = keyStore.getEntry(alias, null)
if (entry is KeyStore.TrustedCertificateEntry) {
// The certificate alias always contains the prefix `system` or
// `user` and the MD5 subject hash separated by a colon.
//
// The subject hash can be calculated using openssl as such:
// openssl x509 -subject_hash_old -noout -in mycert.cer
//
// Again, I have not found this to be documented somewhere.
if (alias.startsWith("user")) {
certs.add(entry.trustedCertificate.encoded)
}
}
}
// Let's at least log the number of user-installed certificates we found,
// since the alias isn't particularly useful nor does the issuer seem to
// be easily available.
val certCount = certs.count()
Timber.i("Found $certCount additional user-provided certificates.")
return certs
}
private fun SessionData.toSession() = Session(
accessToken = accessToken,
refreshToken = refreshToken,

6
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt

@ -29,8 +29,8 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails @@ -29,8 +29,8 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.matrix.impl.getAdditionalCertificates
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
import io.element.android.libraries.matrix.impl.mapper.toSessionData
import io.element.android.libraries.network.useragent.UserAgentProvider
@ -57,17 +57,17 @@ class RustMatrixAuthenticationService @Inject constructor( @@ -57,17 +57,17 @@ class RustMatrixAuthenticationService @Inject constructor(
userAgentProvider: UserAgentProvider,
private val rustMatrixClientFactory: RustMatrixClientFactory,
private val passphraseGenerator: PassphraseGenerator,
userCertificatesProvider: UserCertificatesProvider,
private val buildMeta: BuildMeta,
) : MatrixAuthenticationService {
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
// stored in the SessionData.
private val pendingPassphrase = getDatabasePassphrase()
private val additionalCertificates = getAdditionalCertificates()
private val authService: RustAuthenticationService = RustAuthenticationService(
basePath = baseDirectory.absolutePath,
passphrase = pendingPassphrase,
userAgent = userAgentProvider.provide(),
additionalRootCertificates = additionalCertificates,
additionalRootCertificates = userCertificatesProvider.provides(),
oidcConfiguration = oidcConfiguration,
customSlidingSyncProxy = null,
sessionDelegate = null,

77
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/certificates/DefaultUserCertificatesProvider.kt

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.impl.certificates
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import timber.log.Timber
import java.security.KeyStore
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultUserCertificatesProvider @Inject constructor() : UserCertificatesProvider {
/**
* Get additional user-installed certificates from the `AndroidCAStore` `Keystore`.
*
* The Rust HTTP client doesn't include user-installed certificates in its internal certificate
* store. This means that whatever the user installs will be ignored.
*
* While most users don't need user-installed certificates some special deployments or debugging
* setups using a proxy might want to use them.
*
* @return A list of byte arrays where each byte array is a single user-installed certificate
* in encoded form.
*/
override fun provides(): List<ByteArray> {
val certs = mutableListOf<ByteArray>()
// At least for API 34 the `AndroidCAStore` `Keystore` type contained user certificates as well.
// I have not found this to be documented anywhere.
val keyStore: KeyStore = KeyStore.getInstance("AndroidCAStore").apply {
load(null)
}
val aliases = keyStore.aliases()
while (aliases.hasMoreElements()) {
val alias = aliases.nextElement()
val entry = keyStore.getEntry(alias, null)
if (entry is KeyStore.TrustedCertificateEntry) {
// The certificate alias always contains the prefix `system` or
// `user` and the MD5 subject hash separated by a colon.
//
// The subject hash can be calculated using openssl as such:
// openssl x509 -subject_hash_old -noout -in mycert.cer
//
// Again, I have not found this to be documented somewhere.
if (alias.startsWith("user")) {
certs.add(entry.trustedCertificate.encoded)
}
}
}
// Let's at least log the number of user-installed certificates we found,
// since the alias isn't particularly useful nor does the issuer seem to
// be easily available.
val certCount = certs.count()
Timber.i("Found $certCount additional user-provided certificates.")
return certs
}
}

21
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/certificates/UserCertificatesProvider.kt

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.impl.certificates
interface UserCertificatesProvider {
fun provides(): List<ByteArray>
}

3
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt

@ -42,6 +42,7 @@ class MainActivity : ComponentActivity() { @@ -42,6 +42,7 @@ class MainActivity : ComponentActivity() {
val baseDirectory = File(applicationContext.filesDir, "sessions")
val userAgentProvider = SimpleUserAgentProvider("MinimalSample")
val sessionStore = InMemorySessionStore()
val userCertificatesProvider = NoOpUserCertificatesProvider()
RustMatrixAuthenticationService(
baseDirectory = baseDirectory,
coroutineDispatchers = Singleton.coroutineDispatchers,
@ -54,10 +55,12 @@ class MainActivity : ComponentActivity() { @@ -54,10 +55,12 @@ class MainActivity : ComponentActivity() {
coroutineDispatchers = Singleton.coroutineDispatchers,
sessionStore = sessionStore,
userAgentProvider = userAgentProvider,
userCertificatesProvider = userCertificatesProvider,
clock = DefaultSystemClock(),
),
passphraseGenerator = NullPassphraseGenerator(),
buildMeta = Singleton.buildMeta,
userCertificatesProvider = userCertificatesProvider,
)
}

23
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/NoOpUserCertificatesProvider.kt

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.samples.minimal
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
class NoOpUserCertificatesProvider : UserCertificatesProvider {
override fun provides(): List<ByteArray> = emptyList()
}
Loading…
Cancel
Save