laforge has submitted this change. ( 
https://gerrit.osmocom.org/c/pysim/+/36009?usp=email )

Change subject: utils: Add function to verify Luhn check digits and to sanitize 
ICCIDs
......................................................................

utils: Add function to verify Luhn check digits and to sanitize ICCIDs

Change-Id: I7812420cf97984dd834fca6a38c5e5ae113243cb
---
M pySim/utils.py
M tests/test_utils.py
2 files changed, 56 insertions(+), 1 deletion(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved




diff --git a/pySim/utils.py b/pySim/utils.py
index 86874ba..afa476b 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -9,7 +9,7 @@
 import datetime
 import argparse
 from io import BytesIO
-from typing import Optional, List, Dict, Any, Tuple, NewType
+from typing import Optional, List, Dict, Any, Tuple, NewType, Union

 # Copyright (C) 2009-2010  Sylvain Munaut <[email protected]>
 # Copyright (C) 2021 Harald Welte <[email protected]>
@@ -446,6 +446,30 @@
 def enc_iccid(iccid: str) -> Hexstr:
     return swap_nibbles(rpad(iccid, 20))

+def sanitize_iccid(iccid: Union[int, str]) -> str:
+    iccid = str(iccid)
+    if len(iccid) < 18:
+        raise ValueError('ICCID input value must be at least 18 digits')
+    if len(iccid) > 20:
+        raise ValueError('ICCID input value must be at most 20 digits')
+    if len(iccid) == 18:
+        # 18 digits means we must add a luhn check digit to reach 19 digits
+        iccid += str(calculate_luhn(iccid))
+    if len(iccid) == 20:
+        # 20 digits means we're actually exceeding E.118 by one digit, and
+        # the luhn check digit must already be included
+        verify_luhn(iccid)
+    if len(iccid) == 19:
+        # 19 digits means that it's either an in-spec 19-digits ICCID with
+        # its luhn check digit already present, or it's an out-of-spec 20-digit
+        # ICCID without that check digit...
+        try:
+            verify_luhn(iccid)
+        except ValueError:
+            # 19th digit was not luhn check digit; we must add it
+            iccid += str(calculate_luhn(iccid))
+    return iccid
+

 def enc_plmn(mcc: Hexstr, mnc: Hexstr) -> Hexstr:
     """Converts integer MCC/MNC into 3 bytes for EF"""
@@ -606,6 +630,11 @@
                            for d in num[::-2]]) % 10
     return 0 if check_digit == 10 else check_digit

+def verify_luhn(digits: str):
+    """Verify the Luhn check digit; raises ValueError if it is incorrect."""
+    cd = calculate_luhn(digits[:-1])
+    if str(cd) != digits[-1]:
+        raise ValueError('Luhn check digit mismatch: should be %s but is %s' % 
(str(cd), digits[-1]))

 def mcc_from_imsi(imsi: str) -> Optional[str]:
     """
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 7609f80..2c24e3e 100755
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -236,5 +236,22 @@
         self.assertEqual(utils.dgi_parse_len(b'\xfe\x0b'), (254, b'\x0b'))
         self.assertEqual(utils.dgi_parse_len(b'\xff\x00\xff\x0b'), (255, 
b'\x0b'))

+class TestLuhn(unittest.TestCase):
+    def test_verify(self):
+        utils.verify_luhn('8988211000000530082')
+
+    def test_encode(self):
+        self.assertEqual(utils.calculate_luhn('898821100000053008'), 2)
+
+    def test_sanitize_iccid(self):
+        # 19 digits with correct luhn; we expect no change
+        self.assertEqual(utils.sanitize_iccid('8988211000000530082'), 
'8988211000000530082')
+        # 20 digits with correct luhn; we expect no change
+        self.assertEqual(utils.sanitize_iccid('89882110000005300811'), 
'89882110000005300811')
+        # 19 digits without correct luhn; we expect check digit to be added
+        self.assertEqual(utils.sanitize_iccid('8988211000000530081'), 
'89882110000005300811')
+        # 18 digits; we expect luhn check digit to be added
+        self.assertEqual(utils.sanitize_iccid('898821100000053008'), 
'8988211000000530082')
+
 if __name__ == "__main__":
        unittest.main()

--
To view, visit https://gerrit.osmocom.org/c/pysim/+/36009?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I7812420cf97984dd834fca6a38c5e5ae113243cb
Gerrit-Change-Number: 36009
Gerrit-PatchSet: 2
Gerrit-Owner: laforge <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-MessageType: merged

Reply via email to