Skip to content

Commit 715d75c

Browse files
authored
Add Google Tink dependency, replacing androidx.security.crypto (#4405)
* Add Google Tink dependency, replacing `androidx.security.crypto` * Replace the `EncryptedFile` implementation too * Extract constants, add some more docs
1 parent fbdd7e0 commit 715d75c

File tree

10 files changed

+171
-38
lines changed

10 files changed

+171
-38
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ google_firebase_bom = "com.google.firebase:firebase-bom:33.10.0"
8181
firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" }
8282
autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" }
8383
ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
84+
google_tink = "com.google.crypto.tink:tink-android:1.16.0"
8485

8586
# AndroidX
8687
androidx_core = { module = "androidx.core:core", version.ref = "core" }
@@ -100,7 +101,6 @@ androidx_browser = "androidx.browser:browser:1.8.0"
100101
androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" }
101102
androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" }
102103
androidx_splash = "androidx.core:core-splashscreen:1.0.1"
103-
androidx_security_crypto = "androidx.security:security-crypto:1.1.0-alpha06"
104104
androidx_media3_exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
105105
androidx_media3_ui = { module = "androidx.media3:media3-ui", version.ref = "media3" }
106106
androidx_biometric = "androidx.biometric:biometric-ktx:1.2.0-alpha05"

libraries/androidutils/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ dependencies {
3131
implementation(libs.androidx.activity.activity)
3232
implementation(libs.androidx.recyclerview)
3333
implementation(libs.androidx.exifinterface)
34-
implementation(libs.androidx.security.crypto)
3534
api(libs.androidx.browser)
3635

3736
testImplementation(projects.tests.testutils)

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/EncryptedFileFactory.kt

Lines changed: 0 additions & 30 deletions
This file was deleted.

libraries/encrypted-db/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies {
2323
implementation(libs.sqldelight.driver.android)
2424
implementation(libs.sqlcipher)
2525
implementation(libs.sqlite)
26-
implementation(libs.androidx.security.crypto)
26+
implementation(libs.google.tink)
2727

2828
implementation(projects.libraries.androidutils)
2929
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.encrypteddb.crypto
9+
10+
import android.content.Context
11+
import com.google.crypto.tink.KeyTemplates
12+
import com.google.crypto.tink.RegistryConfiguration
13+
import com.google.crypto.tink.StreamingAead
14+
import com.google.crypto.tink.integration.android.AndroidKeysetManager
15+
import com.google.crypto.tink.streamingaead.StreamingAeadConfig
16+
import java.io.File
17+
import java.io.FileInputStream
18+
import java.io.FileOutputStream
19+
20+
/**
21+
* This class is used to write and read encrypted data to/from a file.
22+
*
23+
* It's a simplified version of the same class in [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary).
24+
*
25+
* It uses hardcoded constants that are used in that library, for backwards compatibility reasons.
26+
*/
27+
internal class EncryptedFile(
28+
private val context: Context,
29+
private val file: File
30+
) {
31+
companion object {
32+
/**
33+
* The file content is encrypted using StreamingAead with AES-GCM, with the file name as associated data.
34+
*/
35+
private const val KEYSET_ENCRYPTION_SCHEME = "AES256_GCM_HKDF_4KB"
36+
37+
/**
38+
* The keyset is stored in a shared preference file with this name.
39+
*/
40+
private const val KEYSET_PREF_FILE_NAME = "__androidx_security_crypto_encrypted_file_pref__"
41+
42+
/**
43+
* The keyset is stored in a shared preference with this key, inside the specified file.
44+
*/
45+
private const val KEYSET_ALIAS = "__androidx_security_crypto_encrypted_file_keyset__"
46+
47+
/**
48+
* The URI referencing the master key in the Android Keystore used to encrypt/decrypt the keyset.
49+
*/
50+
private const val MASTER_KEY_URI = "android-keystore://_androidx_security_master_key_"
51+
}
52+
53+
private val androidKeysetManager by lazy {
54+
val keysetManagerBuilder = AndroidKeysetManager.Builder()
55+
.withKeyTemplate(KeyTemplates.get(KEYSET_ENCRYPTION_SCHEME))
56+
.withSharedPref(context, KEYSET_ALIAS, KEYSET_PREF_FILE_NAME)
57+
.withMasterKeyUri(MASTER_KEY_URI)
58+
59+
keysetManagerBuilder.build()
60+
}
61+
62+
private val streamingAead: StreamingAead by lazy {
63+
val streamingAeadKeysetHandle = androidKeysetManager.keysetHandle
64+
streamingAeadKeysetHandle.getPrimitive(RegistryConfiguration.get(), StreamingAead::class.java)
65+
}
66+
67+
init {
68+
StreamingAeadConfig.register()
69+
}
70+
71+
fun openFileOutput(): FileOutputStream {
72+
val fos = FileOutputStream(file)
73+
val stream = streamingAead.newEncryptingStream(fos, file.name.toByteArray())
74+
return EncryptedFileOutputStream(fos.fd, stream)
75+
}
76+
77+
fun openFileInput(): FileInputStream {
78+
val fis = FileInputStream(file)
79+
val stream = streamingAead.newDecryptingStream(fis, file.name.toByteArray())
80+
return EncryptedFileInputStream(fis.fd, stream)
81+
}
82+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.encrypteddb.crypto
9+
10+
import android.os.Build
11+
import androidx.annotation.RequiresApi
12+
import java.io.FileDescriptor
13+
import java.io.FileInputStream
14+
import java.io.InputStream
15+
16+
/**
17+
* This class is used to read encrypted data from a file.
18+
*
19+
* It comes directly from [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary).
20+
*/
21+
internal class EncryptedFileInputStream(
22+
fileDescriptor: FileDescriptor,
23+
private val inputStream: InputStream,
24+
) : FileInputStream(fileDescriptor) {
25+
private val lock = Any()
26+
27+
override fun read(): Int = inputStream.read()
28+
29+
override fun read(b: ByteArray?): Int = inputStream.read(b)
30+
31+
override fun read(b: ByteArray?, off: Int, len: Int): Int = inputStream.read(b, off, len)
32+
33+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
34+
override fun readAllBytes(): ByteArray? = inputStream.readAllBytes()
35+
36+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
37+
override fun readNBytes(b: ByteArray?, off: Int, len: Int): Int = inputStream.readNBytes(b, off, len)
38+
39+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
40+
override fun readNBytes(len: Int): ByteArray? = inputStream.readNBytes(len)
41+
42+
override fun skip(n: Long): Long = inputStream.skip(n)
43+
44+
override fun available(): Int = inputStream.available()
45+
46+
override fun mark(readlimit: Int) = synchronized(lock) { inputStream.mark(readlimit) }
47+
48+
override fun markSupported(): Boolean = inputStream.markSupported()
49+
50+
override fun reset() = synchronized(lock) { inputStream.reset() }
51+
52+
override fun close() = inputStream.close()
53+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.encrypteddb.crypto
9+
10+
import java.io.FileDescriptor
11+
import java.io.FileOutputStream
12+
import java.io.OutputStream
13+
14+
/**
15+
* This class is used to write encrypted data to a file.
16+
*
17+
* It comes directly from [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary).
18+
*/
19+
internal class EncryptedFileOutputStream(
20+
fileDescriptor: FileDescriptor,
21+
private val outputStream: OutputStream
22+
) : FileOutputStream(fileDescriptor) {
23+
override fun write(b: ByteArray?) = outputStream.write(b)
24+
25+
override fun write(b: ByteArray?, off: Int, len: Int) = outputStream.write(b, off, len)
26+
27+
override fun write(b: Int) = outputStream.write(b)
28+
29+
override fun flush() = outputStream.flush()
30+
31+
override fun close() = outputStream.close()
32+
}

libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
package io.element.encrypteddb.passphrase
99

1010
import android.content.Context
11-
import androidx.security.crypto.EncryptedFile
12-
import io.element.android.libraries.androidutils.file.EncryptedFileFactory
11+
import io.element.encrypteddb.crypto.EncryptedFile
1312
import java.io.File
1413
import java.security.SecureRandom
1514

@@ -25,7 +24,7 @@ class RandomSecretPassphraseProvider(
2524
private val secretSize: Int = 256,
2625
) : PassphraseProvider {
2726
override fun getPassphrase(): ByteArray {
28-
val encryptedFile = EncryptedFileFactory(context).create(file)
27+
val encryptedFile = EncryptedFile(context, file)
2928
return if (!file.exists()) {
3029
val secret = generateSecret()
3130
encryptedFile.openFileOutput().use { it.write(secret) }

libraries/push/impl/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ dependencies {
2727
implementation(libs.dagger)
2828
implementation(libs.androidx.corektx)
2929
implementation(libs.androidx.datastore.preferences)
30-
implementation(libs.androidx.security.crypto)
3130
implementation(platform(libs.network.retrofit.bom))
3231
implementation(libs.network.retrofit)
3332
implementation(libs.serialization.json)

libraries/session-storage/impl/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ dependencies {
2828
implementation(libs.sqldelight.driver.android)
2929
implementation(libs.sqlcipher)
3030
implementation(libs.sqlite)
31-
implementation(libs.androidx.security.crypto)
3231
implementation(projects.libraries.di)
3332
implementation(libs.sqldelight.coroutines)
3433

0 commit comments

Comments
 (0)