mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-01-27 16:05:32 +00:00
Import APK in tests
This commit is contained in:
parent
5d6e3743f0
commit
f4974c8705
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,3 +6,7 @@
|
|||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
app/src/main/res/values/keys.xml
|
app/src/main/res/values/keys.xml
|
||||||
|
|
||||||
|
app/src/test/resources/com/github/nacabaro/vbhelper/source/com.bandai.vitalbraceletarena.apk
|
||||||
|
|
||||||
|
app/src/test/resources/com/github/nacabaro/vbhelper/source/classes.dex
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
|
class ApkSecretsImporter(private val dexFileSecretsImporter: DexFileSecretsImporter = DexFileSecretsImporter()) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DEX_FILE = "classes.dex"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun importSecrets(inputStream: InputStream): Map<UShort, Secrets> {
|
||||||
|
ZipInputStream(inputStream).use { zip ->
|
||||||
|
var zipEntry = zip.nextEntry
|
||||||
|
while(zipEntry != null) {
|
||||||
|
println("Zip Entry: ${zipEntry.name}")
|
||||||
|
if(zipEntry.name == DEX_FILE) {
|
||||||
|
return dexFileSecretsImporter.importSecrets(zip)
|
||||||
|
}
|
||||||
|
zipEntry = zip.nextEntry
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("File `$DEX_FILE` is missing from apk!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.data.DeviceType
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
|
||||||
|
class DexFileSecretsImporter {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val VBDM_SUBSTITUTION_CIPHER_IDX = 1080145
|
||||||
|
const val BE_SUBSTITUTION_CIPHER_IDX = 1080217
|
||||||
|
|
||||||
|
const val VBDM_HMAC_KEY_2_IDX = 1249063
|
||||||
|
const val VBDM_HMAC_KEY_1_IDX = 1494074
|
||||||
|
const val VBC_HMAC_KEY_1_IDX = 1241640
|
||||||
|
const val VBC_HMAC_KEY_2_IDX = 1466955
|
||||||
|
const val BE_HMAC_KEY_1_IDX = 1580157
|
||||||
|
const val BE_HMAC_KEY_2_IDX = 1593759
|
||||||
|
const val AES_KEY_IDX = 1277527
|
||||||
|
}
|
||||||
|
|
||||||
|
fun importSecrets(inputStream: InputStream): Map<UShort, Secrets> {
|
||||||
|
val dexFile = inputStream.readBytes()
|
||||||
|
val byteOrder = ByteOrder.BIG_ENDIAN
|
||||||
|
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 aesKey = dexFile.sliceArray(AES_KEY_IDX until AES_KEY_IDX+24).toString(StandardCharsets.UTF_8)
|
||||||
|
val cryptographicTransformerByDevices = mapOf(
|
||||||
|
Pair(DeviceType.VitalSeriesDeviceType, buildSecrets(dexFile, VBDM_HMAC_KEY_1_IDX, VBDM_HMAC_KEY_2_IDX, aesKey, vbdmSubstitutionCipher)),
|
||||||
|
Pair(DeviceType.VitalBraceletBEDeviceType, buildSecrets(dexFile, BE_HMAC_KEY_1_IDX, BE_HMAC_KEY_2_IDX, aesKey, beSubstitutionCipher)),
|
||||||
|
Pair(DeviceType.VitalCharactersDeviceType, buildSecrets(dexFile, VBC_HMAC_KEY_1_IDX, VBC_HMAC_KEY_2_IDX, aesKey, vbdmSubstitutionCipher)),
|
||||||
|
)
|
||||||
|
return cryptographicTransformerByDevices
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildSecrets(dexFile: ByteArray, hmacKeyIdx1: Int, hmacKeyIdx2: Int, aesKey: String, substitutionCipher: IntArray): Secrets {
|
||||||
|
val hmacKey1 = dexFile.sliceArray(hmacKeyIdx1 until hmacKeyIdx1+24).toString(StandardCharsets.UTF_8)
|
||||||
|
val hmacKey2 = dexFile.sliceArray(hmacKeyIdx2 until hmacKeyIdx2+24).toString(StandardCharsets.UTF_8)
|
||||||
|
return Secrets(hmacKey1, hmacKey2, aesKey, substitutionCipher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.toIntArray(byteOrder: ByteOrder): IntArray {
|
||||||
|
require(this.size % 4 == 0) { "Number of bytes must be multiple of 4 to convert into 32-bit words" }
|
||||||
|
val values = IntArray(this.size / 4)
|
||||||
|
ByteBuffer.wrap(this).order(byteOrder).asIntBuffer()[values]
|
||||||
|
return values
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.data.DeviceType
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ApkSecretsImporterTest {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Test
|
||||||
|
fun importSecretsTest() {
|
||||||
|
val apkSecretsImporter = ApkSecretsImporter()
|
||||||
|
val url = javaClass.getResource("com.bandai.vitalbraceletarena.apk")
|
||||||
|
if(url == null) {
|
||||||
|
Assert.assertTrue("""
|
||||||
|
Create `resources\com\github\nacabaro\vbhelper\source` within the src/test directory.
|
||||||
|
Add com.bandai.vitalbraceletarena.apk (the official apk) in the above directory. It
|
||||||
|
should never be checked in and should be on the .gitignore.
|
||||||
|
""".trimIndent(), false)
|
||||||
|
}
|
||||||
|
val file = File(url.path)
|
||||||
|
val testTagId = byteArrayOf(0x04, 0x40, 0xaf.toByte(), 0xa2.toByte(), 0xee.toByte(), 0x0f, 0x90.toByte())
|
||||||
|
file.inputStream().use {
|
||||||
|
val deviceIdToCryptographicTransformer = apkSecretsImporter.importSecrets(it)
|
||||||
|
var password = deviceIdToCryptographicTransformer[DeviceType.VitalBraceletBEDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("5651b1c8", password.toHexString())
|
||||||
|
password = deviceIdToCryptographicTransformer[DeviceType.VitalSeriesDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("dd2ceb84", password.toHexString())
|
||||||
|
password = deviceIdToCryptographicTransformer[DeviceType.VitalCharactersDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("515e0c12", password.toHexString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.data.DeviceType
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
class DexFileSecretsImporterTest {
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Test
|
||||||
|
fun importSecretsTest() {
|
||||||
|
val dexFileSecretsImporter = DexFileSecretsImporter()
|
||||||
|
val url = javaClass.getResource("classes.dex")
|
||||||
|
if(url == null) {
|
||||||
|
Assert.assertTrue("""
|
||||||
|
Create `resources\com\github\nacabaro\vbhelper\source` within the src/test directory.
|
||||||
|
Add classes.dex from the official apk in the above directory. It should never be
|
||||||
|
checked in and should be on the .gitignore.
|
||||||
|
""".trimIndent(), false)
|
||||||
|
}
|
||||||
|
val file = File(url.path)
|
||||||
|
val testTagId = byteArrayOf(0x04, 0x40, 0xaf.toByte(), 0xa2.toByte(), 0xee.toByte(), 0x0f, 0x90.toByte())
|
||||||
|
file.inputStream().use {
|
||||||
|
val deviceIdToCryptographicTransformer = dexFileSecretsImporter.importSecrets(it)
|
||||||
|
var password = deviceIdToCryptographicTransformer[DeviceType.VitalBraceletBEDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("5651b1c8", password.toHexString())
|
||||||
|
password = deviceIdToCryptographicTransformer[DeviceType.VitalSeriesDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("dd2ceb84", password.toHexString())
|
||||||
|
password = deviceIdToCryptographicTransformer[DeviceType.VitalCharactersDeviceType]!!.toCryptographicTransformer().createNfcPassword(testTagId)
|
||||||
|
Assert.assertEquals("515e0c12", password.toHexString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user