WIP: feat: add eastcompeace supports #185
1 changed files with 45 additions and 19 deletions
|
@ -12,23 +12,16 @@ data class EuiccVendorInfo(
|
||||||
val firmwareVersion: String?,
|
val firmwareVersion: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val EUICC_VENDORS: Array<EuiccVendor> = arrayOf(EstkMe(), SimLink())
|
private val EUICC_VENDORS: Array<EuiccVendor> = arrayOf(ESTKme(), SIMLink9(), Eastcompeace())
|
||||||
|
|
||||||
fun EuiccChannel.tryParseEuiccVendorInfo(): EuiccVendorInfo? {
|
fun EuiccChannel.tryParseEuiccVendorInfo(): EuiccVendorInfo? =
|
||||||
EUICC_VENDORS.forEach { vendor ->
|
EUICC_VENDORS.firstNotNullOfOrNull { it.tryParseEuiccVendorInfo(this) }
|
||||||
vendor.tryParseEuiccVendorInfo(this@tryParseEuiccVendorInfo)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EuiccVendor {
|
interface EuiccVendor {
|
||||||
fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo?
|
fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo?
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EstkMe : EuiccVendor {
|
private class ESTKme : EuiccVendor {
|
||||||
companion object {
|
companion object {
|
||||||
private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex()
|
private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex()
|
||||||
private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray()
|
private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray()
|
||||||
|
@ -47,12 +40,6 @@ private class EstkMe : EuiccVendor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeAsn1String(b: ByteArray): String? {
|
|
||||||
if (b.size < 2) return null
|
|
||||||
if (b[b.size - 2] != 0x90.toByte() || b[b.size - 1] != 0x00.toByte()) return null
|
|
||||||
return b.sliceArray(0 until b.size - 2).decodeToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? {
|
override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? {
|
||||||
if (!checkAtr(channel)) return null
|
if (!checkAtr(channel)) return null
|
||||||
|
|
||||||
|
@ -60,7 +47,8 @@ private class EstkMe : EuiccVendor {
|
||||||
return try {
|
return try {
|
||||||
iface.withLogicalChannel(PRODUCT_AID) { transmit ->
|
iface.withLogicalChannel(PRODUCT_AID) { transmit ->
|
||||||
fun invoke(p1: Byte) =
|
fun invoke(p1: Byte) =
|
||||||
decodeAsn1String(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)))
|
decodeResponse(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)))
|
||||||
|
?.decodeToString()
|
||||||
EuiccVendorInfo(
|
EuiccVendorInfo(
|
||||||
skuName = invoke(0x03),
|
skuName = invoke(0x03),
|
||||||
serialNumber = invoke(0x00),
|
serialNumber = invoke(0x00),
|
||||||
|
@ -75,7 +63,7 @@ private class EstkMe : EuiccVendor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SimLink : EuiccVendor {
|
private class SIMLink9 : EuiccVendor {
|
||||||
companion object {
|
companion object {
|
||||||
private val EID_PATTERN = Regex("^89044045(84|21)67274948")
|
private val EID_PATTERN = Regex("^89044045(84|21)67274948")
|
||||||
}
|
}
|
||||||
|
@ -110,3 +98,41 @@ private class SimLink : EuiccVendor {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SpellCheckingInspection")
|
||||||
|
private class Eastcompeace : EuiccVendor {
|
||||||
|
companion object {
|
||||||
|
private const val EID_PREFIX = "89086030"
|
||||||
|
private val PRODUCT_AID = "A000000533C000FF860000000427".decodeHex()
|
||||||
|
private val GET_SCID_COMMAND = "80CA000050".decodeHex()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? {
|
||||||
|
if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null
|
||||||
|
return try {
|
||||||
|
channel.apduInterface.withLogicalChannel(PRODUCT_AID) { transmit ->
|
||||||
|
decodeResponse(transmit(GET_SCID_COMMAND))?.let(::parseSCID)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d(TAG, "Failed to get EastcompeaceInfo", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseSCID(scid: ByteArray): EuiccVendorInfo {
|
||||||
|
// TODO: Some data needs to be accumulated to distinguish SKUs
|
||||||
|
Log.i(TAG, "Eastcompeace SCID: ${scid.encodeHex()}")
|
||||||
|
return EuiccVendorInfo(
|
||||||
|
skuName = "Eastcompeace",
|
||||||
|
serialNumber = null,
|
||||||
|
bootloaderVersion = null,
|
||||||
|
firmwareVersion = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeResponse(b: ByteArray): ByteArray? {
|
||||||
|
if (b.size < 2) return null
|
||||||
|
if (b[b.size - 2] != 0x90.toByte() || b[b.size - 1] != 0x00.toByte()) return null
|
||||||
|
return b.sliceArray(0 until b.size - 2)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue