diff --git a/pySim-read.py b/pySim-read.py index bcdbca66..16607cfe 100755 --- a/pySim-read.py +++ b/pySim-read.py @@ -37,7 +37,7 @@ except ImportError: import simplejson as json from pySim.commands import SimCardCommands -from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid +from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, format_xplmn_w_act def parse_options(): @@ -129,7 +129,7 @@ if __name__ == '__main__': try: (res, sw) = scc.read_binary(EF['PLMNwAcT']) if sw == '9000': - print("PLMNwAcT: %s" % (res)) + print("PLMNwAcT:\n%s" % (format_xplmn_w_act(res))) else: print("PLMNwAcT: Can't read, response code = %s" % (sw,)) except Exception as e: @@ -139,7 +139,7 @@ if __name__ == '__main__': try: (res, sw) = scc.read_binary(EF['OPLMNwAcT']) if sw == '9000': - print("OPLMNwAcT: %s" % (res)) + print("OPLMNwAcT:\n%s" % (format_xplmn_w_act(res))) else: print("OPLMNwAcT: Can't read, response code = %s" % (sw,)) except Exception as e: @@ -149,7 +149,7 @@ if __name__ == '__main__': try: (res, sw) = scc.read_binary(EF['HPLMNAcT']) if sw == '9000': - print("HPLMNAcT: %s" % (res)) + print("HPLMNAcT:\n%s" % (format_xplmn_w_act(res))) else: print("HPLMNAcT: Can't read, response code = %s" % (sw,)) except Exception as e: diff --git a/pySim/utils.py b/pySim/utils.py index ba947028..65f10c5c 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -113,6 +113,79 @@ def enc_spn(name, hplmn_disp=False, oplmn_disp=False): if oplmn_disp: byte1 = byte1|0x02 return i2h([byte1])+s2h(name) +def hexstr_to_fivebytearr(s): + return [s[i:i+10] for i in range(0, len(s), 10) ] + +# Accepts hex string representing three bytes +def dec_mcc_from_plmn(plmn): + ia = h2i(plmn) + digit1 = ia[0] & 0x0F # 1st byte, LSB + digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB + digit3 = ia[1] & 0x0F # 2nd byte, LSB + if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF: + return 0xFFF # 4095 + mcc = digit1 * 100 + mcc += digit2 * 10 + mcc += digit3 + return mcc + +def dec_mnc_from_plmn(plmn): + ia = h2i(plmn) + digit1 = ia[2] & 0x0F # 3rd byte, LSB + digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB + digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB + if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF: + return 0xFFF # 4095 + mnc = 0 + # signifies two digit MNC + if digit3 == 0xF: + mnc += digit1 * 10 + mnc += digit2 + else: + mnc += digit1 * 100 + mnc += digit2 * 10 + mnc += digit3 + return mnc + +def dec_act(twohexbytes): + act_list = [ + {'bit': 15, 'name': "UTRAN"}, + {'bit': 14, 'name': "E-UTRAN"}, + {'bit': 7, 'name': "GSM"}, + {'bit': 6, 'name': "GSM COMPACT"}, + {'bit': 5, 'name': "cdma2000 HRPD"}, + {'bit': 4, 'name': "cdma2000 1xRTT"}, + ] + ia = h2i(twohexbytes) + u16t = (ia[0] << 8)|ia[1] + sel = [] + for a in act_list: + if u16t & (1 << a['bit']): + sel.append(a['name']) + return sel + +def dec_xplmn_w_act(fivehexbytes): + res = {'mcc': 0, 'mnc': 0, 'act': []} + plmn_chars = 6 + act_chars = 4 + plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars) + act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes + res['mcc'] = dec_mcc_from_plmn(plmn_str) + res['mnc'] = dec_mnc_from_plmn(plmn_str) + res['act'] = dec_act(act_str) + return res + +def format_xplmn_w_act(hexstr): + s = "" + for rec_data in hexstr_to_fivebytearr(hexstr): + rec_info = dec_xplmn_w_act(rec_data) + if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF: + rec_str = "unused" + else: + rec_str = "MCC: %3s MNC: %3s AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act'])) + s += "\t%s # %s\n" % (rec_data, rec_str) + return s + def derive_milenage_opc(ki_hex, op_hex): """ Run the milenage algorithm to calculate OPC from Ki and OP diff --git a/pySim/utils_test.py b/pySim/utils_test.py new file mode 100644 index 00000000..ff028dad --- /dev/null +++ b/pySim/utils_test.py @@ -0,0 +1,76 @@ +#!/usr/bin/pyton + +import unittest +import utils + +class DecTestCase(unittest.TestCase): + + def testSplitHexStringToListOf5ByteEntries(self): + input_str = "ffffff0003ffffff0002ffffff0001" + expected = [ + "ffffff0003", + "ffffff0002", + "ffffff0001", + ] + self.assertEqual(utils.hexstr_to_fivebytearr(input_str), expected) + + def testDecMCCfromPLMN(self): + self.assertEqual(utils.dec_mcc_from_plmn("92f501"), 295) + + def testDecMCCfromPLMN_unused(self): + self.assertEqual(utils.dec_mcc_from_plmn("ff0f00"), 4095) + + def testDecMNCfromPLMN_twoDigitMNC(self): + self.assertEqual(utils.dec_mnc_from_plmn("92f501"), 10) + + def testDecMNCfromPLMN_threeDigitMNC(self): + self.assertEqual(utils.dec_mnc_from_plmn("031263"), 361) + + def testDecMNCfromPLMN_unused(self): + self.assertEqual(utils.dec_mnc_from_plmn("00f0ff"), 4095) + + def testDecAct_noneSet(self): + self.assertEqual(utils.dec_act("0000"), []) + + def testDecAct_onlyUtran(self): + self.assertEqual(utils.dec_act("8000"), ["UTRAN"]) + + def testDecAct_onlyEUtran(self): + self.assertEqual(utils.dec_act("4000"), ["E-UTRAN"]) + + def testDecAct_onlyGsm(self): + self.assertEqual(utils.dec_act("0080"), ["GSM"]) + + def testDecAct_onlyGsmCompact(self): + self.assertEqual(utils.dec_act("0040"), ["GSM COMPACT"]) + + def testDecAct_onlyCdma2000HRPD(self): + self.assertEqual(utils.dec_act("0020"), ["cdma2000 HRPD"]) + + def testDecAct_onlyCdma20001xRTT(self): + self.assertEqual(utils.dec_act("0010"), ["cdma2000 1xRTT"]) + + def testDecAct_allSet(self): + self.assertEqual(utils.dec_act("ffff"), ["UTRAN", "E-UTRAN", "GSM", "GSM COMPACT", "cdma2000 HRPD", "cdma2000 1xRTT"]) + + def testDecxPlmn_w_act(self): + expected = {'mcc': 295, 'mnc': 10, 'act': ["UTRAN"]} + self.assertEqual(utils.dec_xplmn_w_act("92f5018000"), expected) + + def testFormatxPlmn_w_act(self): + input_str = "92f501800092f5508000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000" + expected = '''92f5018000 # MCC: 295 MNC: 10 AcT: UTRAN +92f5508000 # MCC: 295 MNC: 5 AcT: UTRAN +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +ffffff0000 # unused +''' + self.assertEqual(utils.format_xplmn_w_act(input_str), expected) + +if __name__ == "__main__": + unittest.main() diff --git a/pysim-testdata/sysmoUSIM-SJS1.ok b/pysim-testdata/sysmoUSIM-SJS1.ok index 0b799343..275e79e9 100644 --- a/pysim-testdata/sysmoUSIM-SJS1.ok +++ b/pysim-testdata/sysmoUSIM-SJS1.ok @@ -3,9 +3,48 @@ ICCID: 1122334455667788990 IMSI: 001010000000102 SMSP: ffffffffffffffffffffffffffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000 PLMNsel: fff11fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -PLMNwAcT: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -OPLMNwAcT: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -HPLMNAcT: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PLMNwAcT: + fff11fffff # MCC: 1651 MNC: 151 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + +OPLMNwAcT: + fff11fffff # MCC: 1651 MNC: 151 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + ffffff0000 # unused + +HPLMNAcT: + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ffffffffff # unused + ACC: 0008 MSISDN: Not available AD: 00000002