From 25dfd96d70a99c51d35e642f9a6ef31f4102a8c1 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Wed, 10 Jul 2024 12:19:28 -0600 Subject: [PATCH] Make encryption keys align with iOS (#273) * enforce encryption keys like iOS * fix up all the tests --- .../org/xmtp/android/library/ClientTest.kt | 41 ++++++++++++++----- .../android/library/GroupPermissionsTest.kt | 5 ++- .../org/xmtp/android/library/GroupTest.kt | 5 ++- .../xmtp/android/library/GroupUpdatedTest.kt | 3 ++ .../java/org/xmtp/android/library/Client.kt | 40 +----------------- 5 files changed, 43 insertions(+), 51 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt index 29970caa5..a13825a01 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt @@ -13,6 +13,7 @@ import org.xmtp.android.library.messages.PrivateKeyBundleV1Builder import org.xmtp.android.library.messages.generate import org.xmtp.proto.message.contents.PrivateKeyOuterClass import uniffi.xmtpv3.GenericException +import java.security.SecureRandom import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -83,12 +84,14 @@ class ClientTest { @Test fun testV3CanBeCreatedWithBundle() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) val client = Client().create(account = fakeWallet, options = options) @@ -118,6 +121,7 @@ class ClientTest { @Test fun testCreatesAV3Client() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val client = @@ -126,7 +130,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) runBlocking { @@ -137,6 +142,7 @@ class ClientTest { @Test fun testCanDeleteDatabase() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val fakeWallet2 = PrivateKeyBuilder() @@ -146,7 +152,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) val client2 = @@ -155,7 +162,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) @@ -173,7 +181,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) @@ -185,6 +194,7 @@ class ClientTest { @Test fun testCreatesAV3DevClient() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val client = @@ -193,7 +203,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.DEV, true), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) runBlocking { @@ -203,6 +214,7 @@ class ClientTest { @Test fun testCreatesAV3ProductionClient() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val client = @@ -211,7 +223,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.PRODUCTION, true), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) runBlocking { @@ -301,6 +314,7 @@ class ClientTest { @Test fun testCanDropReconnectDatabase() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val fakeWallet = PrivateKeyBuilder() val fakeWallet2 = PrivateKeyBuilder() @@ -310,7 +324,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) val alixClient = @@ -319,7 +334,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) @@ -348,6 +364,7 @@ class ClientTest { @Test fun testCanGetAnInboxIdFromAddress() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext val alixWallet = PrivateKeyBuilder() val boWallet = PrivateKeyBuilder() @@ -357,7 +374,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) val boClient = @@ -366,7 +384,8 @@ class ClientTest { options = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) val boInboxId = runBlocking { diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupPermissionsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupPermissionsTest.kt index 4f08b91b8..87e0d0c5f 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupPermissionsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupPermissionsTest.kt @@ -14,6 +14,7 @@ import org.xmtp.android.library.messages.PrivateKeyBuilder import org.xmtp.android.library.messages.walletAddress import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption +import java.security.SecureRandom @RunWith(AndroidJUnit4::class) class GroupPermissionsTest { @@ -30,13 +31,15 @@ class GroupPermissionsTest { @Before fun setUp() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext fixtures = fixtures( clientOptions = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) alixWallet = fixtures.aliceAccount diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt index c3bfa78f3..736f5ff0b 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -27,6 +27,7 @@ import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.mls.message.contents.TranscriptMessages import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption +import java.security.SecureRandom @RunWith(AndroidJUnit4::class) class GroupTest { @@ -43,13 +44,15 @@ class GroupTest { @Before fun setUp() { + val key = SecureRandom().generateSeed(32) val context = InstrumentationRegistry.getInstrumentation().targetContext fixtures = fixtures( clientOptions = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, - appContext = context + appContext = context, + dbEncryptionKey = key ) ) alixWallet = fixtures.aliceAccount diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt index 0772bbea7..15938fc04 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt @@ -14,6 +14,7 @@ import org.xmtp.android.library.codecs.GroupUpdatedCodec import org.xmtp.android.library.messages.PrivateKey import org.xmtp.android.library.messages.PrivateKeyBuilder import org.xmtp.android.library.messages.walletAddress +import java.security.SecureRandom @RunWith(AndroidJUnit4::class) class GroupUpdatedTest { @@ -31,11 +32,13 @@ class GroupUpdatedTest { @Before fun setUp() { + val key = SecureRandom().generateSeed(32) fixtures = fixtures( clientOptions = ClientOptions( ClientOptions.Api(XMTPEnvironment.LOCAL, false), enableV3 = true, appContext = context, + dbEncryptionKey = key ) ) alixWallet = fixtures.aliceAccount diff --git a/library/src/main/java/org/xmtp/android/library/Client.kt b/library/src/main/java/org/xmtp/android/library/Client.kt index 6831d6b0f..133d3ba5e 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -2,14 +2,10 @@ package org.xmtp.android.library import android.content.Context import android.os.Build -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties import android.util.Log import com.google.crypto.tink.subtle.Base64 import com.google.gson.GsonBuilder -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import org.web3j.crypto.Keys import org.web3j.crypto.Keys.toChecksumAddress import org.xmtp.android.library.codecs.ContentCodec @@ -52,14 +48,11 @@ import uniffi.xmtpv3.getInboxIdForAddress import uniffi.xmtpv3.getVersionInfo import java.io.File import java.nio.charset.StandardCharsets -import java.security.KeyStore import java.text.SimpleDateFormat import java.time.Instant import java.util.Date import java.util.Locale import java.util.TimeZone -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey typealias PublishResponse = org.xmtp.proto.message.api.v1.MessageApiOuterClass.PublishResponse typealias QueryResponse = org.xmtp.proto.message.api.v1.MessageApiOuterClass.QueryResponse @@ -337,37 +330,8 @@ class Client() { directoryFile.mkdir() dbPath = directoryFile.absolutePath + "/$alias.db3" - val encryptionKey = if (options.dbEncryptionKey == null) { - val keyStore = KeyStore.getInstance("AndroidKeyStore") - withContext(Dispatchers.IO) { - keyStore.load(null) - } - - val entry = keyStore.getEntry(alias, null) - - val retrievedKey: SecretKey = if (entry is KeyStore.SecretKeyEntry) { - entry.secretKey - } else { - val keyGenerator = - KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, - "AndroidKeyStore" - ) - val keyGenParameterSpec = KeyGenParameterSpec.Builder( - alias, - KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT - ).setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .build() - - keyGenerator.init(keyGenParameterSpec) - keyGenerator.generateKey() - } - retrievedKey.encoded - } else { - options.dbEncryptionKey - } + val encryptionKey = options.dbEncryptionKey + ?: throw XMTPException("No encryption key passed for the database. Please store and provide a secure encryption key.") createClient( logger = logger,