Reviewed-on: #147 Co-authored-by: septs <github@septs.pw> Co-committed-by: septs <github@septs.pw>
This commit is contained in:
parent
c8ecdee095
commit
1d67fa5cfa
8 changed files with 143 additions and 57 deletions
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
|
@ -9,5 +9,6 @@
|
|||
/navEditor.xml
|
||||
/runConfigurations.xml
|
||||
/workspace.xml
|
||||
/AndroidProjectSystem.xml
|
||||
|
||||
**/*.iml
|
|
@ -23,6 +23,8 @@ import im.angry.openeuicc.common.R
|
|||
import im.angry.openeuicc.core.EuiccChannel
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.util.*
|
||||
import im.angry.openeuicc.vendored.getESTKmeInfo
|
||||
import im.angry.openeuicc.vendored.getSIMLinkVersion
|
||||
import kotlinx.coroutines.launch
|
||||
import net.typeblog.lpac_jni.impl.PKID_GSMA_LIVE_CI
|
||||
import net.typeblog.lpac_jni.impl.PKID_GSMA_TEST_CI
|
||||
|
@ -100,24 +102,22 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
|
||||
private fun buildEuiccInfoItems(channel: EuiccChannel) = buildList {
|
||||
add(Item(R.string.euicc_info_access_mode, channel.type))
|
||||
add(
|
||||
Item(
|
||||
R.string.euicc_info_removable,
|
||||
formatByBoolean(channel.port.card.isRemovable, YES_NO)
|
||||
)
|
||||
)
|
||||
add(
|
||||
Item(
|
||||
R.string.euicc_info_eid,
|
||||
channel.lpa.eID,
|
||||
copiedToastResId = R.string.toast_eid_copied
|
||||
)
|
||||
)
|
||||
add(Item(R.string.euicc_info_removable, formatByBoolean(channel.port.card.isRemovable, YES_NO)))
|
||||
add(Item(R.string.euicc_info_eid, channel.lpa.eID, copiedToastResId = R.string.toast_eid_copied))
|
||||
getESTKmeInfo(channel.apduInterface)?.let {
|
||||
add(Item(R.string.euicc_info_sku, it.skuName))
|
||||
add(Item(R.string.euicc_info_sn, it.serialNumber, copiedToastResId = R.string.toast_sn_copied))
|
||||
add(Item(R.string.euicc_info_bl_ver, it.bootloaderVersion))
|
||||
add(Item(R.string.euicc_info_fw_ver, it.firmwareVersion))
|
||||
}
|
||||
getSIMLinkVersion(channel.lpa.eID, channel.lpa.euiccInfo2?.euiccFirmwareVersion)?.let {
|
||||
add(Item(R.string.euicc_info_sku, "9eSIM $it"))
|
||||
}
|
||||
channel.lpa.euiccInfo2.let { info ->
|
||||
add(Item(R.string.euicc_info_sgp22_version, info?.sgp22Version))
|
||||
add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion))
|
||||
add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion))
|
||||
add(Item(R.string.euicc_info_pp_version, info?.ppVersion))
|
||||
add(Item(R.string.euicc_info_sgp22_version, info?.sgp22Version.toString()))
|
||||
add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion.toString()))
|
||||
add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion.toString()))
|
||||
add(Item(R.string.euicc_info_pp_version, info?.ppVersion.toString()))
|
||||
add(Item(R.string.euicc_info_sas_accreditation_number, info?.sasAccreditationNumber))
|
||||
add(Item(R.string.euicc_info_free_nvram, info?.freeNvram?.let(::formatFreeSpace)))
|
||||
}
|
||||
|
@ -134,13 +134,8 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
}
|
||||
add(Item(R.string.euicc_info_ci_type, getString(resId)))
|
||||
}
|
||||
add(
|
||||
Item(
|
||||
R.string.euicc_info_atr,
|
||||
channel.atr?.encodeHex() ?: getString(R.string.information_unavailable),
|
||||
copiedToastResId = R.string.toast_atr_copied,
|
||||
)
|
||||
)
|
||||
val atr = channel.atr?.encodeHex() ?: getString(R.string.information_unavailable)
|
||||
add(Item(R.string.euicc_info_atr, atr, copiedToastResId = R.string.toast_atr_copied))
|
||||
}
|
||||
|
||||
private fun formatByBoolean(b: Boolean, res: Pair<Int, Int>): String =
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package im.angry.openeuicc.vendored
|
||||
|
||||
import android.util.Log
|
||||
import im.angry.openeuicc.core.ApduInterfaceAtrProvider
|
||||
import im.angry.openeuicc.util.TAG
|
||||
import im.angry.openeuicc.util.decodeHex
|
||||
import net.typeblog.lpac_jni.ApduInterface
|
||||
|
||||
data class ESTKmeInfo(
|
||||
val serialNumber: String?,
|
||||
val bootloaderVersion: String?,
|
||||
val firmwareVersion: String?,
|
||||
val skuName: String?,
|
||||
)
|
||||
|
||||
fun isESTKmeATR(iface: ApduInterface): Boolean {
|
||||
if (iface !is ApduInterfaceAtrProvider) return false
|
||||
val atr = iface.atr ?: return false
|
||||
val fpr = "estk.me".encodeToByteArray()
|
||||
for (index in atr.indices) {
|
||||
if (atr.size - index < fpr.size) break
|
||||
if (atr.sliceArray(index until index + fpr.size).contentEquals(fpr)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getESTKmeInfo(iface: ApduInterface): ESTKmeInfo? {
|
||||
if (!isESTKmeATR(iface)) return null
|
||||
fun decode(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()
|
||||
}
|
||||
return try {
|
||||
iface.withLogicalChannel("A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex()) { transmit ->
|
||||
fun invoke(p1: Byte) = decode(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)))
|
||||
ESTKmeInfo(
|
||||
invoke(0x00), // serial number
|
||||
invoke(0x01), // bootloader version
|
||||
invoke(0x02), // firmware version
|
||||
invoke(0x03), // sku name
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "Failed to get ESTKmeInfo", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package im.angry.openeuicc.vendored
|
||||
|
||||
import net.typeblog.lpac_jni.Version
|
||||
|
||||
private val prefix = Regex("^89044045(84|21)67274948") // SIMLink EID prefix
|
||||
|
||||
fun getSIMLinkVersion(eid: String, version: Version?): String? {
|
||||
if (version == null || prefix.find(eid, 0) == null) return null
|
||||
return when {
|
||||
// @formatter:off
|
||||
version >= Version(37, 1, 41) -> "v3.1 (beta 1)"
|
||||
version >= Version(36, 18, 5) -> "v3 (final)"
|
||||
version >= Version(36, 17, 39) -> "v3 (beta)"
|
||||
version >= Version(36, 17, 4) -> "v2s"
|
||||
version >= Version(36, 9, 3) -> "v2.1"
|
||||
version >= Version(36, 7, 2) -> "v2"
|
||||
// @formatter:on
|
||||
else -> null
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@
|
|||
<string name="toast_profile_enable_failed">Cannot switch to new eSIM profile.</string>
|
||||
<string name="toast_profile_delete_confirm_text_mismatched">Confirmation string mismatch</string>
|
||||
<string name="toast_iccid_copied">ICCID copied to clipboard</string>
|
||||
<string name="toast_sn_copied">Serial Number copied to clipboard</string>
|
||||
<string name="toast_eid_copied">EID copied to clipboard</string>
|
||||
<string name="toast_atr_copied">ATR copied to clipboard</string>
|
||||
|
||||
|
@ -125,6 +126,10 @@
|
|||
<string name="euicc_info_activity_title">eUICC Info (%s)</string>
|
||||
<string name="euicc_info_access_mode">Access Mode</string>
|
||||
<string name="euicc_info_removable">Removable</string>
|
||||
<string name="euicc_info_sku">Product Name</string>
|
||||
<string name="euicc_info_sn">Product Serial Number</string>
|
||||
<string name="euicc_info_bl_ver">Product Bootloader Version</string>
|
||||
<string name="euicc_info_fw_ver">Product Firmware Version</string>
|
||||
<string name="euicc_info_eid" translatable="false">EID</string>
|
||||
<string name="euicc_info_sgp22_version">SGP.22 Version</string>
|
||||
<string name="euicc_info_firmware_version">eUICC OS Version</string>
|
||||
|
|
|
@ -2,14 +2,31 @@ package net.typeblog.lpac_jni
|
|||
|
||||
/* Corresponds to EuiccInfo2 in SGP.22 */
|
||||
data class EuiccInfo2(
|
||||
val sgp22Version: String,
|
||||
val profileVersion: String,
|
||||
val euiccFirmwareVersion: String,
|
||||
val globalPlatformVersion: String,
|
||||
val sgp22Version: Version,
|
||||
val profileVersion: Version,
|
||||
val euiccFirmwareVersion: Version,
|
||||
val globalPlatformVersion: Version,
|
||||
val sasAccreditationNumber: String,
|
||||
val ppVersion: String,
|
||||
val ppVersion: Version,
|
||||
val freeNvram: Int,
|
||||
val freeRam: Int,
|
||||
val euiccCiPKIdListForSigning: Array<String>,
|
||||
val euiccCiPKIdListForVerification: Array<String>,
|
||||
)
|
||||
val euiccCiPKIdListForSigning: Set<String>,
|
||||
val euiccCiPKIdListForVerification: Set<String>,
|
||||
)
|
||||
|
||||
data class Version(
|
||||
val major: Int,
|
||||
val minor: Int,
|
||||
val patch: Int,
|
||||
) {
|
||||
constructor(version: String) : this(version.split('.').map(String::toInt))
|
||||
private constructor(parts: List<Int>) : this(parts[0], parts[1], parts[2])
|
||||
|
||||
operator fun compareTo(other: Version): Int {
|
||||
if (major != other.major) return major - other.major
|
||||
if (minor != other.minor) return minor - other.minor
|
||||
return patch - other.patch
|
||||
}
|
||||
|
||||
override fun toString() = "$major.$minor.$patch"
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import net.typeblog.lpac_jni.LocalProfileAssistant
|
|||
import net.typeblog.lpac_jni.LocalProfileInfo
|
||||
import net.typeblog.lpac_jni.LocalProfileNotification
|
||||
import net.typeblog.lpac_jni.ProfileDownloadCallback
|
||||
import net.typeblog.lpac_jni.Version
|
||||
|
||||
class LocalProfileAssistantImpl(
|
||||
isdrAid: ByteArray,
|
||||
|
@ -84,8 +85,8 @@ class LocalProfileAssistantImpl(
|
|||
throw IllegalArgumentException("Failed to initialize LPA")
|
||||
}
|
||||
|
||||
val pkids = euiccInfo2?.euiccCiPKIdListForVerification ?: arrayOf()
|
||||
httpInterface.usePublicKeyIds(pkids)
|
||||
val pkids = euiccInfo2?.euiccCiPKIdListForVerification ?: setOf()
|
||||
httpInterface.usePublicKeyIds(pkids.toTypedArray())
|
||||
}
|
||||
|
||||
override fun setEs10xMss(mss: Byte) {
|
||||
|
@ -157,31 +158,29 @@ class LocalProfileAssistantImpl(
|
|||
val cInfo = LpacJni.es10cexGetEuiccInfo2(contextHandle)
|
||||
if (cInfo == 0L) return null
|
||||
|
||||
val euiccCiPKIdListForSigning = mutableListOf<String>()
|
||||
var curr = LpacJni.euiccInfo2GetEuiccCiPKIdListForSigning(cInfo)
|
||||
while (curr != 0L) {
|
||||
euiccCiPKIdListForSigning.add(LpacJni.stringDeref(curr))
|
||||
curr = LpacJni.stringArrNext(curr)
|
||||
}
|
||||
|
||||
val euiccCiPKIdListForVerification = mutableListOf<String>()
|
||||
curr = LpacJni.euiccInfo2GetEuiccCiPKIdListForVerification(cInfo)
|
||||
while (curr != 0L) {
|
||||
euiccCiPKIdListForVerification.add(LpacJni.stringDeref(curr))
|
||||
curr = LpacJni.stringArrNext(curr)
|
||||
}
|
||||
|
||||
val ret = EuiccInfo2(
|
||||
LpacJni.euiccInfo2GetSGP22Version(cInfo),
|
||||
LpacJni.euiccInfo2GetProfileVersion(cInfo),
|
||||
LpacJni.euiccInfo2GetEuiccFirmwareVersion(cInfo),
|
||||
LpacJni.euiccInfo2GetGlobalPlatformVersion(cInfo),
|
||||
Version(LpacJni.euiccInfo2GetSGP22Version(cInfo)),
|
||||
Version(LpacJni.euiccInfo2GetProfileVersion(cInfo)),
|
||||
Version(LpacJni.euiccInfo2GetEuiccFirmwareVersion(cInfo)),
|
||||
Version(LpacJni.euiccInfo2GetGlobalPlatformVersion(cInfo)),
|
||||
LpacJni.euiccInfo2GetSasAcreditationNumber(cInfo),
|
||||
LpacJni.euiccInfo2GetPpVersion(cInfo),
|
||||
Version(LpacJni.euiccInfo2GetPpVersion(cInfo)),
|
||||
LpacJni.euiccInfo2GetFreeNonVolatileMemory(cInfo).toInt(),
|
||||
LpacJni.euiccInfo2GetFreeVolatileMemory(cInfo).toInt(),
|
||||
euiccCiPKIdListForSigning.toTypedArray(),
|
||||
euiccCiPKIdListForVerification.toTypedArray()
|
||||
buildSet {
|
||||
var cursor = LpacJni.euiccInfo2GetEuiccCiPKIdListForSigning(cInfo)
|
||||
while (cursor != 0L) {
|
||||
add(LpacJni.stringDeref(cursor))
|
||||
cursor = LpacJni.stringArrNext(cursor)
|
||||
}
|
||||
},
|
||||
buildSet {
|
||||
var cursor = LpacJni.euiccInfo2GetEuiccCiPKIdListForVerification(cInfo)
|
||||
while (cursor != 0L) {
|
||||
add(LpacJni.stringDeref(cursor))
|
||||
cursor = LpacJni.stringArrNext(cursor)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
LpacJni.euiccInfo2Free(cInfo)
|
||||
|
|
|
@ -14,7 +14,7 @@ const val DEFAULT_PKID_GSMA_RSP2_ROOT_CI1 = "81370f5125d0b1d408d4c3b232e6d25e795
|
|||
|
||||
// List of GSMA Live CIs
|
||||
// https://www.gsma.com/solutions-and-impact/technologies/esim/gsma-root-ci/
|
||||
val PKID_GSMA_LIVE_CI = arrayOf(
|
||||
val PKID_GSMA_LIVE_CI = setOf(
|
||||
// GSMA RSP2 Root CI1 (SGP.22 v2+v3, CA: DigiCert)
|
||||
// https://euicc-manual.osmocom.org/docs/pki/ci/files/81370f.txt
|
||||
DEFAULT_PKID_GSMA_RSP2_ROOT_CI1,
|
||||
|
@ -25,7 +25,7 @@ val PKID_GSMA_LIVE_CI = arrayOf(
|
|||
|
||||
// SGP.26 v3.0, 2023-12-01
|
||||
// https://www.gsma.com/solutions-and-impact/technologies/esim/wp-content/uploads/2023/12/SGP.26-v3.0.pdf
|
||||
val PKID_GSMA_TEST_CI = arrayOf(
|
||||
val PKID_GSMA_TEST_CI = setOf(
|
||||
// Test CI (SGP.26, NIST P256)
|
||||
// https://euicc-manual.osmocom.org/docs/pki/ci/files/34eecf.txt
|
||||
"34eecf13156518d48d30bdf06853404d115f955d",
|
||||
|
|
Loading…
Add table
Reference in a new issue