mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-01-28 00:15:32 +00:00
Merge pull request #11 from cfogrady/SecretsRepo
Add Secrets Repo Using a Proto DataStore
This commit is contained in:
commit
da5e6c6628
@ -3,6 +3,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
id("com.google.devtools.ksp") version "2.0.21-1.0.27"
|
id("com.google.devtools.ksp") version "2.0.21-1.0.27"
|
||||||
|
id("com.google.protobuf")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -38,6 +39,29 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
baseline = file("lint-baseline.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protobuf {
|
||||||
|
protoc {
|
||||||
|
artifact = "com.google.protobuf:protoc:4.27.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the java Protobuf-lite code for the Protobufs in this project. See
|
||||||
|
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
|
||||||
|
// for more information.
|
||||||
|
generateProtoTasks {
|
||||||
|
all().forEach { task ->
|
||||||
|
task.builtins {
|
||||||
|
create("java") {
|
||||||
|
option("lite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -51,6 +75,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.datastore)
|
||||||
implementation(libs.androidx.ui)
|
implementation(libs.androidx.ui)
|
||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
@ -64,5 +89,6 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
implementation("androidx.navigation:navigation-compose:2.7.0")
|
implementation("androidx.navigation:navigation-compose:2.7.0")
|
||||||
implementation("com.google.android.material:material:1.2.0")
|
implementation("com.google.android.material:material:1.2.0")
|
||||||
|
implementation(libs.protobuf.javalite)
|
||||||
implementation("androidx.compose.material:material")
|
implementation("androidx.compose.material:material")
|
||||||
}
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package com.github.nacabaro.vbhelper.di
|
package com.github.nacabaro.vbhelper.di
|
||||||
|
|
||||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val db: AppDatabase
|
val db: AppDatabase
|
||||||
|
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
||||||
}
|
}
|
||||||
@ -1,9 +1,22 @@
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.dataStore
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
import com.github.nacabaro.vbhelper.di.AppContainer
|
import com.github.nacabaro.vbhelper.di.AppContainer
|
||||||
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsSerializer
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
|
||||||
|
private const val SECRETS_DATA_STORE_NAME = "secrets.pb"
|
||||||
|
|
||||||
|
val Context.secretsStore: DataStore<Secrets> by dataStore(
|
||||||
|
fileName = SECRETS_DATA_STORE_NAME,
|
||||||
|
serializer = SecretsSerializer
|
||||||
|
)
|
||||||
|
|
||||||
class DefaultAppContainer(private val context: Context) : AppContainer {
|
class DefaultAppContainer(private val context: Context) : AppContainer {
|
||||||
|
|
||||||
override val db: AppDatabase by lazy {
|
override val db: AppDatabase by lazy {
|
||||||
Room.databaseBuilder(
|
Room.databaseBuilder(
|
||||||
context = context,
|
context = context,
|
||||||
@ -11,4 +24,8 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
|
|||||||
"internalDb"
|
"internalDb"
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ class ApkSecretsImporter(private val dexFileSecretsImporter: SecretsImporter = D
|
|||||||
}
|
}
|
||||||
|
|
||||||
// importSecrets imports the secrets from the apk input stream, and validates them.
|
// importSecrets imports the secrets from the apk input stream, and validates them.
|
||||||
override fun importSecrets(inputStream: InputStream): Map<UShort, Secrets> {
|
override fun importSecrets(inputStream: InputStream): Secrets {
|
||||||
ZipInputStream(inputStream).use { zip ->
|
ZipInputStream(inputStream).use { zip ->
|
||||||
var zipEntry = zip.nextEntry
|
var zipEntry = zip.nextEntry
|
||||||
while(zipEntry != null) {
|
while(zipEntry != null) {
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import com.github.cfogrady.vbnfc.CryptographicTransformer
|
||||||
|
import com.github.cfogrady.vbnfc.data.DeviceType
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets.HmacKeys
|
||||||
|
import kotlinx.coroutines.flow.single
|
||||||
|
|
||||||
|
class DataStoreSecretsRepository(
|
||||||
|
private val secretsDataStore: DataStore<Secrets>,
|
||||||
|
): SecretsRepository {
|
||||||
|
override val secretsFlow = secretsDataStore.data
|
||||||
|
|
||||||
|
override suspend fun updateSecrets(secrets: Secrets) {
|
||||||
|
secretsDataStore.updateData {
|
||||||
|
secrets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getSecrets(): Secrets {
|
||||||
|
return secretsFlow.single()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Secrets.getHmacKeys(deviceTypeId: UShort): HmacKeys {
|
||||||
|
return when(deviceTypeId) {
|
||||||
|
DeviceType.VitalBraceletBEDeviceType -> this.beHmacKeys
|
||||||
|
DeviceType.VitalCharactersDeviceType -> this.vbcHmacKeys
|
||||||
|
DeviceType.VitalSeriesDeviceType -> this.vbdmHmacKeys
|
||||||
|
else -> throw IllegalArgumentException("Unknown DeviceTypeId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Secrets.getCryptographicTransformerMap(): Map<UShort, CryptographicTransformer> {
|
||||||
|
val cipher = this.vbCipherList.toIntArray()
|
||||||
|
val beCipher = this.beCipherList.toIntArray()
|
||||||
|
val vbdmHmacKeys = this.getHmacKeys(DeviceType.VitalSeriesDeviceType)
|
||||||
|
val vbcHmacKeys = this.getHmacKeys(DeviceType.VitalCharactersDeviceType)
|
||||||
|
val beHmacKeys = this.getHmacKeys(DeviceType.VitalBraceletBEDeviceType)
|
||||||
|
return mapOf(
|
||||||
|
Pair(DeviceType.VitalSeriesDeviceType, CryptographicTransformer(vbdmHmacKeys.hmacKey1, vbdmHmacKeys.hmacKey2, this.aesKey, cipher)),
|
||||||
|
Pair(DeviceType.VitalCharactersDeviceType, CryptographicTransformer(vbcHmacKeys.hmacKey1, vbcHmacKeys.hmacKey2, this.aesKey, cipher)),
|
||||||
|
Pair(DeviceType.VitalBraceletBEDeviceType, CryptographicTransformer(beHmacKeys.hmacKey1, beHmacKeys.hmacKey2, this.aesKey, beCipher)),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.CryptographicTransformer
|
||||||
import com.github.cfogrady.vbnfc.data.DeviceType
|
import com.github.cfogrady.vbnfc.data.DeviceType
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets.HmacKeys
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
@ -28,63 +31,58 @@ class DexFileSecretsImporter: SecretsImporter {
|
|||||||
const val VBC_TEST_TAG_PASSWORD = "a71dfb22"
|
const val VBC_TEST_TAG_PASSWORD = "a71dfb22"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun importSecrets(inputStream: InputStream): Map<UShort, Secrets> {
|
override fun importSecrets(inputStream: InputStream): Secrets {
|
||||||
val deviceToSecrets = readSecrets(inputStream)
|
val deviceToSecrets = readSecrets(inputStream)
|
||||||
verifySecretCorrectness(deviceToSecrets)
|
verifySecretCorrectness(deviceToSecrets)
|
||||||
return deviceToSecrets
|
return deviceToSecrets
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readSecrets(inputStream: InputStream): Map<UShort, Secrets> {
|
private fun readSecrets(inputStream: InputStream): Secrets {
|
||||||
val dexFile = inputStream.readBytes()
|
val dexFile = inputStream.readBytes()
|
||||||
val byteOrder = ByteOrder.BIG_ENDIAN
|
val byteOrder = ByteOrder.BIG_ENDIAN
|
||||||
val vbdmSubstitutionCipher = dexFile.sliceArray(VBDM_SUBSTITUTION_CIPHER_IDX until VBDM_SUBSTITUTION_CIPHER_IDX+(16*4)).toIntArray(byteOrder)
|
val vbdmSubstitutionCipher = dexFile.sliceArray(VBDM_SUBSTITUTION_CIPHER_IDX until VBDM_SUBSTITUTION_CIPHER_IDX+(16*4)).toIntArray(byteOrder)
|
||||||
val beSubstitutionCipher = dexFile.sliceArray(BE_SUBSTITUTION_CIPHER_IDX until BE_SUBSTITUTION_CIPHER_IDX+(16*4)).toIntArray(byteOrder)
|
val beSubstitutionCipher = dexFile.sliceArray(BE_SUBSTITUTION_CIPHER_IDX until BE_SUBSTITUTION_CIPHER_IDX+(16*4)).toIntArray(byteOrder)
|
||||||
val aesKey = dexFile.sliceArray(AES_KEY_IDX until AES_KEY_IDX+24).toString(StandardCharsets.UTF_8)
|
val aesKey = dexFile.sliceArray(AES_KEY_IDX until AES_KEY_IDX+24).toString(StandardCharsets.UTF_8)
|
||||||
val secretsByDevices = mapOf(
|
return Secrets.newBuilder()
|
||||||
Pair(DeviceType.VitalSeriesDeviceType, buildSecrets(dexFile, VBDM_HMAC_KEY_1_IDX, VBDM_HMAC_KEY_2_IDX, aesKey, vbdmSubstitutionCipher)),
|
.setAesKey(aesKey)
|
||||||
Pair(DeviceType.VitalBraceletBEDeviceType, buildSecrets(dexFile, BE_HMAC_KEY_1_IDX, BE_HMAC_KEY_2_IDX, aesKey, beSubstitutionCipher)),
|
.addAllVbCipher(vbdmSubstitutionCipher.toList())
|
||||||
Pair(DeviceType.VitalCharactersDeviceType, buildSecrets(dexFile, VBC_HMAC_KEY_1_IDX, VBC_HMAC_KEY_2_IDX, aesKey, vbdmSubstitutionCipher)),
|
.addAllBeCipher(beSubstitutionCipher.toList())
|
||||||
)
|
.setVbdmHmacKeys(getHmac(dexFile, VBDM_HMAC_KEY_1_IDX, VBDM_HMAC_KEY_2_IDX))
|
||||||
return secretsByDevices
|
.setVbcHmacKeys(getHmac(dexFile, VBC_HMAC_KEY_1_IDX, VBC_HMAC_KEY_2_IDX))
|
||||||
|
.setBeHmacKeys(getHmac(dexFile, BE_HMAC_KEY_1_IDX, BE_HMAC_KEY_2_IDX))
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSecrets(dexFile: ByteArray, hmacKeyIdx1: Int, hmacKeyIdx2: Int, aesKey: String, substitutionCipher: IntArray): Secrets {
|
private fun getHmac(dexFile: ByteArray, hmacKeyIdx1: Int, hmacKeyIdx2: Int): HmacKeys {
|
||||||
val hmacKey1 = dexFile.sliceArray(hmacKeyIdx1 until hmacKeyIdx1+24).toString(StandardCharsets.UTF_8)
|
val hmacKey1 = dexFile.sliceArray(hmacKeyIdx1 until hmacKeyIdx1+24).toString(StandardCharsets.UTF_8)
|
||||||
val hmacKey2 = dexFile.sliceArray(hmacKeyIdx2 until hmacKeyIdx2+24).toString(StandardCharsets.UTF_8)
|
val hmacKey2 = dexFile.sliceArray(hmacKeyIdx2 until hmacKeyIdx2+24).toString(StandardCharsets.UTF_8)
|
||||||
return Secrets(hmacKey1, hmacKey2, aesKey, substitutionCipher)
|
return HmacKeys.newBuilder()
|
||||||
|
.setHmacKey1(hmacKey1)
|
||||||
|
.setHmacKey2(hmacKey2)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
private fun verifySecretCorrectness(deviceToSecrets: Map<UShort, Secrets>) {
|
private fun verifySecretCorrectness(secrets: Secrets) {
|
||||||
for (keyValue in deviceToSecrets) {
|
val deviceToCryptographicTransformers = secrets.getCryptographicTransformerMap()
|
||||||
|
for (keyValue in deviceToCryptographicTransformers) {
|
||||||
when(keyValue.key) {
|
when(keyValue.key) {
|
||||||
DeviceType.VitalBraceletBEDeviceType -> {
|
DeviceType.VitalBraceletBEDeviceType -> assertBuildsCorrectPassword(keyValue.value, BE_TEST_TAG_PASSWORD)
|
||||||
val result = keyValue.value.toCryptographicTransformer().createNfcPassword(
|
DeviceType.VitalCharactersDeviceType -> assertBuildsCorrectPassword(keyValue.value, VBC_TEST_TAG_PASSWORD)
|
||||||
TEST_TAG
|
DeviceType.VitalSeriesDeviceType -> assertBuildsCorrectPassword(keyValue.value, VBDM_TEST_TAG_PASSWORD)
|
||||||
)
|
|
||||||
if( result.toHexString() != BE_TEST_TAG_PASSWORD) {
|
|
||||||
throw InvalidKeyException("Secrets were loaded, but were unsuccessful at generating the test password: ${result.toHexString()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DeviceType.VitalCharactersDeviceType -> {
|
|
||||||
val result = keyValue.value.toCryptographicTransformer().createNfcPassword(
|
|
||||||
TEST_TAG
|
|
||||||
)
|
|
||||||
if( result.toHexString() != VBC_TEST_TAG_PASSWORD) {
|
|
||||||
throw InvalidKeyException("Secrets were loaded, but were unsuccessful at generating the test password: ${result.toHexString()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DeviceType.VitalSeriesDeviceType -> {
|
|
||||||
val result = keyValue.value.toCryptographicTransformer().createNfcPassword(
|
|
||||||
TEST_TAG
|
|
||||||
)
|
|
||||||
if( result.toHexString() != VBDM_TEST_TAG_PASSWORD) {
|
|
||||||
throw InvalidKeyException("Secrets were loaded, but were unsuccessful at generating the test password: ${result.toHexString()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
private fun assertBuildsCorrectPassword(cryptographicTransformer: CryptographicTransformer, expectedPassword: String) {
|
||||||
|
val result = cryptographicTransformer.createNfcPassword(
|
||||||
|
TEST_TAG
|
||||||
|
)
|
||||||
|
if( result.toHexString() != expectedPassword) {
|
||||||
|
throw InvalidKeyException("Secrets were loaded, but were unsuccessful at generating the test password")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ByteArray.toIntArray(byteOrder: ByteOrder): IntArray {
|
fun ByteArray.toIntArray(byteOrder: ByteOrder): IntArray {
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
|
||||||
|
|
||||||
import com.github.cfogrady.vbnfc.CryptographicTransformer
|
|
||||||
|
|
||||||
data class Secrets(val hmacKey1: String, val hmacKey2: String, val aesKey: String, val substitutionCipher: IntArray) {
|
|
||||||
fun toCryptographicTransformer(): CryptographicTransformer {
|
|
||||||
return CryptographicTransformer(hmacKey1, hmacKey2, aesKey, substitutionCipher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,8 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
fun interface SecretsImporter {
|
fun interface SecretsImporter {
|
||||||
fun importSecrets(inputStream: InputStream): Map<UShort, Secrets>
|
fun importSecrets(inputStream: InputStream): Secrets
|
||||||
}
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface SecretsRepository {
|
||||||
|
val secretsFlow: Flow<Secrets>
|
||||||
|
|
||||||
|
suspend fun getSecrets(): Secrets
|
||||||
|
suspend fun updateSecrets(secrets: Secrets)
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import androidx.datastore.core.CorruptionException
|
||||||
|
import androidx.datastore.core.Serializer
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
object SecretsSerializer: Serializer<Secrets> {
|
||||||
|
override val defaultValue = Secrets.getDefaultInstance()
|
||||||
|
|
||||||
|
override suspend fun readFrom(input: InputStream): Secrets {
|
||||||
|
try {
|
||||||
|
return Secrets.parseFrom(input)
|
||||||
|
} catch (exception: InvalidProtocolBufferException) {
|
||||||
|
throw CorruptionException("Cannot read proto.", exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun writeTo(t: Secrets, output: OutputStream) {
|
||||||
|
t.writeTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "com.github.nacabaro.vbhelper.source.proto";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Secrets {
|
||||||
|
string aes_key = 1;
|
||||||
|
repeated int32 vb_cipher = 2;
|
||||||
|
repeated int32 be_cipher = 3;
|
||||||
|
|
||||||
|
message HmacKeys {
|
||||||
|
string hmac_key_1 = 1;
|
||||||
|
string hmac_key_2 = 2;
|
||||||
|
}
|
||||||
|
HmacKeys vbdm_hmac_keys = 4;
|
||||||
|
HmacKeys vbc_hmac_keys = 5;
|
||||||
|
HmacKeys be_hmac_keys = 6;
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
import com.github.cfogrady.vbnfc.data.DeviceType
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@ -14,15 +14,18 @@ import java.util.zip.ZipOutputStream
|
|||||||
class ApkSecretsImporterTest {
|
class ApkSecretsImporterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testThatRealImportSecretsHasAllDeviceTypes() {
|
fun testThatRealImportSecretsPasses() {
|
||||||
val apkFileSecretsImporter = ApkSecretsImporter()
|
val apkFileSecretsImporter = ApkSecretsImporter()
|
||||||
val url = getAndAssertApkFile()
|
val url = getAndAssertApkFile()
|
||||||
val file = File(url.path)
|
val file = File(url.path)
|
||||||
file.inputStream().use {
|
file.inputStream().use {
|
||||||
val deviceIdToSecrets = apkFileSecretsImporter.importSecrets(it)
|
val secrets = apkFileSecretsImporter.importSecrets(it)
|
||||||
Assert.assertNotNull("BE Device Type", deviceIdToSecrets[DeviceType.VitalBraceletBEDeviceType])
|
Assert.assertEquals("Cipher size isn't correct", 16, secrets.vbCipherCount)
|
||||||
Assert.assertNotNull("VBDM Device Type", deviceIdToSecrets[DeviceType.VitalSeriesDeviceType])
|
Assert.assertEquals("BE Cipher size isn't correct", 16, secrets.beCipherCount)
|
||||||
Assert.assertNotNull("VBC Device Type", deviceIdToSecrets[DeviceType.VitalCharactersDeviceType])
|
Assert.assertFalse("AES Key is empty", secrets.aesKey.isEmpty())
|
||||||
|
assertHmacKeysArePopulated("VBDM", secrets.vbdmHmacKeys)
|
||||||
|
assertHmacKeysArePopulated("VBC", secrets.vbcHmacKeys)
|
||||||
|
assertHmacKeysArePopulated("BE", secrets.beHmacKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +37,7 @@ class ApkSecretsImporterTest {
|
|||||||
val inputStreamContents = it.readAllBytes()
|
val inputStreamContents = it.readAllBytes()
|
||||||
Assert.assertTrue("Unexpected file contents received by DexSecretsImporter", inputStreamContents.contentEquals(expectedDexContents))
|
Assert.assertTrue("Unexpected file contents received by DexSecretsImporter", inputStreamContents.contentEquals(expectedDexContents))
|
||||||
foundFile = true
|
foundFile = true
|
||||||
emptyMap()
|
Secrets.newBuilder().build()
|
||||||
}
|
}
|
||||||
val apkBytes = constructTestApk(expectedDexContents)
|
val apkBytes = constructTestApk(expectedDexContents)
|
||||||
ByteArrayInputStream(apkBytes).use {
|
ByteArrayInputStream(apkBytes).use {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package com.github.nacabaro.vbhelper.source
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
import com.github.cfogrady.vbnfc.data.DeviceType
|
import com.github.nacabaro.vbhelper.source.proto.Secrets.HmacKeys
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@ -10,18 +10,26 @@ import java.nio.ByteOrder
|
|||||||
import java.security.InvalidKeyException
|
import java.security.InvalidKeyException
|
||||||
|
|
||||||
|
|
||||||
|
fun assertHmacKeysArePopulated(msg: String, hmacKeys: HmacKeys) {
|
||||||
|
Assert.assertFalse("$msg hmacKey1 is empty", hmacKeys.hmacKey1.isEmpty())
|
||||||
|
Assert.assertFalse("$msg hmacKey2 is empty", hmacKeys.hmacKey2.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
class DexFileSecretsImporterTest {
|
class DexFileSecretsImporterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testThatImportSecretsHasAllDeviceTypes() {
|
fun testThatImportSecretsIsPopulated() {
|
||||||
val dexFileSecretsImporter = DexFileSecretsImporter()
|
val dexFileSecretsImporter = DexFileSecretsImporter()
|
||||||
val url = getAndAssertClassesDexFile()
|
val url = getAndAssertClassesDexFile()
|
||||||
val file = File(url.path)
|
val file = File(url.path)
|
||||||
file.inputStream().use {
|
file.inputStream().use {
|
||||||
val deviceIdToSecrets = dexFileSecretsImporter.importSecrets(it)
|
val secrets = dexFileSecretsImporter.importSecrets(it)
|
||||||
Assert.assertNotNull("BE Device Type", deviceIdToSecrets[DeviceType.VitalBraceletBEDeviceType])
|
Assert.assertEquals("Cipher size isn't correct", 16, secrets.vbCipherCount)
|
||||||
Assert.assertNotNull("VBDM Device Type", deviceIdToSecrets[DeviceType.VitalSeriesDeviceType])
|
Assert.assertEquals("BE Cipher size isn't correct", 16, secrets.beCipherCount)
|
||||||
Assert.assertNotNull("VBC Device Type", deviceIdToSecrets[DeviceType.VitalCharactersDeviceType])
|
Assert.assertFalse("AES Key is empty", secrets.aesKey.isEmpty())
|
||||||
|
assertHmacKeysArePopulated("VBDM", secrets.vbdmHmacKeys)
|
||||||
|
assertHmacKeysArePopulated("VBC", secrets.vbcHmacKeys)
|
||||||
|
assertHmacKeysArePopulated("BE", secrets.beHmacKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,3 +4,9 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
classpath(libs.protobuf.gradle.plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.3"
|
agp = "8.7.3"
|
||||||
|
datastore = "1.1.1"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
coreKtx = "1.15.0"
|
coreKtx = "1.15.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
@ -8,12 +9,15 @@ espressoCore = "3.6.1"
|
|||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
activityCompose = "1.9.3"
|
activityCompose = "1.9.3"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2024.04.01"
|
||||||
|
protobufGradlePlugin = "0.9.4"
|
||||||
|
protobufJavalite = "4.27.0"
|
||||||
roomRuntime = "2.6.1"
|
roomRuntime = "2.6.1"
|
||||||
vbNfcReader = "0.1.0"
|
vbNfcReader = "0.1.0"
|
||||||
dimReader = "2.1.0"
|
dimReader = "2.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
|
||||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
|
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
|
||||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
@ -29,6 +33,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
|
|||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
protobuf-gradle-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobufGradlePlugin" }
|
||||||
|
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobufJavalite" }
|
||||||
vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }
|
vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }
|
||||||
dim-reader = { module = "com.github.cfogrady:vb-dim-reader", version.ref = "dimReader" }
|
dim-reader = { module = "com.github.cfogrady:vb-dim-reader", version.ref = "dimReader" }
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user