BITAU-96 Setup AIDL interface and files (#3880)

This commit is contained in:
Andrew Haisting 2024-09-09 10:43:33 -05:00 committed by GitHub
parent fa2d7e0218
commit b7544ef7f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 347 additions and 0 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -0,0 +1,3 @@
package com.bitwarden.bridge.model;
parcelable EncryptedAddTotpLoginItemData;

View file

@ -0,0 +1,3 @@
package com.bitwarden.bridge.model;
parcelable EncryptedSharedAccountData;

View file

@ -0,0 +1,3 @@
package com.bitwarden.bridge.model;
parcelable SymmetricEncryptionKeyData;

View file

@ -0,0 +1,3 @@
package com.bitwarden.bridge.model;
parcelable SymmetricEncryptionKeyFingerprintData;

View file

@ -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,
)

View file

@ -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,
)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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<Account>,
) {
/**
* 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<String>,
val lastSyncTime: Instant,
)
}

View file

@ -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<AccountJson>,
) {
/**
* 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<String>,
@SerialName("lastSyncTime")
@Contextual
val lastSyncTime: Instant,
)
}

View file

@ -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

View file

@ -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

View file

@ -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<Instant> {
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())
}