From b7544ef7f4ef58d6ac3da4b58204f21ee08d76f5 Mon Sep 17 00:00:00 2001 From: Andrew Haisting <142518658+ahaisting-livefront@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:43:33 -0500 Subject: [PATCH] BITAU-96 Setup AIDL interface and files (#3880) --- .../com/bitwarden/bridge/IBridgeService.aidl | 61 +++++++++++++++++++ .../bridge/IBridgeServiceCallback.aidl | 10 +++ .../model/EncryptedAddTotpLoginItemData.aidl | 3 + .../model/EncryptedSharedAccountData.aidl | 3 + .../model/SymmetricEncryptionKeyData.aidl | 3 + ...SymmetricEncryptionKeyFingerprintData.aidl | 3 + .../bridge/model/AddTotpLoginItemData.kt | 13 ++++ .../bridge/model/AddTotpLoginItemDataJson.kt | 16 +++++ .../bridge/model/ByteArrayContainer.kt | 34 +++++++++++ .../model/EncryptedAddTotpLoginItemData.kt | 17 ++++++ .../model/EncryptedSharedAccountData.kt | 17 ++++++ .../bridge/model/SharedAccountData.kt | 32 ++++++++++ .../bridge/model/SharedAccountDataJson.kt | 54 ++++++++++++++++ .../model/SymmetricEncryptionKeyData.kt | 14 +++++ .../SymmetricEncryptionKeyFingerprintData.kt | 15 +++++ .../com/bitwarden/bridge/util/Constants.kt | 52 ++++++++++++++++ 16 files changed, 347 insertions(+) create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/IBridgeService.aidl create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/IBridgeServiceCallback.aidl create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.aidl create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedSharedAccountData.aidl create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.aidl create mode 100644 bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.aidl create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemDataJson.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/ByteArrayContainer.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/EncryptedSharedAccountData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountDataJson.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.kt create mode 100644 bridge/src/main/java/com/bitwarden/bridge/util/Constants.kt diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeService.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeService.aidl new file mode 100644 index 000000000..02256f3a5 --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeService.aidl @@ -0,0 +1,61 @@ +package com.bitwarden.bridge; + +import com.bitwarden.bridge.model.EncryptedAddTotpLoginItemData; +import com.bitwarden.bridge.model.SymmetricEncryptionKeyData; +import com.bitwarden.bridge.model.SymmetricEncryptionKeyFingerprintData; +import com.bitwarden.bridge.IBridgeServiceCallback; + +interface IBridgeService { + // ============== + // Configuration + // ============== + + // Returns the version number string of the Bridge SDK. This is useful so that callers + // can compare the version of their Bridge SDK with this value and ensure that the two are + // compatible. + // + // For more info about versioning the Bridge SDK, see the Bridge SDK README. + String getVersionNumber(); + + // Returns true when the given symmetric fingerprint data matches that contained by the SDK. + boolean checkSymmetricEncryptionKeyFingerprint(in SymmetricEncryptionKeyFingerprintData data); + + // Returns a symmetric key that will be used for encypting all IPC traffic. + // + // Consumers should only call this function once to limit the number of times this key is + // sent via IPC. Additionally, once the key is shared, checkSymmetricEncryptionKeyFingerprint + // should be used to safely confirm that the key is valid. + @nullable SymmetricEncryptionKeyData getSymmetricEncryptionKeyData(); + + // ============== + // Registration + // ============== + + // Register the given callback to receive updates after syncAccounts is called. + void registerBridgeServiceCallback(IBridgeServiceCallback callback); + + // Unregister the given callback from receiving updates. + void unregisterBridgeServiceCallback(IBridgeServiceCallback callback); + + // ============== + // Data Syncing + // ============== + + // Sync available accounts. Callers should register a callback via + // registerBridgeServiceCallback before calling this function. + void syncAccounts(); + + // ============== + // Add TOTP Item + // ============== + + // Returns an intent that can be launched to navigate the user to the add Totp item flow + // in the main password manager app. + Intent createAddTotpLoginItemIntent(); + + // Give the given TOTP item data to the main Bitwarden app before launching the add TOTP + // item flow. This should be called before launching the intent returned from + // createAddTotpLoginItemIntent(). + void setPendingAddTotpLoginItemData(in EncryptedAddTotpLoginItemData data); + +} diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeServiceCallback.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeServiceCallback.aidl new file mode 100644 index 000000000..409116ab6 --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/IBridgeServiceCallback.aidl @@ -0,0 +1,10 @@ +package com.bitwarden.bridge; + +import com.bitwarden.bridge.model.EncryptedSharedAccountData; + +interface IBridgeServiceCallback { + + // This function will be called when there is updated shared account data. + void onAccountsSync(in EncryptedSharedAccountData data); + +} diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.aidl new file mode 100644 index 000000000..bb28849ba --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.aidl @@ -0,0 +1,3 @@ +package com.bitwarden.bridge.model; + +parcelable EncryptedAddTotpLoginItemData; diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedSharedAccountData.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedSharedAccountData.aidl new file mode 100644 index 000000000..af15b9bc4 --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/model/EncryptedSharedAccountData.aidl @@ -0,0 +1,3 @@ +package com.bitwarden.bridge.model; + +parcelable EncryptedSharedAccountData; diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.aidl new file mode 100644 index 000000000..07a1a784e --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.aidl @@ -0,0 +1,3 @@ +package com.bitwarden.bridge.model; + +parcelable SymmetricEncryptionKeyData; diff --git a/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.aidl b/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.aidl new file mode 100644 index 000000000..c362df5e7 --- /dev/null +++ b/bridge/src/main/aidl/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.aidl @@ -0,0 +1,3 @@ +package com.bitwarden.bridge.model; + +parcelable SymmetricEncryptionKeyFingerprintData; diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemData.kt new file mode 100644 index 000000000..7a576d863 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemData.kt @@ -0,0 +1,13 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Domain level model for a TOTP item to be added to the Bitwarden app. + * + * @param totpUri A TOTP code URI to be added to the Bitwarden app. + */ +data class AddTotpLoginItemData( + val totpUri: String, +) diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemDataJson.kt b/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemDataJson.kt new file mode 100644 index 000000000..76eeee524 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/AddTotpLoginItemDataJson.kt @@ -0,0 +1,16 @@ +package com.bitwarden.bridge.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Serializable model for a TOTP item to be added to the Bitwarden app. For domain level + * model, see [AddTotpLoginItemData]. + * + * @param totpUri A TOTP code URI to be added to the Bitwarden app. + */ +@Serializable +data class AddTotpLoginItemDataJson( + @SerialName("totpUri") + val totpUri: String, +) diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/ByteArrayContainer.kt b/bridge/src/main/java/com/bitwarden/bridge/model/ByteArrayContainer.kt new file mode 100644 index 000000000..01eb71a05 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/ByteArrayContainer.kt @@ -0,0 +1,34 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Wraps a [ByteArray] and implements [equals], [hashCode], and [Parcelable] so that it can more + * easily be included in [Parcelable] models. + * + * @param byteArray Wrapped byte array + */ +@Parcelize +data class ByteArrayContainer( + val byteArray: ByteArray, +) : Parcelable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ByteArrayContainer + + return byteArray.contentEquals(other.byteArray) + } + + override fun hashCode(): Int { + return byteArray.contentHashCode() + } +} + +/** + * Helper function for converting [ByteArray] to [ByteArrayContainer]. + */ +fun ByteArray.toByteArrayContainer(): ByteArrayContainer = + ByteArrayContainer(this) diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.kt new file mode 100644 index 000000000..3b7ac83be --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedAddTotpLoginItemData.kt @@ -0,0 +1,17 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Models an encrypted totp item to be added to the Bitwarden app. + * + * @param initializationVector Cryptographic initialization vector. + * @param encryptedTotpUriJson Encrypted JSON string containing TOTP URI info. See + * [AddTotpLoginItemDataJson] for the json structure of the string. + */ +@Parcelize +data class EncryptedAddTotpLoginItemData( + val initializationVector: ByteArrayContainer, + val encryptedTotpUriJson: ByteArrayContainer, +) : Parcelable diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedSharedAccountData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedSharedAccountData.kt new file mode 100644 index 000000000..0b89f4d42 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/EncryptedSharedAccountData.kt @@ -0,0 +1,17 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Models data that will be sent to calling application via IPC. + * + * @param initializationVector Cryptographic initialization vector. + * @param encryptedAccountsJson Encrypted JSON blob containing shared account data. See + * [SharedAccountDataJson] For the serializable model contained in the blob. + */ +@Parcelize +data class EncryptedSharedAccountData( + val initializationVector: ByteArrayContainer, + val encryptedAccountsJson: ByteArrayContainer, +) : Parcelable diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountData.kt new file mode 100644 index 000000000..b7bb4ac64 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountData.kt @@ -0,0 +1,32 @@ +package com.bitwarden.bridge.model + +import java.time.Instant + +/** + * Domain level model representing shared account data. + * + * @param accounts The list of shared accounts. + */ +data class SharedAccountData( + val accounts: List, +) { + + /** + * Models a single shared account. + * + * @param userId user ID tied to the account. + * @param name name associated with the account. + * @param email email associated with the account. + * @param environmentLabel environment associated with the account. + * @param totpUris list of totp URIs associated with the account. + * @param lastSyncTime the last time the account was synced by the main Bitwarden app. + */ + data class Account( + val userId: String, + val name: String?, + val email: String, + val environmentLabel: String, + val totpUris: List, + val lastSyncTime: Instant, + ) +} diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountDataJson.kt b/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountDataJson.kt new file mode 100644 index 000000000..bcb2a29f2 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/SharedAccountDataJson.kt @@ -0,0 +1,54 @@ +package com.bitwarden.bridge.model + +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.time.Instant + +/** + * Models a serializable list of shared accounts to be shared with other applications. + * + * For domain level model, see [SharedAccountData]. + * + * @param accounts The list of shared accounts. + */ +@Serializable +data class SharedAccountDataJson( + @SerialName("accounts") + val accounts: List, +) { + + /** + * Models a single shared account in a serializable format. + * + * @param userId user ID tied to the account. + * @param name name associated with the account. + * @param email email associated with the account. + * @param environmentLabel environment associated with the account. + * @param totpUris list of totp URIs associated with the account. + * @param lastSyncTime the last time the account was synced by the main Bitwarden app. + */ + @Serializable + data class AccountJson( + @SerialName("userId") + val userId: String, + + @SerialName("name") + val name: String?, + + @SerialName("email") + val email: String, + + @SerialName("environmentLabel") + val environmentLabel: String, + + @SerialName("totpUris") + val totpUris: List, + + @SerialName("lastSyncTime") + @Contextual + val lastSyncTime: Instant, + ) +} + + diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.kt new file mode 100644 index 000000000..6f40dae7a --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyData.kt @@ -0,0 +1,14 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Wrapper for a symmetric encryption key. + * + * @param symmetricEncryptionKey The symmetric encryption key. + */ +@Parcelize +data class SymmetricEncryptionKeyData( + val symmetricEncryptionKey: ByteArrayContainer, +) : Parcelable diff --git a/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.kt b/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.kt new file mode 100644 index 000000000..926d78262 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/model/SymmetricEncryptionKeyFingerprintData.kt @@ -0,0 +1,15 @@ +package com.bitwarden.bridge.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Wrapper for a symmetric encryption fingerprint (hash) that can be used + * to verify if callers have the correct symmetric key for encrypting/decrypting IPC data. + * + * @param symmetricEncryptionKeyFingerprint The fingerprint of the symmetric encryption key. + */ +@Parcelize +data class SymmetricEncryptionKeyFingerprintData( + val symmetricEncryptionKeyFingerprint: ByteArrayContainer, +) : Parcelable diff --git a/bridge/src/main/java/com/bitwarden/bridge/util/Constants.kt b/bridge/src/main/java/com/bitwarden/bridge/util/Constants.kt new file mode 100644 index 000000000..bb2af5628 --- /dev/null +++ b/bridge/src/main/java/com/bitwarden/bridge/util/Constants.kt @@ -0,0 +1,52 @@ +package com.bitwarden.bridge.util + +import com.bitwarden.bridge.BuildConfig +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual +import java.time.Instant + +/** + * Version of the native bridge sdk. + */ +const val NATIVE_BRIDGE_SDK_VERSION = BuildConfig.VERSION + +/** + * Common instance of [Json] that should be used throughout the app. + */ +internal val JSON = Json { + // If there are keys returned by the server not modeled by a serializable class, + // ignore them. + // This makes additive server changes non-breaking. + ignoreUnknownKeys = true + + // We allow for nullable values to have keys missing in the JSON response. + explicitNulls = false + + // Add serializer for Instant serialization. + serializersModule = SerializersModule { + contextual(InstantSerializer) + } + + // Respect model default property values. + coerceInputValues = true +} + +/** + * A simple serializer for serializing [Instant]. + */ +private object InstantSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("java.time.Instant", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Instant) = + encoder.encodeString(value.toString()) + + override fun deserialize(decoder: Decoder): Instant = Instant.parse(decoder.decodeString()) +}