From 593bfa09119383d23b9e3385fc2d35a036a796c5 Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Thu, 23 Apr 2026 10:59:49 +0200 Subject: [PATCH] ts_51_011/EF.SMSP: fix handling of 'alpha_id' field The field 'alpha_id' is technically not an optional field, even though the specification describes it as optional. Once the card manufacturer decides that the field should be present, it must be always present and vice versa. (see code comment for a more detailed description) Related: SYS#7765 Change-Id: I0ec99b2648b22c56f9145345e4cd8776f9217701 --- pySim/ts_51_011.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 0dbf6eb9..89ff8248 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -251,6 +251,16 @@ class EF_SMSP(LinFixedEF): "numbering_plan_id": "isdn_e164" }, "call_number": "4915790109999" }, "tp_pid": b"\x00", "tp_dcs": b"\x00", "tp_vp_minutes": 4320 } ), + ( 'e1ffffffffffffffffffffffff0891945197109099f9ffffff0000a9', + { "alpha_id": "", "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": 8, "ton_npi": { "ext": True, "type_of_number": "international", + "numbering_plan_id": "isdn_e164" }, + "call_number": "4915790109999" }, + "tp_pid": b"\x00", "tp_dcs": b"\x00", "tp_vp_minutes": 4320 } ), ( '454e6574776f726b73fffffffffffffff1ffffffffffffffffffffffffffffffffffffffffffffffff0000a7', { "alpha_id": "ENetworks", "parameter_indicators": { "tp_dest_addr": False, "tp_sc_addr": True, "tp_pid": True, "tp_dcs": True, "tp_vp": False }, @@ -331,7 +341,8 @@ class EF_SMSP(LinFixedEF): 'ton_npi'/TonNpi, 'call_number'/PaddedBcdAdapter(Rpad(Bytes(10)))) DestAddr = Struct('length'/Rebuild(Int8ub, lambda ctx: EF_SMSP.dest_addr_len(ctx)), 'ton_npi'/TonNpi, 'call_number'/PaddedBcdAdapter(Rpad(Bytes(10)))) - self._construct = Struct('alpha_id'/COptional(GsmOrUcs2Adapter(Rpad(Bytes(this._.total_len-28)))), + # (see comment below) + self._construct = Struct('alpha_id'/GsmOrUcs2Adapter(Rpad(Bytes(this._.total_len-28))), 'parameter_indicators'/InvertAdapter(BitStruct( Const(7, BitsInteger(3)), 'tp_vp'/Flag, @@ -345,6 +356,25 @@ class EF_SMSP(LinFixedEF): 'tp_dcs'/Bytes(1), 'tp_vp_minutes'/EF_SMSP.ValidityPeriodAdapter(Byte)) + # Ensure 'alpha_id' is always present + def encode_record_hex(self, abstract_data: dict, record_nr: int, total_len: int = None) -> str: + # Problem: TS 51.011 Section 10.5.6 describes the 'alpha_id' field as optional. However, this is only true + # at the time when the record length of the file is set up in the file system. A card manufacturer may decide + # to remove the field by setting the record length to 28. Likewise, the card manaufacturer may also decide to + # set the field to a distinct length by setting the record length to a value greater than 28 (e.g. 14 bytes + # 'alpha_id' + 28 bytes). Due to the fixed nature of the record length, this eventually means that in practice + # 'alpha_id' is a mandatory field with a fixed length. + # + # Due to the problematic specification of 'alpha_id' as a pseudo-optional field at the beginning of a + # fixed-size memory, the construct definition in self._construct has been incorrectly implemented and the field + # has been marked as COptional. We may correct the problem by removing COptional. But to maintain compatibility, + # we then have to ensure that in case the field is not provided (None), it is set to an empty string (''). + # + # See also ts_31_102.py, class EF_OCI for a correct example. + if abstract_data['alpha_id'] is None: + abstract_data['alpha_id'] = '' + return super().encode_record_hex(abstract_data, record_nr, total_len) + # TS 51.011 Section 10.5.7 class EF_SMSS(TransparentEF): class MemCapAdapter(Adapter):