dexter has uploaded this change for review.

View Change

utils: support alpha identifier in enc/dec_msisdn

The functions enc_msisdn and dec_msisdn do not support encoding
and decoding of the alpha identifier. Let's extend the functions
accordingly.

Related: OS#5714
Change-Id: I19ec8ba14551ec282fc0cc12ae2f6d528bdfc527
---
M pySim-read.py
M pySim/utils.py
M tests/unittests/test_utils.py
3 files changed, 33 insertions(+), 14 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/18/38118/1
diff --git a/pySim-read.py b/pySim-read.py
index aae5ad4..7af40b2 100755
--- a/pySim-read.py
+++ b/pySim-read.py
@@ -209,7 +209,7 @@
if sw == '9000':
# (npi, ton, msisdn) = res
if res is not None:
- print("MSISDN (NPI=%d ToN=%d): %s" % res)
+ print("MSISDN (Alpha-Identifier=\"%s\" NPI=%d ToN=%d): %s" % res)
else:
print("MSISDN: Not available")
else:
diff --git a/pySim/utils.py b/pySim/utils.py
index 05ad962..aa69a18 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -355,8 +355,9 @@
if len(ef_msisdn) < 14:
raise ValueError("EF.MSISDN is too short")

- # Skip optional Alpha Identifier
+ # Decode and skip optional Alpha Identifier
xlen = len(ef_msisdn) - 14
+ alpha_id = h2s(b2h(ef_msisdn[0:xlen]))
msisdn_lhv = ef_msisdn[xlen:]

# Parse the length (in bytes) of the BCD encoded number
@@ -382,23 +383,28 @@
if ton == 0x01:
msisdn = '+' + msisdn

- return (npi, ton, msisdn)
+ return (alpha_id, npi, ton, msisdn)


-def enc_msisdn(msisdn: str, npi: int = 0x01, ton: int = 0x03) -> Hexstr:
+def enc_msisdn(msisdn: str, npi: int = 0x01, ton: int = 0x03, alpha_id: str = "", rec_len: int = 14) -> 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. (The result
- will not contain the optional Alpha Identifier at the beginning.)
+ See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.

Default NPI / ToN values:
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
- ToN: network specific or international number (if starts with '+').
+ - alpha_id: human readable string that serves a label for the MSISDN
+ - rec_len: overall record length of the encoded result (>= 14)
"""

- # If no MSISDN is supplied then encode the file contents as all "ff"
+ # The resulting record length must have a length of at least 14 bytes.
+ if rec_len < 14:
+ raise ValueError("record length must be at least 14 bytess")
+
+ # If no MSISDN is supplied then encode the record contents as all "ff"
if msisdn in ["", "+"]:
- return "ff" * 14
+ return "ff" * rec_len

# Leading '+' indicates International Number
if msisdn[0] == '+':
@@ -418,7 +424,14 @@
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 + ("ff" * 2)
+ # Encode Alpha identifier
+ alpha_id_enc = s2h(alpha_id)
+ alpha_id_padding_len = rec_len - 14 - len(alpha_id)
+ if alpha_id_padding_len < 0:
+ raise ValueError("alpha_id too long, increase rec_len")
+ alpha_id_padding = "ff" * alpha_id_padding_len
+
+ return alpha_id_enc + alpha_id_padding + ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd + ("ff" * 2)


def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
diff --git a/tests/unittests/test_utils.py b/tests/unittests/test_utils.py
index 0e61a61..ed22a73 100755
--- a/tests/unittests/test_utils.py
+++ b/tests/unittests/test_utils.py
@@ -157,20 +157,26 @@
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
msisdn_encoded = utils.enc_msisdn("+", npi=0x01, ton=0x03)
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
+ msisdn_encoded = utils.enc_msisdn("+4917279119183", npi=0x01, ton=0x01, alpha_id="Eigene Rufnummer", rec_len=34)
+ self.assertEqual(msisdn_encoded, "456967656e65205275666e756d6d6572ffffffff0891947172199181f3ffffffffff")

def testDec_msisdn(self):
msisdn_decoded = utils.dec_msisdn("0891946110325476f8ffffffffff")
- self.assertEqual(msisdn_decoded, (1, 1, "+4916012345678"))
+ self.assertEqual(msisdn_decoded, ("", 1, 1, "+4916012345678"))
msisdn_decoded = utils.dec_msisdn("04b1214365ffffffffffffffffff")
- self.assertEqual(msisdn_decoded, (1, 3, "123456"))
+ self.assertEqual(msisdn_decoded, ("", 1, 3, "123456"))
msisdn_decoded = utils.dec_msisdn("0bb121436587092143658709ffff")
- self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
+ 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"))
+ self.assertEqual(msisdn_decoded, ('\x00\x11"3DUfw\x88\x99ª»ÌÝî\x00\x11"3', 1, 3, "12345678901234567890"))
msisdn_decoded = utils.dec_msisdn("ffffffffffffffffffffffffffffffffffffffff0bb121436587092143658709ffff")
- self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
+ self.assertEqual(msisdn_decoded, ("", 1, 3, "12345678901234567890"))
+ msisdn_decoded = utils.dec_msisdn("456967656e65205275666e756d6d6572ffffffff0891947172199181f3ffffffffff")
+ self.assertEqual(msisdn_decoded, ( "Eigene Rufnummer", 1, 1, "+4917279119183" ))
+ msisdn_decoded = utils.dec_msisdn("456967656e65205275666e756d6d65720891947172199181f3ffffffffff")
+ self.assertEqual(msisdn_decoded, ( "Eigene Rufnummer", 1, 1, "+4917279119183" ))

class TestLuhn(unittest.TestCase):
def test_verify(self):

To view, visit change 38118. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I19ec8ba14551ec282fc0cc12ae2f6d528bdfc527
Gerrit-Change-Number: 38118
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier@sysmocom.de>