mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-16 18:38:32 +03:00
utils: move enc_msisdn and dec_msisdn to legacy/utils.py
We now have a construct based encoder/decoder for the record content of EF.MSISDN. This means that we do not need the functions enc_msisdn and dec_msisdn in the non-legacy code anymore. We can now move both to legacy/utils.py. Related: OS#5714 Change-Id: I19ec8ba14551ec282fc0cc12ae2f6d528bdfc527
This commit is contained in:
@@ -42,8 +42,8 @@ from pySim.commands import SimCardCommands
|
|||||||
from pySim.transport import init_reader, argparse_add_reader_args
|
from pySim.transport import init_reader, argparse_add_reader_args
|
||||||
from pySim.exceptions import SwMatchError
|
from pySim.exceptions import SwMatchError
|
||||||
from pySim.legacy.cards import card_detect, SimCard, UsimCard, IsimCard
|
from pySim.legacy.cards import card_detect, SimCard, UsimCard, IsimCard
|
||||||
from pySim.utils import dec_imsi, dec_iccid, dec_msisdn
|
from pySim.utils import dec_imsi, dec_iccid
|
||||||
from pySim.legacy.utils import format_xplmn_w_act, dec_st
|
from pySim.legacy.utils import format_xplmn_w_act, dec_st, dec_msisdn
|
||||||
|
|
||||||
option_parser = argparse.ArgumentParser(description='Legacy tool for reading some parts of a SIM card',
|
option_parser = argparse.ArgumentParser(description='Legacy tool for reading some parts of a SIM card',
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ from smartcard.util import toBytes
|
|||||||
from pytlv.TLV import *
|
from pytlv.TLV import *
|
||||||
|
|
||||||
from pySim.cards import SimCardBase, UiccCardBase
|
from pySim.cards import SimCardBase, UiccCardBase
|
||||||
from pySim.utils import dec_iccid, enc_iccid, dec_imsi, enc_imsi, dec_msisdn, enc_msisdn
|
from pySim.utils import dec_iccid, enc_iccid, dec_imsi, enc_imsi
|
||||||
from pySim.utils import enc_plmn, get_addr_type
|
from pySim.utils import enc_plmn, get_addr_type
|
||||||
from pySim.utils import is_hex, h2b, b2h, h2s, s2h, lpad, rpad
|
from pySim.utils import is_hex, h2b, b2h, h2s, s2h, lpad, rpad
|
||||||
from pySim.legacy.utils import enc_ePDGSelection, format_xplmn_w_act, format_xplmn, dec_st, enc_st
|
from pySim.legacy.utils import enc_ePDGSelection, format_xplmn_w_act, format_xplmn, dec_st, enc_st
|
||||||
from pySim.legacy.utils import format_ePDGSelection, dec_addr_tlv, enc_addr_tlv
|
from pySim.legacy.utils import format_ePDGSelection, dec_addr_tlv, enc_addr_tlv, dec_msisdn, enc_msisdn
|
||||||
from pySim.legacy.ts_51_011 import EF, DF
|
from pySim.legacy.ts_51_011 import EF, DF
|
||||||
from pySim.legacy.ts_31_102 import EF_USIM_ADF_map
|
from pySim.legacy.ts_31_102 import EF_USIM_ADF_map
|
||||||
from pySim.legacy.ts_31_103 import EF_ISIM_ADF_map
|
from pySim.legacy.ts_31_103 import EF_ISIM_ADF_map
|
||||||
|
|||||||
@@ -20,8 +20,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from typing import Optional, Tuple
|
||||||
from pySim.utils import Hexstr, rpad, enc_plmn, h2i, i2s, s2h
|
from pySim.utils import Hexstr, rpad, enc_plmn, h2i, i2s, s2h
|
||||||
from pySim.utils import dec_xplmn_w_act, dec_xplmn, dec_mcc_from_plmn, dec_mnc_from_plmn
|
from pySim.utils import dec_xplmn_w_act, dec_xplmn, dec_mcc_from_plmn, dec_mnc_from_plmn
|
||||||
|
from osmocom.utils import swap_nibbles, h2b, b2h
|
||||||
|
|
||||||
def hexstr_to_Nbytearr(s, nbytes):
|
def hexstr_to_Nbytearr(s, nbytes):
|
||||||
return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
|
return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
|
||||||
@@ -330,3 +332,82 @@ def enc_addr_tlv(addr, addr_type='00'):
|
|||||||
s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
|
s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def dec_msisdn(ef_msisdn: Hexstr) -> Optional[Tuple[int, int, Optional[str]]]:
|
||||||
|
"""
|
||||||
|
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
|
||||||
|
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Convert from str to (kind of) 'bytes'
|
||||||
|
ef_msisdn = h2b(ef_msisdn)
|
||||||
|
|
||||||
|
# Make sure mandatory fields are present
|
||||||
|
if len(ef_msisdn) < 14:
|
||||||
|
raise ValueError("EF.MSISDN is too short")
|
||||||
|
|
||||||
|
# Skip optional Alpha Identifier
|
||||||
|
xlen = len(ef_msisdn) - 14
|
||||||
|
msisdn_lhv = ef_msisdn[xlen:]
|
||||||
|
|
||||||
|
# Parse the length (in bytes) of the BCD encoded number
|
||||||
|
bcd_len = msisdn_lhv[0]
|
||||||
|
# BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
|
||||||
|
if bcd_len == 0xff:
|
||||||
|
return None
|
||||||
|
elif bcd_len > 11 or bcd_len < 1:
|
||||||
|
raise ValueError(
|
||||||
|
"Length of MSISDN (%d bytes) is out of range" % bcd_len)
|
||||||
|
|
||||||
|
# Parse ToN / NPI
|
||||||
|
ton = (msisdn_lhv[1] >> 4) & 0x07
|
||||||
|
npi = msisdn_lhv[1] & 0x0f
|
||||||
|
bcd_len -= 1
|
||||||
|
|
||||||
|
# No MSISDN?
|
||||||
|
if not bcd_len:
|
||||||
|
return (npi, ton, None)
|
||||||
|
|
||||||
|
msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
|
||||||
|
# International number 10.5.118/3GPP TS 24.008
|
||||||
|
if ton == 0x01:
|
||||||
|
msisdn = '+' + msisdn
|
||||||
|
|
||||||
|
return (npi, ton, msisdn)
|
||||||
|
|
||||||
|
|
||||||
|
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. (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 in ["", "+"]:
|
||||||
|
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'
|
||||||
|
|
||||||
|
# BCD length also includes NPI/ToN header
|
||||||
|
bcd_len = len(msisdn) // 2 + 1
|
||||||
|
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)
|
||||||
|
|||||||
@@ -341,86 +341,6 @@ def derive_mnc(digit1: int, digit2: int, digit3: int = 0x0f) -> int:
|
|||||||
|
|
||||||
return mnc
|
return mnc
|
||||||
|
|
||||||
|
|
||||||
def dec_msisdn(ef_msisdn: Hexstr) -> Optional[Tuple[int, int, Optional[str]]]:
|
|
||||||
"""
|
|
||||||
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
|
|
||||||
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Convert from str to (kind of) 'bytes'
|
|
||||||
ef_msisdn = h2b(ef_msisdn)
|
|
||||||
|
|
||||||
# Make sure mandatory fields are present
|
|
||||||
if len(ef_msisdn) < 14:
|
|
||||||
raise ValueError("EF.MSISDN is too short")
|
|
||||||
|
|
||||||
# Skip optional Alpha Identifier
|
|
||||||
xlen = len(ef_msisdn) - 14
|
|
||||||
msisdn_lhv = ef_msisdn[xlen:]
|
|
||||||
|
|
||||||
# Parse the length (in bytes) of the BCD encoded number
|
|
||||||
bcd_len = msisdn_lhv[0]
|
|
||||||
# BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
|
|
||||||
if bcd_len == 0xff:
|
|
||||||
return None
|
|
||||||
elif bcd_len > 11 or bcd_len < 1:
|
|
||||||
raise ValueError(
|
|
||||||
"Length of MSISDN (%d bytes) is out of range" % bcd_len)
|
|
||||||
|
|
||||||
# Parse ToN / NPI
|
|
||||||
ton = (msisdn_lhv[1] >> 4) & 0x07
|
|
||||||
npi = msisdn_lhv[1] & 0x0f
|
|
||||||
bcd_len -= 1
|
|
||||||
|
|
||||||
# No MSISDN?
|
|
||||||
if not bcd_len:
|
|
||||||
return (npi, ton, None)
|
|
||||||
|
|
||||||
msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
|
|
||||||
# International number 10.5.118/3GPP TS 24.008
|
|
||||||
if ton == 0x01:
|
|
||||||
msisdn = '+' + msisdn
|
|
||||||
|
|
||||||
return (npi, ton, msisdn)
|
|
||||||
|
|
||||||
|
|
||||||
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. (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 in ["", "+"]:
|
|
||||||
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'
|
|
||||||
|
|
||||||
# BCD length also includes NPI/ToN header
|
|
||||||
bcd_len = len(msisdn) // 2 + 1
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
|
def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
|
||||||
"""
|
"""
|
||||||
The ADM pin can be supplied either in its hexadecimal form or as
|
The ADM pin can be supplied either in its hexadecimal form or as
|
||||||
|
|||||||
@@ -145,31 +145,31 @@ class DecTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(encoded.lower(), self.testfile_suci_calc_info.lower())
|
self.assertEqual(encoded.lower(), self.testfile_suci_calc_info.lower())
|
||||||
|
|
||||||
def testEnc_msisdn(self):
|
def testEnc_msisdn(self):
|
||||||
msisdn_encoded = utils.enc_msisdn("+4916012345678", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("+4916012345678", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "0891946110325476f8ffffffffff")
|
self.assertEqual(msisdn_encoded, "0891946110325476f8ffffffffff")
|
||||||
msisdn_encoded = utils.enc_msisdn("123456", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("123456", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "04b1214365ffffffffffffffffff")
|
self.assertEqual(msisdn_encoded, "04b1214365ffffffffffffffffff")
|
||||||
msisdn_encoded = utils.enc_msisdn("12345678901234567890", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("12345678901234567890", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "0bb121436587092143658709ffff")
|
self.assertEqual(msisdn_encoded, "0bb121436587092143658709ffff")
|
||||||
msisdn_encoded = utils.enc_msisdn("+12345678901234567890", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("+12345678901234567890", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "0b9121436587092143658709ffff")
|
self.assertEqual(msisdn_encoded, "0b9121436587092143658709ffff")
|
||||||
msisdn_encoded = utils.enc_msisdn("", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
|
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
|
||||||
msisdn_encoded = utils.enc_msisdn("+", npi=0x01, ton=0x03)
|
msisdn_encoded = legacy_utils.enc_msisdn("+", npi=0x01, ton=0x03)
|
||||||
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
|
self.assertEqual(msisdn_encoded, "ffffffffffffffffffffffffffff")
|
||||||
|
|
||||||
def testDec_msisdn(self):
|
def testDec_msisdn(self):
|
||||||
msisdn_decoded = utils.dec_msisdn("0891946110325476f8ffffffffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("0891946110325476f8ffffffffff")
|
||||||
self.assertEqual(msisdn_decoded, (1, 1, "+4916012345678"))
|
self.assertEqual(msisdn_decoded, (1, 1, "+4916012345678"))
|
||||||
msisdn_decoded = utils.dec_msisdn("04b1214365ffffffffffffffffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("04b1214365ffffffffffffffffff")
|
||||||
self.assertEqual(msisdn_decoded, (1, 3, "123456"))
|
self.assertEqual(msisdn_decoded, (1, 3, "123456"))
|
||||||
msisdn_decoded = utils.dec_msisdn("0bb121436587092143658709ffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("0bb121436587092143658709ffff")
|
||||||
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
||||||
msisdn_decoded = utils.dec_msisdn("ffffffffffffffffffffffffffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("ffffffffffffffffffffffffffff")
|
||||||
self.assertEqual(msisdn_decoded, None)
|
self.assertEqual(msisdn_decoded, None)
|
||||||
msisdn_decoded = utils.dec_msisdn("00112233445566778899AABBCCDDEEFF001122330bb121436587092143658709ffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("00112233445566778899AABBCCDDEEFF001122330bb121436587092143658709ffff")
|
||||||
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
||||||
msisdn_decoded = utils.dec_msisdn("ffffffffffffffffffffffffffffffffffffffff0bb121436587092143658709ffff")
|
msisdn_decoded = legacy_utils.dec_msisdn("ffffffffffffffffffffffffffffffffffffffff0bb121436587092143658709ffff")
|
||||||
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
self.assertEqual(msisdn_decoded, (1, 3, "12345678901234567890"))
|
||||||
|
|
||||||
class TestLuhn(unittest.TestCase):
|
class TestLuhn(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user