utils: fix encoding of EF.MSISDN

The encoding of EF.MSISDN is a bit unstrutured. The encoder function
does not return a valid result since it lacks the parameters
Capability/Configuration2 Record Identifier and Extension5 Record
Identifier, which are mandatory but can be set to 0xFF. Also the
encoder gets its input from pySim-shell, so it should have some
more input validation, especially when the user encodes an empty
string. The encoder and decoder function also do not have unit-tests.

Since the encoder now adds the missing two bytes by isself this does
not have to be done manually anymore, so cards.py needs to be
re-aligned.

For pySim-shell.py the encoder is used from ts_51_011.py. Unfortunately
it is used wrongly there. The optional Alpha Identifier is required
here as well.

Related: OS#4963
Change-Id: Iee5369b3e3ba7fa1155facc8fa824bc60e33b55b
This commit is contained in:
Philipp Maier 2021-04-20 22:38:21 +02:00
parent 977035c429
commit b46cb3ffa2
4 changed files with 46 additions and 6 deletions

View File

@ -963,7 +963,7 @@ class SysmoUSIMSJS1(UsimCard):
# TODO: Extension1 Record Identifier
if p.get('msisdn') is not None:
msisdn = enc_msisdn(p['msisdn'])
data = 'ff' * 20 + msisdn + 'ff' * 2
data = 'ff' * 20 + msisdn
r = self._scc.select_path(['3f00', '7f10'])
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
@ -1356,7 +1356,7 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
# TODO: Extension1 Record Identifier
if p.get('msisdn') is not None:
msisdn = enc_msisdn(p['msisdn'])
content = 'ff' * 20 + msisdn + 'ff' * 2
content = 'ff' * 20 + msisdn
r = self._scc.select_path(['3f00', '7f10'])
data, sw = self._scc.update_record('6F40', 1, content, force_len=True)

View File

@ -377,11 +377,13 @@ class EF_SMS(LinFixedEF):
# TS 51.011 Section 10.5.5
class EF_MSISDN(LinFixedEF):
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={15, None})
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={15, 34})
def _decode_record_hex(self, raw_hex_data):
return {'msisdn': dec_msisdn(raw_hex_data)}
def _encode_record_hex(self, abstract):
return enc_msisdn(abstract['msisdn'])
encoded_msisdn = enc_msisdn(abstract['msisdn'])
alpha_identifier = (list(self.rec_len)[0] - len(encoded_msisdn) // 2) * "ff"
return alpha_identifier + encoded_msisdn
# TS 51.011 Section 10.5.6
class EF_SMSP(LinFixedEF):

View File

@ -400,18 +400,27 @@ def dec_msisdn(ef_msisdn:Hexstr) -> Optional[Tuple[int,int,Optional[str]]]:
def enc_msisdn(msisdn:str, npi:int=0x01, ton:int=0x03) -> Hexstr:
"""
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3. (The result
will not contain the optional Alpha Identifier at the beginning.)
Default NPI / ToN values:
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
- ToN: network specific or international number (if starts with '+').
"""
# If no MSISDN is supplied then encode the file contents as all "ff"
if msisdn == "" or msisdn == "+":
return "ff" * 14
# Leading '+' indicates International Number
if msisdn[0] == '+':
msisdn = msisdn[1:]
ton = 0x01
# An MSISDN must not exceed 20 digits
if len(msisdn) > 20:
raise ValueError("msisdn must not be longer than 20 digits")
# Append 'f' padding if number of digits is odd
if len(msisdn) % 2 > 0:
msisdn += 'f'
@ -421,7 +430,8 @@ def enc_msisdn(msisdn:str, npi:int=0x01, ton:int=0x03) -> Hexstr:
npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd + ("ff" * 2)
def dec_st(st, table="sim") -> str:
"""

View File

@ -109,5 +109,33 @@ class DecTestCase(unittest.TestCase):
encoded = suci_calc_info._encode_hex(self.decoded_testfile_suci)
self.assertEqual(encoded.lower(), self.testfile_suci_calc_info.lower())
def testEnc_msisdn(self):
msisdn_encoded = utils.enc_msisdn("+4916012345678", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "0891946110325476f8ffffffffff")
msisdn_encoded = utils.enc_msisdn("123456", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "04b1214365ffffffffffffffffff")
msisdn_encoded = utils.enc_msisdn("12345678901234567890", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "0bb121436587092143658709ffff")
msisdn_encoded = utils.enc_msisdn("+12345678901234567890", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "0b9121436587092143658709ffff")
msisdn_encoded = utils.enc_msisdn("", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
msisdn_encoded = utils.enc_msisdn("+", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
def testDec_msisdn(self):
msisdn_decoded = utils.dec_msisdn("0891946110325476f8ffffffffff")
self.assertEqual(msisdn_decoded, (1, 1, "+4916012345678"))
msisdn_decoded = utils.dec_msisdn("04b1214365ffffffffffffffffff")
self.assertEqual(msisdn_decoded, (1, 3, "123456"))
msisdn_decoded = utils.dec_msisdn("0bb121436587092143658709ffff")
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
msisdn_decoded = utils.dec_msisdn("ffffffffffffffffffffffffffff")
self.assertEqual(msisdn_decoded, None)
msisdn_decoded = utils.dec_msisdn("00112233445566778899AABBCCDDEEFF001122330bb121436587092143658709ffff")
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
msisdn_decoded = utils.dec_msisdn("ffffffffffffffffffffffffffffffffffffffff0bb121436587092143658709ffff")
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
if __name__ == "__main__":
unittest.main()