aboutsummaryrefslogtreecommitdiffstats
path: root/pySim
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2024-02-18 11:05:45 +0100
committerHarald Welte <laforge@osmocom.org>2024-02-18 22:30:08 +0100
commitefdf423a7f758987de249412e8e67ac94f9d09aa (patch)
tree528ca7ac2046739ca4d688e728ce8f54850a995b /pySim
parent979c837286d3e5fa41322d2f97c4a31494cb5da5 (diff)
utils: Add function to verify Luhn check digits and to sanitize ICCIDs
Diffstat (limited to 'pySim')
-rw-r--r--pySim/utils.py31
1 files changed, 30 insertions, 1 deletions
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 string
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 <tnt@246tNt.com>
# Copyright (C) 2021 Harald Welte <laforge@osmocom.org>
@@ -446,6 +446,30 @@ def dec_iccid(ef: Hexstr) -> str:
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 @@ def calculate_luhn(cc) -> int:
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]:
"""