diff --git a/pySim/cards.py b/pySim/cards.py index 9a19eeda..719bf0c5 100644 --- a/pySim/cards.py +++ b/pySim/cards.py @@ -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) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 48649cd9..9123330d 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -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): diff --git a/pySim/utils.py b/pySim/utils.py index 2da93a5c..a177c569 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -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: """ diff --git a/tests/test_utils.py b/tests/test_utils.py index badde551..71c0eb0e 100755 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -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()