mirror of https://gerrit.osmocom.org/pysim
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:
parent
977035c429
commit
b46cb3ffa2
|
@ -963,7 +963,7 @@ class SysmoUSIMSJS1(UsimCard):
|
||||||
# TODO: Extension1 Record Identifier
|
# TODO: Extension1 Record Identifier
|
||||||
if p.get('msisdn') is not None:
|
if p.get('msisdn') is not None:
|
||||||
msisdn = enc_msisdn(p['msisdn'])
|
msisdn = enc_msisdn(p['msisdn'])
|
||||||
data = 'ff' * 20 + msisdn + 'ff' * 2
|
data = 'ff' * 20 + msisdn
|
||||||
|
|
||||||
r = self._scc.select_path(['3f00', '7f10'])
|
r = self._scc.select_path(['3f00', '7f10'])
|
||||||
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
|
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
|
||||||
|
@ -1356,7 +1356,7 @@ class SysmoISIMSJA2(UsimCard, IsimCard):
|
||||||
# TODO: Extension1 Record Identifier
|
# TODO: Extension1 Record Identifier
|
||||||
if p.get('msisdn') is not None:
|
if p.get('msisdn') is not None:
|
||||||
msisdn = enc_msisdn(p['msisdn'])
|
msisdn = enc_msisdn(p['msisdn'])
|
||||||
content = 'ff' * 20 + msisdn + 'ff' * 2
|
content = 'ff' * 20 + msisdn
|
||||||
|
|
||||||
r = self._scc.select_path(['3f00', '7f10'])
|
r = self._scc.select_path(['3f00', '7f10'])
|
||||||
data, sw = self._scc.update_record('6F40', 1, content, force_len=True)
|
data, sw = self._scc.update_record('6F40', 1, content, force_len=True)
|
||||||
|
|
|
@ -377,11 +377,13 @@ class EF_SMS(LinFixedEF):
|
||||||
# TS 51.011 Section 10.5.5
|
# TS 51.011 Section 10.5.5
|
||||||
class EF_MSISDN(LinFixedEF):
|
class EF_MSISDN(LinFixedEF):
|
||||||
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN'):
|
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):
|
def _decode_record_hex(self, raw_hex_data):
|
||||||
return {'msisdn': dec_msisdn(raw_hex_data)}
|
return {'msisdn': dec_msisdn(raw_hex_data)}
|
||||||
def _encode_record_hex(self, abstract):
|
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
|
# TS 51.011 Section 10.5.6
|
||||||
class EF_SMSP(LinFixedEF):
|
class EF_SMSP(LinFixedEF):
|
||||||
|
|
|
@ -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:
|
def enc_msisdn(msisdn:str, npi:int=0x01, ton:int=0x03) -> Hexstr:
|
||||||
"""
|
"""
|
||||||
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
|
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:
|
Default NPI / ToN values:
|
||||||
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
|
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
|
||||||
- ToN: network specific or international number (if starts with '+').
|
- 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
|
# Leading '+' indicates International Number
|
||||||
if msisdn[0] == '+':
|
if msisdn[0] == '+':
|
||||||
msisdn = msisdn[1:]
|
msisdn = msisdn[1:]
|
||||||
ton = 0x01
|
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
|
# Append 'f' padding if number of digits is odd
|
||||||
if len(msisdn) % 2 > 0:
|
if len(msisdn) % 2 > 0:
|
||||||
msisdn += 'f'
|
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
|
npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
|
||||||
bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
|
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:
|
def dec_st(st, table="sim") -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -109,5 +109,33 @@ class DecTestCase(unittest.TestCase):
|
||||||
encoded = suci_calc_info._encode_hex(self.decoded_testfile_suci)
|
encoded = suci_calc_info._encode_hex(self.decoded_testfile_suci)
|
||||||
self.assertEqual(encoded.lower(), self.testfile_suci_calc_info.lower())
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue