Move getAdditionalCertificates function to a dedicated class (no change in the implementation).
This commit is contained in:
parent
7efef9441b
commit
f1790c9a7a
6 changed files with 130 additions and 57 deletions
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl
|
||||||
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.di.CacheDirectory
|
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.network.useragent.UserAgentProvider
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
|
@ -27,9 +28,7 @@ import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||||
import org.matrix.rustcomponents.sdk.Session
|
import org.matrix.rustcomponents.sdk.Session
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.KeyStore
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RustMatrixClientFactory @Inject constructor(
|
class RustMatrixClientFactory @Inject constructor(
|
||||||
|
|
@ -39,6 +38,7 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
private val userAgentProvider: UserAgentProvider,
|
private val userAgentProvider: UserAgentProvider,
|
||||||
|
private val userCertificatesProvider: UserCertificatesProvider,
|
||||||
private val clock: SystemClock,
|
private val clock: SystemClock,
|
||||||
) {
|
) {
|
||||||
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
||||||
|
|
@ -48,7 +48,7 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
.username(sessionData.userId)
|
.username(sessionData.userId)
|
||||||
.passphrase(sessionData.passphrase)
|
.passphrase(sessionData.passphrase)
|
||||||
.userAgent(userAgentProvider.provide())
|
.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
|
// 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"))
|
.serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"))
|
||||||
.use { it.build() }
|
.use { it.build() }
|
||||||
|
|
@ -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(
|
private fun SessionData.toSession() = Session(
|
||||||
accessToken = accessToken,
|
accessToken = accessToken,
|
||||||
refreshToken = refreshToken,
|
refreshToken = refreshToken,
|
||||||
|
|
|
||||||
|
|
@ -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.auth.OidcDetails
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
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.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.keys.PassphraseGenerator
|
||||||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||||
|
|
@ -57,17 +57,17 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
userAgentProvider: UserAgentProvider,
|
userAgentProvider: UserAgentProvider,
|
||||||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||||
private val passphraseGenerator: PassphraseGenerator,
|
private val passphraseGenerator: PassphraseGenerator,
|
||||||
|
userCertificatesProvider: UserCertificatesProvider,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) : MatrixAuthenticationService {
|
) : MatrixAuthenticationService {
|
||||||
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
|
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
|
||||||
// stored in the SessionData.
|
// stored in the SessionData.
|
||||||
private val pendingPassphrase = getDatabasePassphrase()
|
private val pendingPassphrase = getDatabasePassphrase()
|
||||||
private val additionalCertificates = getAdditionalCertificates()
|
|
||||||
private val authService: RustAuthenticationService = RustAuthenticationService(
|
private val authService: RustAuthenticationService = RustAuthenticationService(
|
||||||
basePath = baseDirectory.absolutePath,
|
basePath = baseDirectory.absolutePath,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
userAgent = userAgentProvider.provide(),
|
userAgent = userAgentProvider.provide(),
|
||||||
additionalRootCertificates = additionalCertificates,
|
additionalRootCertificates = userCertificatesProvider.provides(),
|
||||||
oidcConfiguration = oidcConfiguration,
|
oidcConfiguration = oidcConfiguration,
|
||||||
customSlidingSyncProxy = null,
|
customSlidingSyncProxy = null,
|
||||||
sessionDelegate = null,
|
sessionDelegate = null,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ class MainActivity : ComponentActivity() {
|
||||||
val baseDirectory = File(applicationContext.filesDir, "sessions")
|
val baseDirectory = File(applicationContext.filesDir, "sessions")
|
||||||
val userAgentProvider = SimpleUserAgentProvider("MinimalSample")
|
val userAgentProvider = SimpleUserAgentProvider("MinimalSample")
|
||||||
val sessionStore = InMemorySessionStore()
|
val sessionStore = InMemorySessionStore()
|
||||||
|
val userCertificatesProvider = NoOpUserCertificatesProvider()
|
||||||
RustMatrixAuthenticationService(
|
RustMatrixAuthenticationService(
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory,
|
||||||
coroutineDispatchers = Singleton.coroutineDispatchers,
|
coroutineDispatchers = Singleton.coroutineDispatchers,
|
||||||
|
|
@ -54,10 +55,12 @@ class MainActivity : ComponentActivity() {
|
||||||
coroutineDispatchers = Singleton.coroutineDispatchers,
|
coroutineDispatchers = Singleton.coroutineDispatchers,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
userAgentProvider = userAgentProvider,
|
userAgentProvider = userAgentProvider,
|
||||||
|
userCertificatesProvider = userCertificatesProvider,
|
||||||
clock = DefaultSystemClock(),
|
clock = DefaultSystemClock(),
|
||||||
),
|
),
|
||||||
passphraseGenerator = NullPassphraseGenerator(),
|
passphraseGenerator = NullPassphraseGenerator(),
|
||||||
buildMeta = Singleton.buildMeta,
|
buildMeta = Singleton.buildMeta,
|
||||||
|
userCertificatesProvider = userCertificatesProvider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue