diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py index 78c9ad99..389a8cb8 100644 --- a/pySim/gsm_r.py +++ b/pySim/gsm_r.py @@ -58,7 +58,11 @@ class FuncNTypeAdapter(Adapter): class EF_FN(LinFixedEF): """Section 7.2""" - + _test_decode = [ + ( "40315801000010ff01", + { "functional_number_and_type": { "functional_number": "04138510000001f", + "presentation_of_only_this_fn": True, "permanent_fn": True }, "list_number": 1 } ), + ] def __init__(self): super().__init__(fid='6ff1', sfid=None, name='EF.FN', desc='Functional numbers', rec_len=(9, 9)) @@ -147,7 +151,12 @@ NextTableType = Enum(Byte, decision=0xf0, predefined=0xf1, class EF_CallconfC(TransparentEF): """Section 7.3""" - + _test_de_encode = [ + ( "026121ffffffffffff1e000a040a010253600795792426f0", + { "pl_conf": 3, "conf_nr": "1612ffffffffffff", "max_rand": 30, "n_ack_max": 10, + "pl_ack": 1, "n_nested_max": 10, "train_emergency_gid": 1, "shunting_emergency_gid": 2, + "imei": "350670599742620f" } ), + ] def __init__(self): super().__init__(fid='6ff2', sfid=None, name='EF.CallconfC', size=(24, 24), desc='Call Configuration of emergency calls Configuration') @@ -180,7 +189,9 @@ class EF_CallconfI(LinFixedEF): class EF_Shunting(TransparentEF): """Section 7.6""" - + _test_de_encode = [ + ( "03f8ffffff000000", { "common_gid": 3, "shunting_gid": "f8ffffff000000" } ), + ] def __init__(self): super().__init__(fid='6ff4', sfid=None, name='EF.Shunting', desc='Shunting', size=(8, 8)) @@ -190,7 +201,18 @@ class EF_Shunting(TransparentEF): class EF_GsmrPLMN(LinFixedEF): """Section 7.7""" - + _test_de_encode = [ + ( "22f860f86f8d6f8e01", { "plmn": "228f06", "class_of_network": { + "supported": { "vbs": True, "vgcs": True, "emlpp": True, + "fn": True, "eirene": True }, "preference": 0 }, + "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e", + "ic_table_ref": "01" } ), + ( "22f810416f8d6f8e02", { "plmn": "228f01", "class_of_network": { + "supported": { "vbs": False, "vgcs": False, "emlpp": False, + "fn": True, "eirene": False }, "preference": 1 }, + "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e", + "ic_table_ref": "02" } ), + ] def __init__(self): super().__init__(fid='6ff5', sfid=None, name='EF.GsmrPLMN', desc='GSM-R network selection', rec_len=(9, 9)) @@ -204,7 +226,12 @@ class EF_GsmrPLMN(LinFixedEF): class EF_IC(LinFixedEF): """Section 7.8""" - + _test_de_encode = [ + ( "f06f8e40f10001", { "next_table_type": "decision", "id_of_next_table": "6f8e", + "ic_decision_value": "041f", "network_string_table_index": 1 } ), + ( "ffffffffffffff", { "next_table_type": "empty", "id_of_next_table": "ffff", + "ic_decision_value": "ffff", "network_string_table_index": 65535 } ), + ] def __init__(self): super().__init__(fid='6f8d', sfid=None, name='EF.IC', desc='International Code', rec_len=(7, 7)) @@ -216,7 +243,12 @@ class EF_IC(LinFixedEF): class EF_NW(LinFixedEF): """Section 7.9""" - + _test_de_encode = [ + ( "47534d2d52204348", "GSM-R CH" ), + ( "537769737347534d", "SwissGSM" ), + ( "47534d2d52204442", "GSM-R DB" ), + ( "47534d2d52524649", "GSM-RRFI" ), + ] def __init__(self): super().__init__(fid='6f80', sfid=None, name='EF.NW', desc='Network Name', rec_len=(8, 8)) @@ -225,8 +257,15 @@ class EF_NW(LinFixedEF): class EF_Switching(LinFixedEF): """Section 8.4""" - - def __init__(self, fid, name, desc): + _test_de_encode = [ + ( "f26f87f0ff00", { "next_table_type": "num_dial_digits", "id_of_next_table": "6f87", + "decision_value": "0fff", "string_table_index": 0 } ), + ( "f06f8ff1ff01", { "next_table_type": "decision", "id_of_next_table": "6f8f", + "decision_value": "1fff", "string_table_index": 1 } ), + ( "f16f89f5ff05", { "next_table_type": "predefined", "id_of_next_table": "6f89", + "decision_value": "5fff", "string_table_index": 5 } ), + ] + def __init__(self, fid='1234', name='Switching', desc=None): super().__init__(fid=fid, sfid=None, name=name, desc=desc, rec_len=(6, 6)) self._construct = Struct('next_table_type'/NextTableType, @@ -237,13 +276,17 @@ class EF_Switching(LinFixedEF): class EF_Predefined(LinFixedEF): """Section 8.5""" + _test_de_encode = [ + ( "f26f85", 1, { "next_table_type": "num_dial_digits", "id_of_next_table": "6f85" } ), + ( "f0ffc8", 2, { "predefined_value1": "0fff", "string_table_index1": 200 } ), + ] # header and other records have different structure. WTF !?! construct_first = Struct('next_table_type'/NextTableType, 'id_of_next_table'/HexAdapter(Bytes(2))) construct_others = Struct('predefined_value1'/BcdAdapter(Bytes(2)), 'string_table_index1'/Int8ub) - def __init__(self, fid, name, desc): + def __init__(self, fid='1234', name='Predefined', desc=None): super().__init__(fid=fid, sfid=None, name=name, desc=desc, rec_len=(3, 3)) @@ -263,8 +306,11 @@ class EF_Predefined(LinFixedEF): class EF_DialledVals(TransparentEF): """Section 8.6""" - - def __init__(self, fid, name, desc): + _test_de_encode = [ + ( "ffffff22", { "next_table_type": "empty", "id_of_next_table": "ffff", "dialed_digits": "22" } ), + ( "f16f8885", { "next_table_type": "predefined", "id_of_next_table": "6f88", "dialed_digits": "58" }), + ] + def __init__(self, fid='1234', name='DialledVals', desc=None): super().__init__(fid=fid, sfid=None, name=name, desc=desc, size=(4, 4)) self._construct = Struct('next_table_type'/NextTableType, 'id_of_next_table'/HexAdapter(Bytes(2)), diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index e180b28e..b8db5c96 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -574,6 +574,14 @@ SC_DO = DataObjectChoice('security_condition', 'Security Condition', # TS 102 221 Section 13.1 class EF_DIR(LinFixedEF): + # FIXME: re-encode failure when changing to _test_de_encode + _test_decode = [ + ( '61294f10a0000000871002ffffffff890709000050055553696d31730ea00c80011781025f608203454150', + { "application_template": [ { "application_id": h2b("a0000000871002ffffffff8907090000") }, + { "application_label": "USim1" }, + { "discretionary_template": h2b("a00c80011781025f608203454150") } ] } + ), + ] class ApplicationLabel(BER_TLV_IE, tag=0x50): # TODO: UCS-2 coding option as per Annex A of TS 102 221 _construct = GreedyString('ascii') @@ -593,6 +601,9 @@ class EF_DIR(LinFixedEF): # TS 102 221 Section 13.2 class EF_ICCID(TransparentEF): + _test_de_encode = [ + ( '988812010000400310f0', { "iccid": "8988211000000430010" } ), + ] def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=(10, 10)) @@ -604,6 +615,11 @@ class EF_ICCID(TransparentEF): # TS 102 221 Section 13.3 class EF_PL(TransRecEF): + _test_de_encode = [ + ( '6465', "de" ), + ( '656e', "en" ), + ( 'ffff', None ), + ] def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'): super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=2, size=(2, None)) @@ -623,6 +639,28 @@ class EF_PL(TransRecEF): # TS 102 221 Section 13.4 class EF_ARR(LinFixedEF): + _test_de_encode = [ + ( '800101a40683010a950108800106900080016097008401d4a40683010a950108', + [ [ { "access_mode": [ "read_search_compare" ] }, + { "control_reference_template": "ADM1" } ], + [ { "access_mode": [ "write_append", "update_erase" ] }, + { "always": None } ], + [ { "access_mode": [ "delete_file", "terminate_ef" ] }, + { "never": None } ], + [ { "command_header": { "INS": 212 } }, + { "control_reference_template": "ADM1" } ] + ] ), + ( '80010190008001029700800118a40683010a9501088401d4a40683010a950108', + [ [ { "access_mode": [ "read_search_compare" ] }, + { "always": None } ], + [ { "access_mode": [ "update_erase" ] }, + { "never": None } ], + [ { "access_mode": [ "activate_file_or_record", "deactivate_file_or_record" ] }, + { "control_reference_template": "ADM1" } ], + [ { "command_header": { "INS": 212 } }, + { "control_reference_template": "ADM1" } ] + ] ), + ] def __init__(self, fid='2f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'): super().__init__(fid, sfid=sfid, name=name, desc=desc) # add those commands to the general commands of a TransparentEF @@ -705,6 +743,10 @@ class EF_ARR(LinFixedEF): # TS 102 221 Section 13.6 class EF_UMPC(TransparentEF): + _test_de_encode = [ + ( '3cff02', { "max_current_mA": 60, "t_op_s": 255, + "addl_info": { "req_inc_idle_current": False, "support_uicc_suspend": True } } ), + ] def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=(5, 5)) addl_info = FlagsEnum(Byte, req_inc_idle_current=1, diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py index 4125f4ad..3edc6e3a 100644 --- a/pySim/ts_31_102.py +++ b/pySim/ts_31_102.py @@ -539,6 +539,7 @@ class EF_Keys(TransparentEF): # TS 31.102 Section 4.2.6 class EF_HPPLMN(TransparentEF): + _test_de_encode = [ ( '05', 5 ) ] def __init__(self, fid='6f31', sfid=0x12, name='EF.HPPLMN', size=(1, 1), desc='Higher Priority PLMN search period'): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) @@ -595,7 +596,17 @@ class EF_UST(EF_UServiceTable): # TS 31.103 Section 4.2.7 - *not* the same as DF.GSM/EF.ECC! class EF_ECC(LinFixedEF): - cc_construct = Rpad(BcdAdapter(Rpad(Bytes(3))), pattern='f') + _test_de_encode = [ + ( '19f1ff01', { "call_code": "911f", + "service_category": { "police": True, "ambulance": False, "fire_brigade": False, + "marine_guard": False, "mountain_rescue": False, + "manual_ecall": False, "automatic_ecall": False } } ), + ( '19f3ff02', { "call_code": "913f", + "service_category": { "police": False, "ambulance": True, "fire_brigade": False, + "marine_guard": False, "mountain_rescue": False, + "manual_ecall": False, "automatic_ecall": False } } ), + ] + cc_construct = BcdAdapter(Rpad(Bytes(3))) category_construct = FlagsEnum(Byte, police=1, ambulance=2, fire_brigade=3, marine_guard=4, mountain_rescue=5, manual_ecall=6, automatic_ecall=7) alpha_construct = GsmStringAdapter(Rpad(GreedyBytes)) @@ -633,12 +644,28 @@ class EF_ECC(LinFixedEF): # TS 31.102 Section 4.2.17 class EF_LOCI(TransparentEF): + _test_de_encode = [ + ( '47d1264a62f21037211e00', + { "tmsi": "47d1264a", "lai": { "mcc_mnc": "262f01", "lac": "3721" }, + "rfu": 30, "lu_status": 0 } ), + ] def __init__(self, fid='6f7e', sfid=0x0b, name='EF.LOCI', desc='Location information', size=(11, 11)): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) Lai = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'lac'/HexAdapter(Bytes(2))) self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/Lai, 'rfu'/Int8ub, 'lu_status'/Int8ub) + # TS 31.102 Section 4.2.18 class EF_AD(TransparentEF): + _test_de_encode = [ + ( '00000002', { "ms_operation_mode": "normal", + "additional_info": { "ciphering_indicator": False, "csg_display_control": False, + "prose_services": False, "extended_drx": False }, + "rfu": 0, "mnc_len": 2, "extensions": b'' } ), + ( '01000102', { "ms_operation_mode": "normal_and_specific_facilities", + "additional_info": { "ciphering_indicator": True, "csg_display_control": False, + "prose_services": False, "extended_drx": False }, + "rfu": 0, "mnc_len": 2, "extensions": b'' } ), + ] class OP_MODE(enum.IntEnum): normal = 0x00 type_approval = 0x80 @@ -739,6 +766,9 @@ class EF_ACL(TransparentEF): # TS 31.102 Section 4.2.51 class EF_START_HFN(TransparentEF): + _test_de_encode = [ + ( 'f00000f00000', { "start_cs": 15728640, "start_ps": 15728640 } ), + ] def __init__(self, fid='6f5b', sfid=0x0f, name='EF.START-HFN', size=(6, 6), desc='Initialisation values for Hyperframe number', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -746,6 +776,9 @@ class EF_START_HFN(TransparentEF): # TS 31.102 Section 4.2.52 class EF_THRESHOLD(TransparentEF): + _test_de_encode = [ + ( 'f01000', { "max_start": 15732736 } ), + ] def __init__(self, fid='6f5c', sfid=0x10, name='EF.THRESHOLD', size=(3, 3), desc='Maximum value of START', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -820,6 +853,9 @@ class EF_GBANL(LinFixedEF): # TS 31.102 Section 4.2.85 class EF_EHPLMNPI(TransparentEF): + _test_de_encode = [ + ( '02', { "presentation_ind": "display_all" } ), + ] def __init__(self, fid='6fdb', sfid=None, name='EF.EHPLMNPI', size=(1, 1), desc='Equivalent HPLMN Presentation Indication', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -905,6 +941,10 @@ class EF_EPSNSC(LinFixedEF): # TS 31.102 Section 4.2.96 class EF_PWS(TransparentEF): + _test_de_encode = [ + ( '00', { "pws_configuration": { "ignore_pws_in_hplmn_and_equivalent": False, + "ignore_pws_in_vplmn": False } } ), + ] def __init__(self, fid='6fec', sfid=None, name='EF.PWS', desc='Public Warning System', size=(1, 1), **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) pws_config = FlagsEnum( @@ -973,6 +1013,10 @@ class EF_5GS3GPPLOCI(TransparentEF): # TS 31.102 Section 4.4.11.7 class EF_UAC_AIC(TransparentEF): + _test_de_encode = [ + ( '03', { "uac_access_id_config": { "multimedia_priority_service": True, + "mission_critical_service": True } } ), + ] def __init__(self, fid='4f06', sfid=0x06, name='EF.UAC_AIC', size=(4, 4), desc='UAC Access Identities Configuration', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) diff --git a/pySim/ts_31_102_telecom.py b/pySim/ts_31_102_telecom.py index 06317b9f..a0098c72 100644 --- a/pySim/ts_31_102_telecom.py +++ b/pySim/ts_31_102_telecom.py @@ -124,6 +124,7 @@ class EF_UServiceTable(TransparentEF): # TS 31.102 Section 4.4.2.1 class EF_PBR(LinFixedEF): + # TODO: a80ac0034f3a02c5034f0904aa0acb034f3d07c2034f4a06 def __init__(self, fid='4F30', name='EF.PBR', desc='Phone Book Reference', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) #self._tlv = FIXME diff --git a/pySim/ts_31_103.py b/pySim/ts_31_103.py index ee50ddba..4a66c0db 100644 --- a/pySim/ts_31_103.py +++ b/pySim/ts_31_103.py @@ -80,6 +80,11 @@ EF_ISIM_ADF_map = { # TS 31.103 Section 4.2.2 class EF_IMPI(TransparentEF): + # FIXME: re-encode fails with "string encoding failed, expected unicode string" + _test_decode = [ + ( '803137333830303630303030303031303140696d732e6d6e633030302e6d63633733382e336770706e6574776f726b2e6f7267', + { "nai": "738006000000101@ims.mnc000.mcc738.3gppnetwork.org" } ), + ] class nai(BER_TLV_IE, tag=0x80): _construct = GreedyString("utf8") @@ -89,6 +94,11 @@ class EF_IMPI(TransparentEF): # TS 31.103 Section 4.2.3 class EF_DOMAIN(TransparentEF): + # FIXME: re-encode fails with "string encoding failed, expected unicode string" + _test_decode = [ + ( '8021696d732e6d6e633030302e6d63633733382e336770706e6574776f726b2e6f7267', + { "domain": "ims.mnc000.mcc738.3gppnetwork.org" } ), + ] class domain(BER_TLV_IE, tag=0x80): _construct = GreedyString("utf8") @@ -98,6 +108,11 @@ class EF_DOMAIN(TransparentEF): # TS 31.103 Section 4.2.4 class EF_IMPU(LinFixedEF): + # FIXME: re-encode fails with "string encoding failed, expected unicode string" + _test_decode = [ + ( '80357369703a37333830303630303030303031303140696d732e6d6e633030302e6d63633733382e336770706e6574776f726b2e6f7267', + { "impu": "sip:738006000000101@ims.mnc000.mcc738.3gppnetwork.org" } ), + ] class impu(BER_TLV_IE, tag=0x80): _construct = GreedyString("utf8") @@ -139,6 +154,10 @@ class EF_IST(EF_UServiceTable): # TS 31.103 Section 4.2.8 class EF_PCSCF(LinFixedEF): + _test_de_encode = [ + ( '802c0070637363662e696d732e6d6e633030302e6d63633733382e7075622e336770706e6574776f726b2e6f7267', + { "addr": "pcscf.ims.mnc000.mcc738.pub.3gppnetwork.org", "addr_type": "00" } ), + ] def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address', **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) @@ -163,6 +182,8 @@ class EF_GBANL(LinFixedEF): # TS 31.103 Section 4.2.11 class EF_NAFKCA(LinFixedEF): + # TODO: 80296273662e696d732e6d6e633030302e6d63633733382e7075622e336770706e6574776f726b2e6f7267ffffffffffffff + # TODO: 8030656e65746e61667830312e696d732e6d6e633030302e6d63633733382e7075622e336770706e6574776f726b2e6f7267 def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address', **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 80182af2..96de2412 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -358,6 +358,14 @@ class ExtendedBcdAdapter(Adapter): # TS 51.011 Section 10.5.1 class EF_ADN(LinFixedEF): + _test_decode = [ + ( '42204841203120536963ffffffff06810628560810ffffffffffffff', + { "alpha_id": "B HA 1 Sic", "len_of_bcd": 6, "ton_npi": { "ext": True, "type_of_number": + "unknown", "numbering_plan_id": + "isdn_e164" }, "dialing_nr": + "6082658001", "cap_conf_id": 255, "ext1_record_id": 255 }), + ] + def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers', ext=1, **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=(14, 30), **kwargs) ext_name = 'ext%u_record_id' % ext @@ -419,6 +427,19 @@ class EF_MSISDN(LinFixedEF): # TS 51.011 Section 10.5.6 class EF_SMSP(LinFixedEF): + # FIXME: re-encode fails / missing alpha_id at start of output + _test_decode = [ + ( '454e6574776f726b73fffffffffffffff1ffffffffffffffffffffffffffffffffffffffffffffffff0000a7', + { "alpha_id": "ENetworks", "parameter_indicators": { "tp_dest_addr": False, "tp_sc_addr": True, + "tp_pid": True, "tp_dcs": True, "tp_vp": True }, + "tp_dest_addr": { "length": 255, "ton_npi": { "ext": True, "type_of_number": "reserved_for_extension", + "numbering_plan_id": "reserved_for_extension" }, + "call_number": "" }, + "tp_sc_addr": { "length": 255, "ton_npi": { "ext": True, "type_of_number": "reserved_for_extension", + "numbering_plan_id": "reserved_for_extension" }, + "call_number": "" }, + "tp_pid": "00", "tp_dcs": "00", "tp_vp_minutes": 1440 } ), + ] class ValidityPeriodAdapter(Adapter): def _decode(self, obj, context, path): if obj <= 143: @@ -530,6 +551,9 @@ class DF_TELECOM(CardDF): # TS 51.011 Section 10.3.1 class EF_LP(TransRecEF): + _test_de_encode = [ + ( "24", "24"), + ] def __init__(self, fid='6f05', sfid=None, name='EF.LP', size=(1, None), rec_len=1, desc='Language Preference'): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len) @@ -542,6 +566,10 @@ class EF_LP(TransRecEF): # TS 51.011 Section 10.3.2 class EF_IMSI(TransparentEF): + _test_de_encode = [ + ( "082982608200002080", { "imsi": "228062800000208" } ), + ( "082926101160845740", { "imsi": "262011106487504" } ), + ] def __init__(self, fid='6f07', sfid=None, name='EF.IMSI', desc='IMSI', size=(9, 9)): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) # add those commands to the general commands of a TransparentEF @@ -580,6 +608,10 @@ class EF_IMSI(TransparentEF): # TS 51.011 Section 10.3.4 class EF_PLMNsel(TransRecEF): + _test_de_encode = [ + ( "22F860", { "mcc": "228", "mnc": "06" } ), + ( "330420", { "mcc": "334", "mnc": "020" } ), + ] def __init__(self, fid='6f30', sfid=None, name='EF.PLMNsel', desc='PLMN selector', size=(24, None), rec_len=3, **kwargs): super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len, **kwargs) @@ -598,6 +630,9 @@ class EF_PLMNsel(TransRecEF): # TS 51.011 Section 10.3.6 class EF_ACMmax(TransparentEF): + _test_de_encode = [ + ( "000000", { "acm_max": 0 } ), + ] def __init__(self, fid='6f37', sfid=None, name='EF.ACMmax', size=(3, 3), desc='ACM maximum value', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -655,6 +690,11 @@ class EF_ServiceTable(TransparentEF): # TS 51.011 Section 10.3.11 class EF_SPN(TransparentEF): + _test_de_encode = [ + ( "0147534d2d52204348ffffffffffffffff", + { "rfu": 0, "hide_in_oplmn": False, "show_in_hplmn": True, "spn": "GSM-R CH" } ), + ] + def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size=(17, 17), **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -669,6 +709,7 @@ class EF_SPN(TransparentEF): # TS 51.011 Section 10.3.13 class EF_CBMI(TransRecEF): + # TODO: Test vectors def __init__(self, fid='6f45', sfid=None, name='EF.CBMI', size=(2, None), rec_len=2, desc='Cell Broadcast message identifier selection', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) @@ -676,6 +717,9 @@ class EF_CBMI(TransRecEF): # TS 51.011 Section 10.3.15 class EF_ACC(TransparentEF): + _test_de_encode = [ + ( "0100", "0100" ), + ] def __init__(self, fid='6f78', sfid=None, name='EF.ACC', desc='Access Control Class', size=(2, 2), **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -683,6 +727,10 @@ class EF_ACC(TransparentEF): # TS 51.011 Section 10.3.16 class EF_LOCI(TransparentEF): + _test_de_encode = [ + ( "7802570222f81009780000", + { "tmsi": "78025702", "lai": "22f8100978", "tmsi_time": 0, "lu_status": "updated" } ), + ] def __init__(self, fid='6f7e', sfid=None, name='EF.LOCI', desc='Location Information', size=(11, 11)): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/HexAdapter(Bytes(5)), 'tmsi_time'/Int8ub, @@ -691,6 +739,10 @@ class EF_LOCI(TransparentEF): # TS 51.011 Section 10.3.18 class EF_AD(TransparentEF): + _test_de_encode = [ + ( "00ffff", + { "ms_operation_mode": "normal", "rfu1": 255, "rfu2": 127, "ofm": True, "extensions": None } ), + ] class OP_MODE(enum.IntEnum): normal = 0x00 type_approval = 0x80 @@ -722,6 +774,9 @@ class EF_AD(TransparentEF): # TS 51.011 Section 10.3.20 / 10.3.22 class EF_VGCS(TransRecEF): + _test_de_encode = [ + ( "92f9ffff", "299fffff" ), + ] def __init__(self, fid='6fb1', sfid=None, name='EF.VGCS', size=(4, 200), rec_len=4, desc='Voice Group Call Service', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) @@ -729,6 +784,12 @@ class EF_VGCS(TransRecEF): # TS 51.011 Section 10.3.21 / 10.3.23 class EF_VGCSS(TransparentEF): + _test_decode = [ + ( "010000004540fc", + { "flags": [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ] } + ), + ] def __init__(self, fid='6fb2', sfid=None, name='EF.VGCSS', size=(7, 7), desc='Voice Group Call Service Status', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -737,6 +798,13 @@ class EF_VGCSS(TransparentEF): # TS 51.011 Section 10.3.24 class EF_eMLPP(TransparentEF): + _test_de_encode = [ + ( "7c04", { "levels": { "A": False, "B": False, "zero": True, "one": True, + "two": True, "three": True, "four": True }, + "fast_call_setup_cond": { "A": False, "B": False, "zero": True, "one": False, + "two": False, "three": False, "four": False } + }), + ] def __init__(self, fid='6fb5', sfid=None, name='EF.eMLPP', size=(2, 2), desc='enhanced Multi Level Pre-emption and Priority', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -747,6 +815,10 @@ class EF_eMLPP(TransparentEF): # TS 51.011 Section 10.3.25 class EF_AAeM(TransparentEF): + _test_de_encode = [ + ( "3c", { "auto_answer_prio_levels": { "A": False, "B": False, "zero": True, "one": True, + "two": True, "three": True, "four": False } } ), + ] def __init__(self, fid='6fb6', sfid=None, name='EF.AAeM', size=(1, 1), desc='Automatic Answer for eMLPP Service', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) @@ -816,12 +888,19 @@ class EF_NIA(LinFixedEF): # TS 51.011 Section 10.3.32 class EF_Kc(TransparentEF): + _test_de_encode = [ + ( "837d783609a3858f05", { "kc": "837d783609a3858f", "cksn": 5 } ), + ] def __init__(self, fid='6f20', sfid=None, name='EF.Kc', desc='Ciphering key Kc', size=(9, 9), **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) self._construct = Struct('kc'/HexAdapter(Bytes(8)), 'cksn'/Int8ub) # TS 51.011 Section 10.3.33 class EF_LOCIGPRS(TransparentEF): + _test_de_encode = [ + ( "ffffffffffffff22f8990000ff01", + { "ptmsi": "ffffffff", "ptmsi_sig": "ffffff", "rai": "22f8990000ff", "rau_status": "not_updated" } ), + ] def __init__(self, fid='6f53', sfid=None, name='EF.LOCIGPRS', desc='GPRS Location Information', size=(14, 14)): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) self._construct = Struct('ptmsi'/HexAdapter(Bytes(4)), 'ptmsi_sig'/HexAdapter(Bytes(3)), @@ -831,7 +910,11 @@ class EF_LOCIGPRS(TransparentEF): # TS 51.011 Section 10.3.35..37 class EF_xPLMNwAcT(TransRecEF): - def __init__(self, fid, sfid=None, name=None, desc=None, size=(40, None), rec_len=5, **kwargs): + _test_de_encode = [ + ( '62F2104000', { "mcc": "262", "mnc": "01", "act": [ "E-UTRAN" ] } ), + ( '62F2108000', { "mcc": "262", "mnc": "01", "act": [ "UTRAN" ] } ), + ] + def __init__(self, fid='1234', sfid=None, name=None, desc=None, size=(40, None), rec_len=5, **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) def _decode_record_hex(self, in_hex, **kwargs): @@ -895,6 +978,8 @@ class EF_InvScan(TransparentEF): # TS 51.011 Section 4.2.58 class EF_PNN(LinFixedEF): + # TODO: 430a82d432bbbc7eb75de432450a82d432bbbc7eb75de432ffffffff + # TODO: 430a82c596b34cbfbfe5eb39ffffffffffffffffffffffffffffffffffff class FullNameForNetwork(BER_TLV_IE, tag=0x43): # TS 24.008 10.5.3.5a # TODO: proper decode @@ -914,6 +999,10 @@ class EF_PNN(LinFixedEF): # TS 51.011 Section 10.3.42 class EF_OPL(LinFixedEF): + _test_de_encode = [ + ( '62f2100000fffe01', + { "lai": { "mcc_mnc": "262f01", "lac_min": "0000", "lac_max": "fffe" }, "pnn_record_id": 1 } ), + ] def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len=(8, 8), desc='Operator PLMN List', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs) self._construct = Struct('lai'/Struct('mcc_mnc'/BcdAdapter(Bytes(3)), @@ -921,6 +1010,10 @@ class EF_OPL(LinFixedEF): # TS 51.011 Section 10.3.44 + TS 31.102 4.2.62 class EF_MBI(LinFixedEF): + _test_de_encode = [ + ( '0100000000', + { "mbi_voicemail": 1, "mbi_fax": 0, "mbi_email": 0, "mbi_other": 0, "mbi_videocall": 0 } ), + ] def __init__(self, fid='6fc9', sfid=None, name='EF.MBI', rec_len=(4, 5), desc='Mailbox Identifier', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs) self._construct = Struct('mbi_voicemail'/Int8ub, 'mbi_fax'/Int8ub, 'mbi_email'/Int8ub, @@ -938,6 +1031,7 @@ class EF_MWIS(LinFixedEF): # TS 51.011 Section 10.3.66 class EF_SPDI(TransparentEF): + # TODO: a305800337f800ffffffffffffffffffffffffffffffffffffffffffffff class ServiceProviderPLMN(BER_TLV_IE, tag=0x80): # flexible numbers of 3-byte PLMN records _construct = GreedyRange(BcdAdapter(Bytes(3))) diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100755 index 00000000..3fb10620 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 + +# (C) 2023 by Harald Welte +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import unittest +import logging + +from pySim.utils import * +from pySim.filesystem import * + +import pySim.iso7816_4 +import pySim.ts_102_221 +import pySim.ts_102_222 +import pySim.ts_31_102 +import pySim.ts_31_103 +import pySim.ts_51_011 +import pySim.sysmocom_sja2 +import pySim.gsm_r + +def get_qualified_name(c): + """return the qualified (by module) name of a class.""" + return "%s.%s" % (c.__module__, c.__name__) + +class LinFixed_Test(unittest.TestCase): + classes = all_subclasses(LinFixedEF) + + def test_decode_record(self): + """Test the decoder for a linear-fixed EF. Requires the given LinFixedEF subclass + to have an '_test_decode' attribute, containing a list of tuples. Each tuple can + either be a + * 2-tuple (hexstring, decoded_dict) or a + * 3-tuple (hexstring, record_nr, decoded_dict) + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_decode'): + for t in c._test_decode: + with self.subTest(name, test_decode=t): + inst = c() + if len(t) == 2: + encoded = t[0] + rec_num = 1 + decoded = t[1] + else: + encoded = t[0] + rec_num = t[1] + decoded = t[2] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_record_hex(encoded, rec_num) + self.assertEqual(decoded, re_dec) + + def test_encode_record(self): + """Test the encoder for a linear-fixed EF. Requires the given LinFixedEF subclass + to have an '_test_encode' attribute, containing a list of tuples. Each tuple can + either be a + * 2-tuple (hexstring, decoded_dict) or a + * 3-tuple (hexstring, record_nr, decoded_dict) + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_encode'): + for t in c._test_encode: + with self.subTest(name, test_encode=t): + inst = c() + if len(t) == 2: + encoded = t[0] + rec_num = 1 + decoded = t[1] + else: + encoded = t[0] + rec_num = t[1] + decoded = t[2] + logging.debug("Testing encode of %s", name) + re_enc = inst.encode_record_hex(decoded, rec_num) + self.assertEqual(encoded, re_enc) + + def test_de_encode_record(self): + """Test the decoder and encoder for a linear-fixed EF. Performs first a decoder + test, and then re-encodes the decoded data, comparing the re-encoded data with the + initial input data. + + Requires the given LinFixedEF subclass to have a '_test_de_encode' attribute, + containing a list of tuples. Each tuple can + either be a + * 2-tuple (hexstring, decoded_dict) or a + * 3-tuple (hexstring, record_nr, decoded_dict) + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_de_encode'): + for t in c._test_de_encode: + with self.subTest(name, test_de_encode=t): + inst = c() + if len(t) == 2: + encoded = t[0] + rec_num = 1 + decoded = t[1] + else: + encoded = t[0] + rec_num = t[1] + decoded = t[2] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_record_hex(encoded, rec_num) + self.assertEqual(decoded, re_dec) + # re-encode the decoded data + logging.debug("Testing re-encode of %s", name) + re_enc = inst.encode_record_hex(re_dec, rec_num) + self.assertEqual(encoded, re_enc) + + +class TransRecEF_Test(unittest.TestCase): + classes = all_subclasses(TransRecEF) + + def test_decode_record(self): + """Test the decoder for a transparent record-oriented EF. Requires the given TransRecEF subclass + to have an '_test_decode' attribute, containing a list of tuples. Each tuple has to be a + 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_decode'): + for t in c._test_decode: + with self.subTest(name, test_decode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_record_hex(encoded) + self.assertEqual(decoded, re_dec) + + def test_encode_record(self): + """Test the encoder for a transparent record-oriented EF. Requires the given TransRecEF subclass + to have an '_test_encode' attribute, containing a list of tuples. Each tuple has to be a + 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_decode'): + for t in c._test_decode: + with self.subTest(name, test_decode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_record_hex(encoded) + self.assertEqual(decoded, re_dec) + + + def test_de_encode_record(self): + """Test the decoder and encoder for a transparent record-oriented EF. Performs first a decoder + test, and then re-encodes the decoded data, comparing the re-encoded data with the + initial input data. + + Requires the given TransRecEF subclass to have a '_test_de_encode' attribute, + containing a list of tuples. Each tuple has to be a 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_de_encode'): + for t in c._test_de_encode: + with self.subTest(name, test_de_encode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_record_hex(encoded) + self.assertEqual(decoded, re_dec) + # re-encode the decoded data + logging.debug("Testing re-encode of %s", name) + re_enc = inst.encode_record_hex(re_dec) + self.assertEqual(encoded, re_enc) + + +class TransparentEF_Test(unittest.TestCase): + @classmethod + def get_classes(cls): + """get list of TransparentEF sub-classes which are not a TransRecEF subclass.""" + classes = all_subclasses(TransparentEF) + trans_rec_classes = all_subclasses(TransRecEF) + return filter(lambda c: c not in trans_rec_classes, classes) + + @classmethod + def setUpClass(cls): + """set-up method called once for this class by unittest framework""" + cls.classes = cls.get_classes() + + def test_decode_file(self): + """Test the decoder for a transparent EF. Requires the given TransparentEF subclass + to have a '_test_decode' attribute, containing a list of tuples. Each tuple + is a 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_decode'): + for t in c._test_decode: + with self.subTest(name, test_decode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_hex(encoded) + self.assertEqual(decoded, re_dec) + + def test_encode_file(self): + """Test the encoder for a transparent EF. Requires the given TransparentEF subclass + to have a '_test_encode' attribute, containing a list of tuples. Each tuple + is a 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_encode'): + for t in c._test_encode: + with self.subTest(name, test_encode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing encode of %s", name) + re_dec = inst.decode_hex(encoded) + self.assertEqual(decoded, re_dec) + + def test_de_encode_file(self): + """Test the decoder and encoder for a transparent EF. Performs first a decoder + test, and then re-encodes the decoded data, comparing the re-encoded data with the + initial input data. + + Requires the given TransparentEF subclass to have a '_test_de_encode' attribute, + containing a list of tuples. Each tuple is a 2-tuple (hexstring, decoded_dict). + """ + for c in self.classes: + name = get_qualified_name(c) + if hasattr(c, '_test_de_encode'): + for t in c._test_de_encode: + with self.subTest(name, test_de_encode=t): + inst = c() + encoded = t[0] + decoded = t[1] + logging.debug("Testing decode of %s", name) + re_dec = inst.decode_hex(encoded) + self.assertEqual(decoded, re_dec) + logging.debug("Testing re-encode of %s", name) + re_dec = inst.decode_hex(encoded) + re_enc = inst.encode_hex(re_dec) + self.assertEqual(encoded, re_enc) + + +if __name__ == '__main__': + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + unittest.main()