gerrit-log
Threads by month
- ----- 2026 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
July 2023
- 1 participants
- 3051 discussions
[XL] Change in pysim[master]: split pySim/legacy/{cards,utils} from pySim/{cards,utils}
by laforge 11 Jul '23
by laforge 11 Jul '23
11 Jul '23
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/33691 )
Change subject: split pySim/legacy/{cards,utils} from pySim/{cards,utils}
......................................................................
split pySim/legacy/{cards,utils} from pySim/{cards,utils}
There are some functions / classes which are only needed by the legacy
tools pySim-{read,prog}, bypassing our modern per-file transcoder
classes. Let's move this code to the pySim/legacy sub-directory,
rendering pySim.legacy.* module names.
The long-term goal is to get rid of those and have all code use the
modern pySim/filesystem classes for reading/decoding/encoding/writing
any kind of data on cards.
Change-Id: Ia8cf831929730c48f90679a83d69049475cc5077
---
M pySim-prog.py
M pySim-read.py
M pySim-shell.py
M pySim-trace.py
M pySim/cards.py
A pySim/legacy/cards.py
A pySim/legacy/utils.py
M pySim/utils.py
M tests/test_utils.py
9 files changed, 1,921 insertions(+), 1,928 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/91/33691/1
diff --git a/pySim-prog.py b/pySim-prog.py
index 028db55..2e9d5d7 100755
--- a/pySim-prog.py
+++ b/pySim-prog.py
@@ -36,7 +36,7 @@
from pySim.commands import SimCardCommands
from pySim.transport import init_reader
-from pySim.cards import _cards_classes, card_detect
+from pySim.legacy.cards import _cards_classes, card_detect
from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn, dec_iccid
from pySim.ts_51_011 import EF, EF_AD
from pySim.card_handler import *
diff --git a/pySim-read.py b/pySim-read.py
index f80509c..52c60be 100755
--- a/pySim-read.py
+++ b/pySim-read.py
@@ -35,10 +35,9 @@
from pySim.commands import SimCardCommands
from pySim.transport import init_reader, argparse_add_reader_args
from pySim.exceptions import SwMatchError
-from pySim.cards import card_detect, SimCard, UsimCard, IsimCard
-from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_msisdn
-from pySim.utils import format_xplmn_w_act, dec_st
-from pySim.utils import h2s, format_ePDGSelection
+from pySim.legacy.cards import card_detect, SimCard, UsimCard, IsimCard
+from pySim.utils import h2b, h2s, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_msisdn
+from pySim.legacy.utils import format_xplmn_w_act, dec_st
option_parser = argparse.ArgumentParser(description='Legacy tool for reading some parts of a SIM card',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
diff --git a/pySim-shell.py b/pySim-shell.py
index a65e42a..061ac36 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -49,7 +49,7 @@
from pySim.exceptions import *
from pySim.commands import SimCardCommands
from pySim.transport import init_reader, ApduTracer, argparse_add_reader_args, ProactiveHandler
-from pySim.cards import card_detect, SimCard, UsimCard
+from pySim.cards import card_detect, SimCardBase, UiccCardBase
from pySim.utils import h2b, b2h, i2h, swap_nibbles, rpad, JsonEncoder, bertlv_parse_one, sw_match
from pySim.utils import sanitize_pin_adm, tabulate_str_list, boxed_heading_str, Hexstr
from pySim.card_handler import CardHandler, CardHandlerAuto
@@ -98,7 +98,7 @@
card = card_detect("auto", scc)
if card is None:
print("Warning: Could not detect card type - assuming a generic card type...")
- card = SimCard(scc)
+ card = SimCardBase(scc)
generic_card = True
profile = CardProfile.pick(scc)
@@ -132,7 +132,7 @@
# IF we don't do this, we will have a SimCard but try USIM specific commands like
# the update_ust method (see https://osmocom.org/issues/6055)
if generic_card:
- card = UsimCard(scc)
+ card = UiccCardBase(scc)
# Create runtime state with card profile
rs = RuntimeState(card, profile)
diff --git a/pySim-trace.py b/pySim-trace.py
index bb1805c..4c8696d 100755
--- a/pySim-trace.py
+++ b/pySim-trace.py
@@ -8,7 +8,7 @@
from pySim.apdu import *
from pySim.filesystem import RuntimeState
-from pySim.cards import UsimCard
+from pySim.cards import UiccCardBase
from pySim.commands import SimCardCommands
from pySim.profile import CardProfile
from pySim.ts_102_221 import CardProfileUICCSIM
@@ -36,13 +36,13 @@
class DummySimLink(LinkBase):
"""A dummy implementation of the LinkBase abstract base class. Currently required
- as the UsimCard doesn't work without SimCardCommands, which in turn require
+ as the UiccCardBase doesn't work without SimCardCommands, which in turn require
a LinkBase implementation talking to a card.
In the tracer, we don't actually talk to any card, so we simply drop everything
and claim it is successful.
- The UsimCard / SimCardCommands should be refactored to make this obsolete later."""
+ The UiccCardBase / SimCardCommands should be refactored to make this obsolete later."""
def __init__(self, debug: bool = False, **kwargs):
super().__init__(**kwargs)
self._debug = debug
@@ -75,7 +75,7 @@
profile.add_application(CardApplicationUSIM())
profile.add_application(CardApplicationISIM())
scc = SimCardCommands(transport=DummySimLink())
- card = UsimCard(scc)
+ card = UiccCardBase(scc)
self.rs = RuntimeState(card, profile)
# APDU Decoder
self.ad = ApduDecoder(ApduCommands)
diff --git a/pySim/cards.py b/pySim/cards.py
index 2cbc507..07966d0 100644
--- a/pySim/cards.py
+++ b/pySim/cards.py
@@ -23,6 +23,7 @@
#
from typing import Optional, Dict, Tuple
+from pySim.ts_102_221 import EF_DIR, EF_ICCID
import abc
from pySim.utils import *
@@ -60,22 +61,21 @@
return False
return True
+ def read_iccid(self):
+ ef_iccid = EF_ICCID()
+ (res, sw) = self._scc.read_binary(ef_iccid.fid)
+ if sw == '9000':
+ return (dec_iccid(res), sw)
+ else:
+ return (None, sw)
+
class SimCardBase(CardBase):
-
"""Here we only add methods for commands specified in TS 51.011, without
any higher-layer processing."""
name = 'SIM'
- def read_binary(self, ef, length=None, offset=0):
- ef_path = ef in EF and EF[ef] or ef
- return self._scc.read_binary(ef_path, length, offset)
-
- def read_record(self, ef, rec_no):
- ef_path = ef in EF and EF[ef] or ef
- return self._scc.read_record(ef_path, rec_no)
-
class UiccCardBase(SimCardBase):
name = 'UICC'
@@ -85,244 +85,16 @@
# See also: ETSI TS 102 221, Table 9.3
self._adm_chv_num = 0xA0
-################################################################################
-# LEGACY
-################################################################################
-
-from smartcard.util import toBytes
-from pytlv.TLV import *
-
-from pySim.ts_51_011 import EF, DF, EF_AD, EF_SPN
-from pySim.ts_31_102 import EF_USIM_ADF_map
-from pySim.ts_31_103 import EF_ISIM_ADF_map
-
-def format_addr(addr: str, addr_type: str) -> str:
- """
- helper function to format an FQDN (addr_type = '00') or IPv4
- (addr_type = '01') address string into a printable string that
- contains the hexadecimal representation and the original address
- string (addr)
- """
- res = ""
- if addr_type == '00': # FQDN
- res += "\t%s # %s\n" % (s2h(addr), addr)
- elif addr_type == '01': # IPv4
- octets = addr.split(".")
- addr_hex = ""
- for o in octets:
- addr_hex += ("%02x" % int(o))
- res += "\t%s # %s\n" % (addr_hex, addr)
- return res
-
-
-
-class SimCard(SimCardBase):
- """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
-
- def __init__(self, scc):
- self._adm_chv_num = 4
- super().__init__(scc)
-
- def verify_adm(self, key):
- """Authenticate with ADM key"""
- (res, sw) = self._scc.verify_chv(self._adm_chv_num, key)
- return sw
-
- def read_iccid(self):
- (res, sw) = self._scc.read_binary(EF['ICCID'])
- if sw == '9000':
- return (dec_iccid(res), sw)
- else:
- return (None, sw)
-
- def update_iccid(self, iccid):
- data, sw = self._scc.update_binary(EF['ICCID'], enc_iccid(iccid))
- return sw
-
- def read_imsi(self):
- (res, sw) = self._scc.read_binary(EF['IMSI'])
- if sw == '9000':
- return (dec_imsi(res), sw)
- else:
- return (None, sw)
-
- def update_imsi(self, imsi):
- data, sw = self._scc.update_binary(EF['IMSI'], enc_imsi(imsi))
- return sw
-
- def update_acc(self, acc):
- data, sw = self._scc.update_binary(EF['ACC'], lpad(acc, 4, c='0'))
- return sw
-
- def read_hplmn_act(self):
- (res, sw) = self._scc.read_binary(EF['HPLMNAcT'])
- if sw == '9000':
- return (format_xplmn_w_act(res), sw)
- else:
- return (None, sw)
-
- def update_hplmn_act(self, mcc, mnc, access_tech='FFFF'):
- """
- Update Home PLMN with access technology bit-field
-
- See Section "10.3.37 EFHPLMNwAcT (HPLMN Selector with Access Technology)"
- in ETSI TS 151 011 for the details of the access_tech field coding.
- Some common values:
- access_tech = '0080' # Only GSM is selected
- access_tech = 'FFFF' # All technologies selected, even Reserved for Future Use ones
- """
- # get size and write EF.HPLMNwAcT
- data = self._scc.read_binary(EF['HPLMNwAcT'], length=None, offset=0)
- size = len(data[0]) // 2
- hplmn = enc_plmn(mcc, mnc)
- content = hplmn + access_tech
- data, sw = self._scc.update_binary(
- EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
- return sw
-
- def read_oplmn_act(self):
- (res, sw) = self._scc.read_binary(EF['OPLMNwAcT'])
- if sw == '9000':
- return (format_xplmn_w_act(res), sw)
- else:
- return (None, sw)
-
- def update_oplmn_act(self, mcc, mnc, access_tech='FFFF'):
- """get size and write EF.OPLMNwAcT, See note in update_hplmn_act()"""
- data = self._scc.read_binary(EF['OPLMNwAcT'], length=None, offset=0)
- size = len(data[0]) // 2
- hplmn = enc_plmn(mcc, mnc)
- content = hplmn + access_tech
- data, sw = self._scc.update_binary(
- EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
- return sw
-
- def read_plmn_act(self):
- (res, sw) = self._scc.read_binary(EF['PLMNwAcT'])
- if sw == '9000':
- return (format_xplmn_w_act(res), sw)
- else:
- return (None, sw)
-
- def update_plmn_act(self, mcc, mnc, access_tech='FFFF'):
- """get size and write EF.PLMNwAcT, See note in update_hplmn_act()"""
- data = self._scc.read_binary(EF['PLMNwAcT'], length=None, offset=0)
- size = len(data[0]) // 2
- hplmn = enc_plmn(mcc, mnc)
- content = hplmn + access_tech
- data, sw = self._scc.update_binary(
- EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
- return sw
-
- def update_plmnsel(self, mcc, mnc):
- data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
- size = len(data[0]) // 2
- hplmn = enc_plmn(mcc, mnc)
- data, sw = self._scc.update_binary(
- EF['PLMNsel'], hplmn + 'ff' * (size-3))
- return sw
-
- def update_smsp(self, smsp):
- data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))
- return sw
-
- def update_ad(self, mnc=None, opmode=None, ofm=None, path=EF['AD']):
- """
- Update Administrative Data (AD)
-
- See Sec. "4.2.18 EF_AD (Administrative Data)"
- in 3GPP TS 31.102 for the details of the EF_AD contents.
-
- Set any parameter to None to keep old value(s) on card.
-
- Parameters:
- mnc (str): MNC of IMSI
- opmode (Hex-str, 1 Byte): MS Operation Mode
- ofm (Hex-str, 1 Byte): Operational Feature Monitor (OFM) aka Ciphering Indicator
- path (optional list with file path e.g. ['3f00', '7f20', '6fad'])
-
- Returns:
- str: Return code of write operation
- """
-
- ad = EF_AD()
-
- # read from card
- raw_hex_data, sw = self._scc.read_binary(
- path, length=None, offset=0)
- abstract_data = ad.decode_hex(raw_hex_data)
-
- # perform updates
- if mnc and abstract_data['extensions']:
- # Note: Since we derive the length of the MNC by the string length
- # of the mnc parameter, the caller must ensure that mnc has the
- # correct length and is padded with zeros (if necessary).
- mnclen = len(str(mnc))
- if mnclen > 3 or mnclen < 2:
- raise RuntimeError('invalid length of mnc "{}", expecting 2 or 3 digits'.format(mnc))
- abstract_data['extensions']['mnc_len'] = mnclen
- if opmode:
- opmode_num = int(opmode, 16)
- if opmode_num in [int(v) for v in EF_AD.OP_MODE]:
- abstract_data['ms_operation_mode'] = opmode_num
- else:
- raise RuntimeError('invalid opmode "{}"'.format(opmode))
- if ofm:
- abstract_data['ofm'] = bool(int(ofm, 16))
-
- # write to card
- raw_hex_data = ad.encode_hex(abstract_data)
- data, sw = self._scc.update_binary(path, raw_hex_data)
- return sw
-
- def read_spn(self):
- (content, sw) = self._scc.read_binary(EF['SPN'])
- if sw == '9000':
- abstract_data = EF_SPN().decode_hex(content)
- show_in_hplmn = abstract_data['show_in_hplmn']
- hide_in_oplmn = abstract_data['hide_in_oplmn']
- name = abstract_data['spn']
- return ((name, show_in_hplmn, hide_in_oplmn), sw)
- else:
- return (None, sw)
-
- def update_spn(self, name="", show_in_hplmn=False, hide_in_oplmn=False):
- abstract_data = {
- 'hide_in_oplmn': hide_in_oplmn,
- 'show_in_hplmn': show_in_hplmn,
- 'spn': name,
- }
- content = EF_SPN().encode_hex(abstract_data)
- data, sw = self._scc.update_binary(EF['SPN'], content)
- return sw
-
- def read_gid1(self):
- (res, sw) = self._scc.read_binary(EF['GID1'])
- if sw == '9000':
- return (res, sw)
- else:
- return (None, sw)
-
- def read_msisdn(self):
- (res, sw) = self._scc.read_record(EF['MSISDN'], 1)
- if sw == '9000':
- return (dec_msisdn(res), sw)
- else:
- return (None, sw)
-
-
-class UsimCard(UiccCardBase, SimCard):
- """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
-
def read_aids(self):
"""Fetch all the AIDs present on UICC"""
self._aids = []
try:
+ ef_dir = EF_DIR()
# Find out how many records the EF.DIR has
# and store all the AIDs in the UICC
- rec_cnt = self._scc.record_count(EF['DIR'])
+ rec_cnt = self._scc.record_count(ef_dir.fid)
for i in range(0, rec_cnt):
- rec = self._scc.read_record(EF['DIR'], i + 1)
+ rec = self._scc.read_record(ef_dir.fid, i + 1)
if (rec[0][0:2], rec[0][4:6]) == ('61', '4f') and len(rec[0]) > 12 \
and rec[0][8:8 + int(rec[0][6:8], 16) * 2] not in self._aids:
self._aids.append(rec[0][8:8 + int(rec[0][6:8], 16) * 2])
@@ -374,1432 +146,3 @@
# If we cannot get the full AID, try with short AID
return self._scc.select_adf(aid)
return (None, None)
-
-
- def read_ehplmn(self):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])
- if sw == '9000':
- return (format_xplmn(res), sw)
- else:
- return (None, sw)
-
- def update_ehplmn(self, mcc, mnc):
- data = self._scc.read_binary(
- EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
- size = len(data[0]) // 2
- ehplmn = enc_plmn(mcc, mnc)
- data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
- return sw
-
- def read_fplmn(self):
- res, sw = self._scc.read_binary(EF_USIM_ADF_map['FPLMN'])
- if sw == '9000':
- return format_xplmn(res), sw
- else:
- return None, sw
-
- def update_fplmn(self, fplmn):
- self._scc.select_file('3f00')
- self.select_adf_by_aid('USIM')
- size = self._scc.binary_size(EF_USIM_ADF_map['FPLMN'])
- encoded = ''.join([enc_plmn(plmn[:3], plmn[3:]) for plmn in fplmn])
- encoded = rpad(encoded, size)
- data, sw = self._scc.update_binary(EF_USIM_ADF_map['FPLMN'], encoded)
- return sw
-
- def read_epdgid(self):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGId'])
- if sw == '9000':
- try:
- addr, addr_type = dec_addr_tlv(res)
- except:
- addr = None
- addr_type = None
- return (format_addr(addr, addr_type), sw)
- else:
- return (None, sw)
-
- def update_epdgid(self, epdgid):
- size = self._scc.binary_size(EF_USIM_ADF_map['ePDGId']) * 2
- if len(epdgid) > 0:
- addr_type = get_addr_type(epdgid)
- if addr_type == None:
- raise ValueError(
- "Unknown ePDG Id address type or invalid address provided")
- epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
- else:
- epdgid_tlv = rpad('ff', size)
- data, sw = self._scc.update_binary(
- EF_USIM_ADF_map['ePDGId'], epdgid_tlv)
- return sw
-
- def read_ePDGSelection(self):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'])
- if sw == '9000':
- return (format_ePDGSelection(res), sw)
- else:
- return (None, sw)
-
- def update_ePDGSelection(self, mcc, mnc):
- (res, sw) = self._scc.read_binary(
- EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
- if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
- # Reset contents
- # 80 - Tag value
- (res, sw) = self._scc.update_binary(
- EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
- elif sw == '9000':
- (res, sw) = self._scc.update_binary(
- EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
- return sw
-
- def read_ust(self):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
- if sw == '9000':
- # Print those which are available
- return ([res, dec_st(res, table="usim")], sw)
- else:
- return ([None, None], sw)
-
- def update_ust(self, service, bit=1):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
- if sw == '9000':
- content = enc_st(res, service, bit)
- (res, sw) = self._scc.update_binary(
- EF_USIM_ADF_map['UST'], content)
- return sw
-
- def update_est(self, service, bit=1):
- (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EST'])
- if sw == '9000':
- content = enc_st(res, service, bit)
- (res, sw) = self._scc.update_binary(
- EF_USIM_ADF_map['EST'], content)
- return sw
-
-
-
-class IsimCard(UiccCardBase):
- """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
-
- name = 'ISIM'
-
- def read_pcscf(self):
- rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['PCSCF'])
- pcscf_recs = ""
- for i in range(0, rec_cnt):
- (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['PCSCF'], i + 1)
- if sw == '9000':
- try:
- addr, addr_type = dec_addr_tlv(res)
- except:
- addr = None
- addr_type = None
- content = format_addr(addr, addr_type)
- pcscf_recs += "%s" % (len(content)
- and content or '\tNot available\n')
- else:
- pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (
- sw)
- return pcscf_recs
-
- def update_pcscf(self, pcscf):
- if len(pcscf) > 0:
- addr_type = get_addr_type(pcscf)
- if addr_type == None:
- raise ValueError(
- "Unknown PCSCF address type or invalid address provided")
- content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
- else:
- # Just the tag value
- content = '80'
- rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
- pcscf_tlv = rpad(content, rec_size_bytes*2)
- data, sw = self._scc.update_record(
- EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
- return sw
-
- def read_domain(self):
- (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
- if sw == '9000':
- # Skip the initial tag value ('80') byte and get length of contents
- length = int(res[2:4], 16)
- content = h2s(res[4:4+(length*2)])
- return (content, sw)
- else:
- return (None, sw)
-
- def update_domain(self, domain=None, mcc=None, mnc=None):
- hex_str = ""
- if domain:
- hex_str = s2h(domain)
- elif mcc and mnc:
- # MCC and MNC always has 3 digits in domain form
- plmn_str = 'mnc' + lpad(mnc, 3, "0") + '.mcc' + lpad(mcc, 3, "0")
- hex_str = s2h('ims.' + plmn_str + '.3gppnetwork.org')
-
- # Build TLV
- tlv = TLV(['80'])
- content = tlv.build({'80': hex_str})
-
- bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
- data, sw = self._scc.update_binary(
- EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
- return sw
-
- def read_impi(self):
- (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
- if sw == '9000':
- # Skip the initial tag value ('80') byte and get length of contents
- length = int(res[2:4], 16)
- content = h2s(res[4:4+(length*2)])
- return (content, sw)
- else:
- return (None, sw)
-
- def update_impi(self, impi=None):
- hex_str = ""
- if impi:
- hex_str = s2h(impi)
- # Build TLV
- tlv = TLV(['80'])
- content = tlv.build({'80': hex_str})
-
- bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
- data, sw = self._scc.update_binary(
- EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
- return sw
-
- def read_impu(self):
- rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['IMPU'])
- impu_recs = ""
- for i in range(0, rec_cnt):
- (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
- if sw == '9000':
- # Skip the initial tag value ('80') byte and get length of contents
- length = int(res[2:4], 16)
- content = h2s(res[4:4+(length*2)])
- impu_recs += "\t%s\n" % (len(content)
- and content or 'Not available')
- else:
- impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (
- sw)
- return impu_recs
-
- def update_impu(self, impu=None):
- hex_str = ""
- if impu:
- hex_str = s2h(impu)
- # Build TLV
- tlv = TLV(['80'])
- content = tlv.build({'80': hex_str})
-
- rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
- impu_tlv = rpad(content, rec_size_bytes*2)
- data, sw = self._scc.update_record(
- EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
- return sw
-
- def read_iari(self):
- rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
- uiari_recs = ""
- for i in range(0, rec_cnt):
- (res, sw) = self._scc.read_record(
- EF_ISIM_ADF_map['UICCIARI'], i + 1)
- if sw == '9000':
- # Skip the initial tag value ('80') byte and get length of contents
- length = int(res[2:4], 16)
- content = h2s(res[4:4+(length*2)])
- uiari_recs += "\t%s\n" % (len(content)
- and content or 'Not available')
- else:
- uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (
- sw)
- return uiari_recs
-
- def update_ist(self, service, bit=1):
- (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IST'])
- if sw == '9000':
- content = enc_st(res, service, bit)
- (res, sw) = self._scc.update_binary(
- EF_ISIM_ADF_map['IST'], content)
- return sw
-
-
-class MagicSimBase(abc.ABC, SimCard):
- """
- Theses cards uses several record based EFs to store the provider infos,
- each possible provider uses a specific record number in each EF. The
- indexes used are ( where N is the number of providers supported ) :
- - [2 .. N+1] for the operator name
- - [1 .. N] for the programmable EFs
-
- * 3f00/7f4d/8f0c : Operator Name
-
- bytes 0-15 : provider name, padded with 0xff
- byte 16 : length of the provider name
- byte 17 : 01 for valid records, 00 otherwise
-
- * 3f00/7f4d/8f0d : Programmable Binary EFs
-
- * 3f00/7f4d/8f0e : Programmable Record EFs
-
- """
-
- _files = {} # type: Dict[str, Tuple[str, int, bool]]
- _ki_file = None # type: Optional[str]
-
- @classmethod
- def autodetect(kls, scc):
- try:
- for p, l, t in kls._files.values():
- if not t:
- continue
- if scc.record_size(['3f00', '7f4d', p]) != l:
- return None
- except:
- return None
-
- return kls(scc)
-
- def _get_count(self):
- """
- Selects the file and returns the total number of entries
- and entry size
- """
- f = self._files['name']
-
- r = self._scc.select_path(['3f00', '7f4d', f[0]])
- rec_len = int(r[-1][28:30], 16)
- tlen = int(r[-1][4:8], 16)
- rec_cnt = (tlen // rec_len) - 1
-
- if (rec_cnt < 1) or (rec_len != f[1]):
- raise RuntimeError('Bad card type')
-
- return rec_cnt
-
- def program(self, p):
- # Go to dir
- self._scc.select_path(['3f00', '7f4d'])
-
- # Home PLMN in PLMN_Sel format
- hplmn = enc_plmn(p['mcc'], p['mnc'])
-
- # Operator name ( 3f00/7f4d/8f0c )
- self._scc.update_record(self._files['name'][0], 2,
- rpad(b2h(p['name']), 32) + ('%02x' %
- len(p['name'])) + '01'
- )
-
- # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
- v = ''
-
- # inline Ki
- if self._ki_file is None:
- v += p['ki']
-
- # ICCID
- v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
-
- # IMSI
- v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
-
- # Ki
- if self._ki_file:
- v += self._ki_file + '10' + p['ki']
-
- # PLMN_Sel
- v += '6f30' + '18' + rpad(hplmn, 36)
-
- # ACC
- # This doesn't work with "fake" SuperSIM cards,
- # but will hopefully work with real SuperSIMs.
- if p.get('acc') is not None:
- v += '6f78' + '02' + lpad(p['acc'], 4)
-
- self._scc.update_record(self._files['b_ef'][0], 1,
- rpad(v, self._files['b_ef'][1]*2)
- )
-
- # SMSP ( 3f00/7f4d/8f0e )
- # FIXME
-
- # Write PLMN_Sel forcefully as well
- r = self._scc.select_path(['3f00', '7f20', '6f30'])
- tl = int(r[-1][4:8], 16)
-
- hplmn = enc_plmn(p['mcc'], p['mnc'])
- self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
-
- def erase(self):
- # Dummy
- df = {}
- for k, v in self._files.items():
- ofs = 1
- fv = v[1] * 'ff'
- if k == 'name':
- ofs = 2
- fv = fv[0:-4] + '0000'
- df[v[0]] = (fv, ofs)
-
- # Write
- for n in range(0, self._get_count()):
- for k, (msg, ofs) in df.items():
- self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
-
-
-class SuperSim(MagicSimBase):
-
- name = 'supersim'
-
- _files = {
- 'name': ('8f0c', 18, True),
- 'b_ef': ('8f0d', 74, True),
- 'r_ef': ('8f0e', 50, True),
- }
-
- _ki_file = None
-
-
-class MagicSim(MagicSimBase):
-
- name = 'magicsim'
-
- _files = {
- 'name': ('8f0c', 18, True),
- 'b_ef': ('8f0d', 130, True),
- 'r_ef': ('8f0e', 102, False),
- }
-
- _ki_file = '6f1b'
-
-
-class FakeMagicSim(SimCard):
- """
- Theses cards have a record based EF 3f00/000c that contains the provider
- information. See the program method for its format. The records go from
- 1 to N.
- """
-
- name = 'fakemagicsim'
-
- @classmethod
- def autodetect(kls, scc):
- try:
- if scc.record_size(['3f00', '000c']) != 0x5a:
- return None
- except:
- return None
-
- return kls(scc)
-
- def _get_infos(self):
- """
- Selects the file and returns the total number of entries
- and entry size
- """
-
- r = self._scc.select_path(['3f00', '000c'])
- rec_len = int(r[-1][28:30], 16)
- tlen = int(r[-1][4:8], 16)
- rec_cnt = (tlen // rec_len) - 1
-
- if (rec_cnt < 1) or (rec_len != 0x5a):
- raise RuntimeError('Bad card type')
-
- return rec_cnt, rec_len
-
- def program(self, p):
- # Home PLMN
- r = self._scc.select_path(['3f00', '7f20', '6f30'])
- tl = int(r[-1][4:8], 16)
-
- hplmn = enc_plmn(p['mcc'], p['mnc'])
- self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
-
- # Get total number of entries and entry size
- rec_cnt, rec_len = self._get_infos()
-
- # Set first entry
- entry = (
- '81' + # 1b Status: Valid & Active
- rpad(s2h(p['name'][0:14]), 28) + # 14b Entry Name
- enc_iccid(p['iccid']) + # 10b ICCID
- enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
- p['ki'] + # 16b Ki
- lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
- )
- self._scc.update_record('000c', 1, entry)
-
- def erase(self):
- # Get total number of entries and entry size
- rec_cnt, rec_len = self._get_infos()
-
- # Erase all entries
- entry = 'ff' * rec_len
- for i in range(0, rec_cnt):
- self._scc.update_record('000c', 1+i, entry)
-
-
-class GrcardSim(SimCard):
- """
- Greencard (grcard.cn) HZCOS GSM SIM
- These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
- and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
- """
-
- name = 'grcardsim'
-
- @classmethod
- def autodetect(kls, scc):
- return None
-
- def program(self, p):
- # We don't really know yet what ADM PIN 4 is about
- #self._scc.verify_chv(4, h2b("4444444444444444"))
-
- # Authenticate using ADM PIN 5
- if p['pin_adm']:
- pin = h2b(p['pin_adm'])
- else:
- pin = h2b("4444444444444444")
- self._scc.verify_chv(5, pin)
-
- # EF.ICCID
- r = self._scc.select_path(['3f00', '2fe2'])
- data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
-
- # EF.IMSI
- r = self._scc.select_path(['3f00', '7f20', '6f07'])
- data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
-
- # EF.ACC
- if p.get('acc') is not None:
- data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
-
- # EF.SMSP
- if p.get('smsp'):
- r = self._scc.select_path(['3f00', '7f10', '6f42'])
- data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
-
- # Set the Ki using proprietary command
- pdu = '80d4020010' + p['ki']
- data, sw = self._scc._tp.send_apdu(pdu)
-
- # EF.HPLMN
- r = self._scc.select_path(['3f00', '7f20', '6f30'])
- size = int(r[-1][4:8], 16)
- hplmn = enc_plmn(p['mcc'], p['mnc'])
- self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
-
- # EF.SPN (Service Provider Name)
- r = self._scc.select_path(['3f00', '7f20', '6f30'])
- size = int(r[-1][4:8], 16)
- # FIXME
-
- # FIXME: EF.MSISDN
-
-
-class SysmoSIMgr1(GrcardSim):
- """
- sysmocom sysmoSIM-GR1
- These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
- and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
- """
- name = 'sysmosim-gr1'
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 99 18 00 11 88 22 33 44 55 66 77 60"):
- return kls(scc)
- except:
- return None
- return None
-
-
-class SysmoUSIMgr1(UsimCard):
- """
- sysmocom sysmoUSIM-GR1
- """
- name = 'sysmoUSIM-GR1'
-
- @classmethod
- def autodetect(kls, scc):
- # TODO: Access the ATR
- return None
-
- def program(self, p):
- # TODO: check if verify_chv could be used or what it needs
- # self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
- # Unlock the card..
- data, sw = self._scc._tp.send_apdu_checksw(
- "0020000A083332323133323332")
-
- # TODO: move into SimCardCommands
- par = (p['ki'] + # 16b K
- p['opc'] + # 32b OPC
- enc_iccid(p['iccid']) + # 10b ICCID
- enc_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
- )
- data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
-
-
-class SysmoSIMgr2(SimCard):
- """
- sysmocom sysmoSIM-GR2
- """
-
- name = 'sysmoSIM-GR2'
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68"):
- return kls(scc)
- except:
- return None
- return None
-
- def program(self, p):
-
- # select MF
- r = self._scc.select_path(['3f00'])
-
- # authenticate as SUPER ADM using default key
- self._scc.verify_chv(0x0b, h2b("3838383838383838"))
-
- # set ADM pin using proprietary command
- # INS: D4
- # P1: 3A for PIN, 3B for PUK
- # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
- # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
- if p['pin_adm']:
- pin = h2b(p['pin_adm'])
- else:
- pin = h2b("4444444444444444")
-
- pdu = 'A0D43A0508' + b2h(pin)
- data, sw = self._scc._tp.send_apdu(pdu)
-
- # authenticate as ADM (enough to write file, and can set PINs)
-
- self._scc.verify_chv(0x05, pin)
-
- # write EF.ICCID
- data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
-
- # select DF_GSM
- r = self._scc.select_path(['7f20'])
-
- # write EF.IMSI
- data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
-
- # write EF.ACC
- if p.get('acc') is not None:
- data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
-
- # get size and write EF.HPLMN
- r = self._scc.select_path(['6f30'])
- size = int(r[-1][4:8], 16)
- hplmn = enc_plmn(p['mcc'], p['mnc'])
- self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
-
- # set COMP128 version 0 in proprietary file
- data, sw = self._scc.update_binary('0001', '001000')
-
- # set Ki in proprietary file
- data, sw = self._scc.update_binary('0001', p['ki'], 3)
-
- # select DF_TELECOM
- r = self._scc.select_path(['3f00', '7f10'])
-
- # write EF.SMSP
- if p.get('smsp'):
- data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
-
-
-class SysmoUSIMSJS1(UsimCard):
- """
- sysmocom sysmoUSIM-SJS1
- """
-
- name = 'sysmoUSIM-SJS1'
-
- def __init__(self, ssc):
- super(SysmoUSIMSJS1, self).__init__(ssc)
- self._scc.cla_byte = "00"
- self._scc.sel_ctrl = "0004" # request an FCP
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5"):
- return kls(scc)
- except:
- return None
- return None
-
- def verify_adm(self, key):
- # authenticate as ADM using default key (written on the card..)
- if not key:
- raise ValueError(
- "Please provide a PIN-ADM as there is no default one")
- (res, sw) = self._scc.verify_chv(0x0A, key)
- return sw
-
- def program(self, p):
- self.verify_adm(h2b(p['pin_adm']))
-
- # select MF
- r = self._scc.select_path(['3f00'])
-
- # write EF.ICCID
- data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
-
- # select DF_GSM
- r = self._scc.select_path(['7f20'])
-
- # set Ki in proprietary file
- data, sw = self._scc.update_binary('00FF', p['ki'])
-
- # set OPc in proprietary file
- if 'opc' in p:
- content = "01" + p['opc']
- data, sw = self._scc.update_binary('00F7', content)
-
- # set Service Provider Name
- if p.get('name') is not None:
- self.update_spn(p['name'], True, True)
-
- if p.get('acc') is not None:
- self.update_acc(p['acc'])
-
- # write EF.IMSI
- data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
-
- # EF.PLMNsel
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmnsel(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNsel failed with code %s" % sw)
-
- # EF.PLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNwAcT failed with code %s" % sw)
-
- # EF.OPLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_oplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming OPLMNwAcT failed with code %s" % sw)
-
- # EF.HPLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_hplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming HPLMNwAcT failed with code %s" % sw)
-
- # EF.AD
- if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
- if p.get('mcc') and p.get('mnc'):
- mnc = p['mnc']
- else:
- mnc = None
- sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
- if sw != '9000':
- print("Programming AD failed with code %s" % sw)
-
- # EF.SMSP
- if p.get('smsp'):
- r = self._scc.select_path(['3f00', '7f10'])
- data, sw = self._scc.update_record(
- '6f42', 1, lpad(p['smsp'], 104), force_len=True)
-
- # EF.MSISDN
- # TODO: Alpha Identifier (currently 'ff'O * 20)
- # TODO: Capability/Configuration1 Record Identifier
- # TODO: Extension1 Record Identifier
- if p.get('msisdn') is not None:
- msisdn = enc_msisdn(p['msisdn'])
- data = 'ff' * 20 + msisdn
-
- r = self._scc.select_path(['3f00', '7f10'])
- data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
-
-
-class FairwavesSIM(UsimCard):
- """
- FairwavesSIM
-
- The SIM card is operating according to the standard.
- For Ki/OP/OPC programming the following files are additionally open for writing:
- 3F00/7F20/FF01 – OP/OPC:
- byte 1 = 0x01, bytes 2-17: OPC;
- byte 1 = 0x00, bytes 2-17: OP;
- 3F00/7F20/FF02: Ki
- """
-
- name = 'Fairwaves-SIM'
- # Propriatary files
- _EF_num = {
- 'Ki': 'FF02',
- 'OP/OPC': 'FF01',
- }
- _EF = {
- 'Ki': DF['GSM']+[_EF_num['Ki']],
- 'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
- }
-
- def __init__(self, ssc):
- super(FairwavesSIM, self).__init__(ssc)
- self._adm_chv_num = 0x11
- self._adm2_chv_num = 0x12
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 44 22 06 10 00 00 01 A9"):
- return kls(scc)
- except:
- return None
- return None
-
- def verify_adm2(self, key):
- '''
- Authenticate with ADM2 key.
-
- Fairwaves SIM cards support hierarchical key structure and ADM2 key
- is a key which has access to proprietary files (Ki and OP/OPC).
- That said, ADM key inherits permissions of ADM2 key and thus we rarely
- need ADM2 key per se.
- '''
- (res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
- return sw
-
- def read_ki(self):
- """
- Read Ki in proprietary file.
-
- Requires ADM1 access level
- """
- return self._scc.read_binary(self._EF['Ki'])
-
- def update_ki(self, ki):
- """
- Set Ki in proprietary file.
-
- Requires ADM1 access level
- """
- data, sw = self._scc.update_binary(self._EF['Ki'], ki)
- return sw
-
- def read_op_opc(self):
- """
- Read Ki in proprietary file.
-
- Requires ADM1 access level
- """
- (ef, sw) = self._scc.read_binary(self._EF['OP/OPC'])
- type = 'OP' if ef[0:2] == '00' else 'OPC'
- return ((type, ef[2:]), sw)
-
- def update_op(self, op):
- """
- Set OP in proprietary file.
-
- Requires ADM1 access level
- """
- content = '00' + op
- data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
- return sw
-
- def update_opc(self, opc):
- """
- Set OPC in proprietary file.
-
- Requires ADM1 access level
- """
- content = '01' + opc
- data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
- return sw
-
- def program(self, p):
- # For some reason the card programming only works when the card
- # is handled as a classic SIM, even though it is an USIM, so we
- # reconfigure the class byte and the select control field on
- # the fly. When the programming is done the original values are
- # restored.
- cla_byte_orig = self._scc.cla_byte
- sel_ctrl_orig = self._scc.sel_ctrl
- self._scc.cla_byte = "a0"
- self._scc.sel_ctrl = "0000"
-
- try:
- self._program(p)
- finally:
- # restore original cla byte and sel ctrl
- self._scc.cla_byte = cla_byte_orig
- self._scc.sel_ctrl = sel_ctrl_orig
-
- def _program(self, p):
- # authenticate as ADM1
- if not p['pin_adm']:
- raise ValueError(
- "Please provide a PIN-ADM as there is no default one")
- self.verify_adm(h2b(p['pin_adm']))
-
- # TODO: Set operator name
- if p.get('smsp') is not None:
- sw = self.update_smsp(p['smsp'])
- if sw != '9000':
- print("Programming SMSP failed with code %s" % sw)
- # This SIM doesn't support changing ICCID
- if p.get('mcc') is not None and p.get('mnc') is not None:
- sw = self.update_hplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming MCC/MNC failed with code %s" % sw)
- if p.get('imsi') is not None:
- sw = self.update_imsi(p['imsi'])
- if sw != '9000':
- print("Programming IMSI failed with code %s" % sw)
- if p.get('ki') is not None:
- sw = self.update_ki(p['ki'])
- if sw != '9000':
- print("Programming Ki failed with code %s" % sw)
- if p.get('opc') is not None:
- sw = self.update_opc(p['opc'])
- if sw != '9000':
- print("Programming OPC failed with code %s" % sw)
- if p.get('acc') is not None:
- sw = self.update_acc(p['acc'])
- if sw != '9000':
- print("Programming ACC failed with code %s" % sw)
-
-
-class OpenCellsSim(SimCard):
- """
- OpenCellsSim
-
- """
-
- name = 'OpenCells-SIM'
-
- def __init__(self, ssc):
- super(OpenCellsSim, self).__init__(ssc)
- self._adm_chv_num = 0x0A
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 9F 95 80 1F C3 80 31 E0 73 FE 21 13 57 86 81 02 86 98 44 18 A8"):
- return kls(scc)
- except:
- return None
- return None
-
- def program(self, p):
- if not p['pin_adm']:
- raise ValueError(
- "Please provide a PIN-ADM as there is no default one")
- self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
-
- # select MF
- r = self._scc.select_path(['3f00'])
-
- # write EF.ICCID
- data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
-
- r = self._scc.select_path(['7ff0'])
-
- # set Ki in proprietary file
- data, sw = self._scc.update_binary('FF02', p['ki'])
-
- # set OPC in proprietary file
- data, sw = self._scc.update_binary('FF01', p['opc'])
-
- # select DF_GSM
- r = self._scc.select_path(['7f20'])
-
- # write EF.IMSI
- data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
-
-
-class WavemobileSim(UsimCard):
- """
- WavemobileSim
-
- """
-
- name = 'Wavemobile-SIM'
-
- def __init__(self, ssc):
- super(WavemobileSim, self).__init__(ssc)
- self._adm_chv_num = 0x0A
- self._scc.cla_byte = "00"
- self._scc.sel_ctrl = "0004" # request an FCP
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes("3B 9F 95 80 1F C7 80 31 E0 73 F6 21 13 67 4D 45 16 00 43 01 00 8F"):
- return kls(scc)
- except:
- return None
- return None
-
- def program(self, p):
- if not p['pin_adm']:
- raise ValueError(
- "Please provide a PIN-ADM as there is no default one")
- self.verify_adm(h2b(p['pin_adm']))
-
- # EF.ICCID
- # TODO: Add programming of the ICCID
- if p.get('iccid'):
- print(
- "Warning: Programming of the ICCID is not implemented for this type of card.")
-
- # KI (Presumably a proprietary file)
- # TODO: Add programming of KI
- if p.get('ki'):
- print(
- "Warning: Programming of the KI is not implemented for this type of card.")
-
- # OPc (Presumably a proprietary file)
- # TODO: Add programming of OPc
- if p.get('opc'):
- print(
- "Warning: Programming of the OPc is not implemented for this type of card.")
-
- # EF.SMSP
- if p.get('smsp'):
- sw = self.update_smsp(p['smsp'])
- if sw != '9000':
- print("Programming SMSP failed with code %s" % sw)
-
- # EF.IMSI
- if p.get('imsi'):
- sw = self.update_imsi(p['imsi'])
- if sw != '9000':
- print("Programming IMSI failed with code %s" % sw)
-
- # EF.ACC
- if p.get('acc'):
- sw = self.update_acc(p['acc'])
- if sw != '9000':
- print("Programming ACC failed with code %s" % sw)
-
- # EF.PLMNsel
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmnsel(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNsel failed with code %s" % sw)
-
- # EF.PLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNwAcT failed with code %s" % sw)
-
- # EF.OPLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_oplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming OPLMNwAcT failed with code %s" % sw)
-
- # EF.AD
- if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
- if p.get('mcc') and p.get('mnc'):
- mnc = p['mnc']
- else:
- mnc = None
- sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
- if sw != '9000':
- print("Programming AD failed with code %s" % sw)
-
- return None
-
-
-class SysmoISIMSJA2(UsimCard, IsimCard):
- """
- sysmocom sysmoISIM-SJA2
- """
-
- name = 'sysmoISIM-SJA2'
-
- def __init__(self, ssc):
- super(SysmoISIMSJA2, self).__init__(ssc)
- self._scc.cla_byte = "00"
- self._scc.sel_ctrl = "0004" # request an FCP
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Try card model #1
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
-
- # Try card model #2
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
-
- # Try card model #3
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
- except:
- return None
- return None
-
- def verify_adm(self, key):
- # authenticate as ADM using default key (written on the card..)
- if not key:
- raise ValueError(
- "Please provide a PIN-ADM as there is no default one")
- (res, sw) = self._scc.verify_chv(0x0A, key)
- return sw
-
- def program(self, p):
- self.verify_adm(h2b(p['pin_adm']))
-
- # Populate AIDs
- self.read_aids()
-
- # This type of card does not allow to reprogram the ICCID.
- # Reprogramming the ICCID would mess up the card os software
- # license management, so the ICCID must be kept at its factory
- # setting!
- if p.get('iccid'):
- print(
- "Warning: Programming of the ICCID is not implemented for this type of card.")
-
- # select DF_GSM
- self._scc.select_path(['7f20'])
-
- # set Service Provider Name
- if p.get('name') is not None:
- self.update_spn(p['name'], True, True)
-
- # write EF.IMSI
- if p.get('imsi'):
- self._scc.update_binary('6f07', enc_imsi(p['imsi']))
-
- # EF.PLMNsel
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmnsel(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNsel failed with code %s" % sw)
-
- # EF.PLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_plmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming PLMNwAcT failed with code %s" % sw)
-
- # EF.OPLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_oplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming OPLMNwAcT failed with code %s" % sw)
-
- # EF.HPLMNwAcT
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_hplmn_act(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming HPLMNwAcT failed with code %s" % sw)
-
- # EF.AD
- if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
- if p.get('mcc') and p.get('mnc'):
- mnc = p['mnc']
- else:
- mnc = None
- sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
- if sw != '9000':
- print("Programming AD failed with code %s" % sw)
-
- # EF.SMSP
- if p.get('smsp'):
- r = self._scc.select_path(['3f00', '7f10'])
- data, sw = self._scc.update_record(
- '6f42', 1, lpad(p['smsp'], 104), force_len=True)
-
- # EF.MSISDN
- # TODO: Alpha Identifier (currently 'ff'O * 20)
- # TODO: Capability/Configuration1 Record Identifier
- # TODO: Extension1 Record Identifier
- if p.get('msisdn') is not None:
- msisdn = enc_msisdn(p['msisdn'])
- content = 'ff' * 20 + msisdn
-
- r = self._scc.select_path(['3f00', '7f10'])
- data, sw = self._scc.update_record(
- '6F40', 1, content, force_len=True)
-
- # EF.ACC
- if p.get('acc'):
- sw = self.update_acc(p['acc'])
- if sw != '9000':
- print("Programming ACC failed with code %s" % sw)
-
- # update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
- # hard linked to EF-USIM_AUTH_KEY)
- self._scc.select_path(['3f00'])
- self._scc.select_path(['a515'])
- if p.get('ki'):
- self._scc.update_binary('6f20', p['ki'], 1)
- if p.get('opc'):
- self._scc.update_binary('6f20', p['opc'], 17)
-
- # update EF-USIM_AUTH_KEY in ADF.ISIM
- if self.adf_present("isim"):
- self.select_adf_by_aid(adf="isim")
-
- if p.get('ki'):
- self._scc.update_binary('af20', p['ki'], 1)
- if p.get('opc'):
- self._scc.update_binary('af20', p['opc'], 17)
-
- # update EF.P-CSCF in ADF.ISIM
- if self.file_exists(EF_ISIM_ADF_map['PCSCF']):
- if p.get('pcscf'):
- sw = self.update_pcscf(p['pcscf'])
- else:
- sw = self.update_pcscf("")
- if sw != '9000':
- print("Programming P-CSCF failed with code %s" % sw)
-
- # update EF.DOMAIN in ADF.ISIM
- if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
- if p.get('ims_hdomain'):
- sw = self.update_domain(domain=p['ims_hdomain'])
- else:
- sw = self.update_domain()
-
- if sw != '9000':
- print(
- "Programming Home Network Domain Name failed with code %s" % sw)
-
- # update EF.IMPI in ADF.ISIM
- # TODO: Validate IMPI input
- if self.file_exists(EF_ISIM_ADF_map['IMPI']):
- if p.get('impi'):
- sw = self.update_impi(p['impi'])
- else:
- sw = self.update_impi()
- if sw != '9000':
- print("Programming IMPI failed with code %s" % sw)
-
- # update EF.IMPU in ADF.ISIM
- # TODO: Validate IMPU input
- # Support multiple IMPU if there is enough space
- if self.file_exists(EF_ISIM_ADF_map['IMPU']):
- if p.get('impu'):
- sw = self.update_impu(p['impu'])
- else:
- sw = self.update_impu()
- if sw != '9000':
- print("Programming IMPU failed with code %s" % sw)
-
- if self.adf_present("usim"):
- self.select_adf_by_aid(adf="usim")
-
- # EF.AD in ADF.USIM
- if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
- if p.get('mcc') and p.get('mnc'):
- mnc = p['mnc']
- else:
- mnc = None
- sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'),
- path=EF_USIM_ADF_map['AD'])
- if sw != '9000':
- print("Programming AD failed with code %s" % sw)
-
- # update EF-USIM_AUTH_KEY in ADF.USIM
- if p.get('ki'):
- self._scc.update_binary('af20', p['ki'], 1)
- if p.get('opc'):
- self._scc.update_binary('af20', p['opc'], 17)
-
- # update EF.EHPLMN in ADF.USIM
- if self.file_exists(EF_USIM_ADF_map['EHPLMN']):
- if p.get('mcc') and p.get('mnc'):
- sw = self.update_ehplmn(p['mcc'], p['mnc'])
- if sw != '9000':
- print("Programming EHPLMN failed with code %s" % sw)
-
- # update EF.ePDGId in ADF.USIM
- if self.file_exists(EF_USIM_ADF_map['ePDGId']):
- if p.get('epdgid'):
- sw = self.update_epdgid(p['epdgid'])
- else:
- sw = self.update_epdgid("")
- if sw != '9000':
- print("Programming ePDGId failed with code %s" % sw)
-
- # update EF.ePDGSelection in ADF.USIM
- if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
- if p.get('epdgSelection'):
- epdg_plmn = p['epdgSelection']
- sw = self.update_ePDGSelection(
- epdg_plmn[:3], epdg_plmn[3:])
- else:
- sw = self.update_ePDGSelection("", "")
- if sw != '9000':
- print("Programming ePDGSelection failed with code %s" % sw)
-
- # After successfully programming EF.ePDGId and EF.ePDGSelection,
- # Set service 106 and 107 as available in EF.UST
- # Disable service 95, 99, 115 if ISIM application is present
- if self.file_exists(EF_USIM_ADF_map['UST']):
- if p.get('epdgSelection') and p.get('epdgid'):
- sw = self.update_ust(106, 1)
- if sw != '9000':
- print("Programming UST failed with code %s" % sw)
- sw = self.update_ust(107, 1)
- if sw != '9000':
- print("Programming UST failed with code %s" % sw)
-
- sw = self.update_ust(95, 0)
- if sw != '9000':
- print("Programming UST failed with code %s" % sw)
- sw = self.update_ust(99, 0)
- if sw != '9000':
- print("Programming UST failed with code %s" % sw)
- sw = self.update_ust(115, 0)
- if sw != '9000':
- print("Programming UST failed with code %s" % sw)
-
- return
-
-class SysmoISIMSJA5(SysmoISIMSJA2):
- """
- sysmocom sysmoISIM-SJA5
- """
-
- name = 'sysmoISIM-SJA5'
-
- @classmethod
- def autodetect(kls, scc):
- try:
- # Try card model #1 (9FJ)
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 51 CC"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
- # Try card model #2 (SLM17)
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 65 F8"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
- # Try card model #3 (9FV)
- atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 59 C4"
- if scc.get_atr() == toBytes(atr):
- return kls(scc)
- except:
- return None
- return None
-
-
-class GialerSim(UsimCard):
- """
- Gialer sim cards (www.gialer.com)
- """
- name = 'gialersim'
-
- def __init__(self, ssc):
- super().__init__(ssc)
- self._program_handlers = {
- 'iccid': self.update_iccid,
- 'imsi': self.update_imsi,
- 'acc': self.update_acc,
- 'smsp': self.update_smsp,
- 'ki': self.update_ki,
- 'opc': self.update_opc,
- 'fplmn': self.update_fplmn,
- }
-
- @classmethod
- def autodetect(cls, scc):
- try:
- # Look for ATR
- if scc.get_atr() == toBytes('3B 9F 95 80 1F C7 80 31 A0 73 B6 A1 00 67 CF 32 15 CA 9C D7 09 20'):
- return cls(scc)
- except:
- return None
- return None
-
- def program(self, p):
- self.set_apdu_parameter('00', '0004')
- # Authenticate
- self._scc.verify_chv(0xc, h2b('3834373936313533'))
- for handler in self._program_handlers:
- if p.get(handler) is not None:
- self._program_handlers[handler](p[handler])
-
- mcc = p.get('mcc')
- mnc = p.get('mnc')
- has_plmn = mcc is not None and mnc is not None
- # EF.HPLMN
- if has_plmn:
- self.update_hplmn_act(mcc, mnc)
-
- # EF.AD
- if has_plmn or (p.get('opmode') is not None):
- self.update_ad(mnc=mnc, opmode=p.get('opmode'))
-
- def update_smsp(self, smsp):
- data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 80))
- return sw
-
- def update_ki(self, ki):
- self._scc.select_path(['3f00', '0001'])
- self._scc.update_binary('0001', ki)
-
- def update_opc(self, opc):
- self._scc.select_path(['3f00', '6002'])
- # No idea why the '01' is required
- self._scc.update_binary('6002', '01' + opc)
-
-
-# In order for autodetection ...
-_cards_classes = [FakeMagicSim, SuperSim, MagicSim, GrcardSim,
- SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
- FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2,
- SysmoISIMSJA5, GialerSim]
-
-
-def card_detect(ctype, scc):
- # Detect type if needed
- card = None
- ctypes = dict([(kls.name, kls) for kls in _cards_classes])
-
- if ctype == "auto":
- for kls in _cards_classes:
- card = kls.autodetect(scc)
- if card:
- print("Autodetected card type: %s" % card.name)
- card.reset()
- break
-
- if card is None:
- print("Autodetection failed")
- return None
-
- elif ctype in ctypes:
- card = ctypes[ctype](scc)
-
- else:
- raise ValueError("Unknown card type: %s" % ctype)
-
- return card
diff --git a/pySim/legacy/cards.py b/pySim/legacy/cards.py
new file mode 100644
index 0000000..401f5ed
--- /dev/null
+++ b/pySim/legacy/cards.py
@@ -0,0 +1,1665 @@
+################################################################################
+# LEGACY
+################################################################################
+
+import abc
+from smartcard.util import toBytes
+from pytlv.TLV import *
+
+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_addr_tlv, enc_addr_tlv
+from pySim.utils import is_hex, h2b, b2h, h2s, s2h
+from pySim.legacy.utils import enc_ePDGSelection, format_xplmn_w_act, format_xplmn, dec_st, enc_st
+from pySim.legacy.utils import format_ePDGSelection
+
+from pySim.ts_51_011 import EF, DF, EF_AD, EF_SPN
+from pySim.ts_31_102 import EF_USIM_ADF_map
+from pySim.ts_31_103 import EF_ISIM_ADF_map
+
+def format_addr(addr: str, addr_type: str) -> str:
+ """
+ helper function to format an FQDN (addr_type = '00') or IPv4
+ (addr_type = '01') address string into a printable string that
+ contains the hexadecimal representation and the original address
+ string (addr)
+ """
+ res = ""
+ if addr_type == '00': # FQDN
+ res += "\t%s # %s\n" % (s2h(addr), addr)
+ elif addr_type == '01': # IPv4
+ octets = addr.split(".")
+ addr_hex = ""
+ for o in octets:
+ addr_hex += ("%02x" % int(o))
+ res += "\t%s # %s\n" % (addr_hex, addr)
+ return res
+
+
+
+class SimCard(SimCardBase):
+ """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
+
+ def __init__(self, scc):
+ self._adm_chv_num = 4
+ super().__init__(scc)
+
+ def read_binary(self, ef, length=None, offset=0):
+ ef_path = ef in EF and EF[ef] or ef
+ return self._scc.read_binary(ef_path, length, offset)
+
+ def read_record(self, ef, rec_no):
+ ef_path = ef in EF and EF[ef] or ef
+ return self._scc.read_record(ef_path, rec_no)
+
+ def verify_adm(self, key):
+ """Authenticate with ADM key"""
+ (res, sw) = self._scc.verify_chv(self._adm_chv_num, key)
+ return sw
+
+ def update_iccid(self, iccid):
+ data, sw = self._scc.update_binary(EF['ICCID'], enc_iccid(iccid))
+ return sw
+
+ def read_imsi(self):
+ (res, sw) = self._scc.read_binary(EF['IMSI'])
+ if sw == '9000':
+ return (dec_imsi(res), sw)
+ else:
+ return (None, sw)
+
+ def update_imsi(self, imsi):
+ data, sw = self._scc.update_binary(EF['IMSI'], enc_imsi(imsi))
+ return sw
+
+ def update_acc(self, acc):
+ data, sw = self._scc.update_binary(EF['ACC'], lpad(acc, 4, c='0'))
+ return sw
+
+ def read_hplmn_act(self):
+ (res, sw) = self._scc.read_binary(EF['HPLMNAcT'])
+ if sw == '9000':
+ return (format_xplmn_w_act(res), sw)
+ else:
+ return (None, sw)
+
+ def update_hplmn_act(self, mcc, mnc, access_tech='FFFF'):
+ """
+ Update Home PLMN with access technology bit-field
+
+ See Section "10.3.37 EFHPLMNwAcT (HPLMN Selector with Access Technology)"
+ in ETSI TS 151 011 for the details of the access_tech field coding.
+ Some common values:
+ access_tech = '0080' # Only GSM is selected
+ access_tech = 'FFFF' # All technologies selected, even Reserved for Future Use ones
+ """
+ # get size and write EF.HPLMNwAcT
+ data = self._scc.read_binary(EF['HPLMNwAcT'], length=None, offset=0)
+ size = len(data[0]) // 2
+ hplmn = enc_plmn(mcc, mnc)
+ content = hplmn + access_tech
+ data, sw = self._scc.update_binary(
+ EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
+ return sw
+
+ def read_oplmn_act(self):
+ (res, sw) = self._scc.read_binary(EF['OPLMNwAcT'])
+ if sw == '9000':
+ return (format_xplmn_w_act(res), sw)
+ else:
+ return (None, sw)
+
+ def update_oplmn_act(self, mcc, mnc, access_tech='FFFF'):
+ """get size and write EF.OPLMNwAcT, See note in update_hplmn_act()"""
+ data = self._scc.read_binary(EF['OPLMNwAcT'], length=None, offset=0)
+ size = len(data[0]) // 2
+ hplmn = enc_plmn(mcc, mnc)
+ content = hplmn + access_tech
+ data, sw = self._scc.update_binary(
+ EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
+ return sw
+
+ def read_plmn_act(self):
+ (res, sw) = self._scc.read_binary(EF['PLMNwAcT'])
+ if sw == '9000':
+ return (format_xplmn_w_act(res), sw)
+ else:
+ return (None, sw)
+
+ def update_plmn_act(self, mcc, mnc, access_tech='FFFF'):
+ """get size and write EF.PLMNwAcT, See note in update_hplmn_act()"""
+ data = self._scc.read_binary(EF['PLMNwAcT'], length=None, offset=0)
+ size = len(data[0]) // 2
+ hplmn = enc_plmn(mcc, mnc)
+ content = hplmn + access_tech
+ data, sw = self._scc.update_binary(
+ EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
+ return sw
+
+ def update_plmnsel(self, mcc, mnc):
+ data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
+ size = len(data[0]) // 2
+ hplmn = enc_plmn(mcc, mnc)
+ data, sw = self._scc.update_binary(
+ EF['PLMNsel'], hplmn + 'ff' * (size-3))
+ return sw
+
+ def update_smsp(self, smsp):
+ data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))
+ return sw
+
+ def update_ad(self, mnc=None, opmode=None, ofm=None, path=EF['AD']):
+ """
+ Update Administrative Data (AD)
+
+ See Sec. "4.2.18 EF_AD (Administrative Data)"
+ in 3GPP TS 31.102 for the details of the EF_AD contents.
+
+ Set any parameter to None to keep old value(s) on card.
+
+ Parameters:
+ mnc (str): MNC of IMSI
+ opmode (Hex-str, 1 Byte): MS Operation Mode
+ ofm (Hex-str, 1 Byte): Operational Feature Monitor (OFM) aka Ciphering Indicator
+ path (optional list with file path e.g. ['3f00', '7f20', '6fad'])
+
+ Returns:
+ str: Return code of write operation
+ """
+
+ ad = EF_AD()
+
+ # read from card
+ raw_hex_data, sw = self._scc.read_binary(
+ path, length=None, offset=0)
+ abstract_data = ad.decode_hex(raw_hex_data)
+
+ # perform updates
+ if mnc and abstract_data['extensions']:
+ # Note: Since we derive the length of the MNC by the string length
+ # of the mnc parameter, the caller must ensure that mnc has the
+ # correct length and is padded with zeros (if necessary).
+ mnclen = len(str(mnc))
+ if mnclen > 3 or mnclen < 2:
+ raise RuntimeError('invalid length of mnc "{}", expecting 2 or 3 digits'.format(mnc))
+ abstract_data['extensions']['mnc_len'] = mnclen
+ if opmode:
+ opmode_num = int(opmode, 16)
+ if opmode_num in [int(v) for v in EF_AD.OP_MODE]:
+ abstract_data['ms_operation_mode'] = opmode_num
+ else:
+ raise RuntimeError('invalid opmode "{}"'.format(opmode))
+ if ofm:
+ abstract_data['ofm'] = bool(int(ofm, 16))
+
+ # write to card
+ raw_hex_data = ad.encode_hex(abstract_data)
+ data, sw = self._scc.update_binary(path, raw_hex_data)
+ return sw
+
+ def read_spn(self):
+ (content, sw) = self._scc.read_binary(EF['SPN'])
+ if sw == '9000':
+ abstract_data = EF_SPN().decode_hex(content)
+ show_in_hplmn = abstract_data['show_in_hplmn']
+ hide_in_oplmn = abstract_data['hide_in_oplmn']
+ name = abstract_data['spn']
+ return ((name, show_in_hplmn, hide_in_oplmn), sw)
+ else:
+ return (None, sw)
+
+ def update_spn(self, name="", show_in_hplmn=False, hide_in_oplmn=False):
+ abstract_data = {
+ 'hide_in_oplmn': hide_in_oplmn,
+ 'show_in_hplmn': show_in_hplmn,
+ 'spn': name,
+ }
+ content = EF_SPN().encode_hex(abstract_data)
+ data, sw = self._scc.update_binary(EF['SPN'], content)
+ return sw
+
+ def read_gid1(self):
+ (res, sw) = self._scc.read_binary(EF['GID1'])
+ if sw == '9000':
+ return (res, sw)
+ else:
+ return (None, sw)
+
+ def read_msisdn(self):
+ (res, sw) = self._scc.read_record(EF['MSISDN'], 1)
+ if sw == '9000':
+ return (dec_msisdn(res), sw)
+ else:
+ return (None, sw)
+
+
+class UsimCard(UiccCardBase, SimCard):
+ """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
+
+ def read_ehplmn(self):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])
+ if sw == '9000':
+ return (format_xplmn(res), sw)
+ else:
+ return (None, sw)
+
+ def update_ehplmn(self, mcc, mnc):
+ data = self._scc.read_binary(
+ EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
+ size = len(data[0]) // 2
+ ehplmn = enc_plmn(mcc, mnc)
+ data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
+ return sw
+
+ def read_fplmn(self):
+ res, sw = self._scc.read_binary(EF_USIM_ADF_map['FPLMN'])
+ if sw == '9000':
+ return format_xplmn(res), sw
+ else:
+ return None, sw
+
+ def update_fplmn(self, fplmn):
+ self._scc.select_file('3f00')
+ self.select_adf_by_aid('USIM')
+ size = self._scc.binary_size(EF_USIM_ADF_map['FPLMN'])
+ encoded = ''.join([enc_plmn(plmn[:3], plmn[3:]) for plmn in fplmn])
+ encoded = rpad(encoded, size)
+ data, sw = self._scc.update_binary(EF_USIM_ADF_map['FPLMN'], encoded)
+ return sw
+
+ def read_epdgid(self):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGId'])
+ if sw == '9000':
+ try:
+ addr, addr_type = dec_addr_tlv(res)
+ except:
+ addr = None
+ addr_type = None
+ return (format_addr(addr, addr_type), sw)
+ else:
+ return (None, sw)
+
+ def update_epdgid(self, epdgid):
+ size = self._scc.binary_size(EF_USIM_ADF_map['ePDGId']) * 2
+ if len(epdgid) > 0:
+ addr_type = get_addr_type(epdgid)
+ if addr_type == None:
+ raise ValueError(
+ "Unknown ePDG Id address type or invalid address provided")
+ epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
+ else:
+ epdgid_tlv = rpad('ff', size)
+ data, sw = self._scc.update_binary(
+ EF_USIM_ADF_map['ePDGId'], epdgid_tlv)
+ return sw
+
+ def read_ePDGSelection(self):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'])
+ if sw == '9000':
+ return (format_ePDGSelection(res), sw)
+ else:
+ return (None, sw)
+
+ def update_ePDGSelection(self, mcc, mnc):
+ (res, sw) = self._scc.read_binary(
+ EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
+ if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
+ # Reset contents
+ # 80 - Tag value
+ (res, sw) = self._scc.update_binary(
+ EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
+ elif sw == '9000':
+ (res, sw) = self._scc.update_binary(
+ EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
+ return sw
+
+ def read_ust(self):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
+ if sw == '9000':
+ # Print those which are available
+ return ([res, dec_st(res, table="usim")], sw)
+ else:
+ return ([None, None], sw)
+
+ def update_ust(self, service, bit=1):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
+ if sw == '9000':
+ content = enc_st(res, service, bit)
+ (res, sw) = self._scc.update_binary(
+ EF_USIM_ADF_map['UST'], content)
+ return sw
+
+ def update_est(self, service, bit=1):
+ (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EST'])
+ if sw == '9000':
+ content = enc_st(res, service, bit)
+ (res, sw) = self._scc.update_binary(
+ EF_USIM_ADF_map['EST'], content)
+ return sw
+
+
+
+class IsimCard(UiccCardBase):
+ """Higher-layer class that is used *only* by legacy pySim-{prog,read}."""
+
+ name = 'ISIM'
+
+ def read_pcscf(self):
+ rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['PCSCF'])
+ pcscf_recs = ""
+ for i in range(0, rec_cnt):
+ (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['PCSCF'], i + 1)
+ if sw == '9000':
+ try:
+ addr, addr_type = dec_addr_tlv(res)
+ except:
+ addr = None
+ addr_type = None
+ content = format_addr(addr, addr_type)
+ pcscf_recs += "%s" % (len(content)
+ and content or '\tNot available\n')
+ else:
+ pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (
+ sw)
+ return pcscf_recs
+
+ def update_pcscf(self, pcscf):
+ if len(pcscf) > 0:
+ addr_type = get_addr_type(pcscf)
+ if addr_type == None:
+ raise ValueError(
+ "Unknown PCSCF address type or invalid address provided")
+ content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
+ else:
+ # Just the tag value
+ content = '80'
+ rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
+ pcscf_tlv = rpad(content, rec_size_bytes*2)
+ data, sw = self._scc.update_record(
+ EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
+ return sw
+
+ def read_domain(self):
+ (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
+ if sw == '9000':
+ # Skip the initial tag value ('80') byte and get length of contents
+ length = int(res[2:4], 16)
+ content = h2s(res[4:4+(length*2)])
+ return (content, sw)
+ else:
+ return (None, sw)
+
+ def update_domain(self, domain=None, mcc=None, mnc=None):
+ hex_str = ""
+ if domain:
+ hex_str = s2h(domain)
+ elif mcc and mnc:
+ # MCC and MNC always has 3 digits in domain form
+ plmn_str = 'mnc' + lpad(mnc, 3, "0") + '.mcc' + lpad(mcc, 3, "0")
+ hex_str = s2h('ims.' + plmn_str + '.3gppnetwork.org')
+
+ # Build TLV
+ tlv = TLV(['80'])
+ content = tlv.build({'80': hex_str})
+
+ bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
+ data, sw = self._scc.update_binary(
+ EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
+ return sw
+
+ def read_impi(self):
+ (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
+ if sw == '9000':
+ # Skip the initial tag value ('80') byte and get length of contents
+ length = int(res[2:4], 16)
+ content = h2s(res[4:4+(length*2)])
+ return (content, sw)
+ else:
+ return (None, sw)
+
+ def update_impi(self, impi=None):
+ hex_str = ""
+ if impi:
+ hex_str = s2h(impi)
+ # Build TLV
+ tlv = TLV(['80'])
+ content = tlv.build({'80': hex_str})
+
+ bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
+ data, sw = self._scc.update_binary(
+ EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
+ return sw
+
+ def read_impu(self):
+ rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['IMPU'])
+ impu_recs = ""
+ for i in range(0, rec_cnt):
+ (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
+ if sw == '9000':
+ # Skip the initial tag value ('80') byte and get length of contents
+ length = int(res[2:4], 16)
+ content = h2s(res[4:4+(length*2)])
+ impu_recs += "\t%s\n" % (len(content)
+ and content or 'Not available')
+ else:
+ impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (
+ sw)
+ return impu_recs
+
+ def update_impu(self, impu=None):
+ hex_str = ""
+ if impu:
+ hex_str = s2h(impu)
+ # Build TLV
+ tlv = TLV(['80'])
+ content = tlv.build({'80': hex_str})
+
+ rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
+ impu_tlv = rpad(content, rec_size_bytes*2)
+ data, sw = self._scc.update_record(
+ EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
+ return sw
+
+ def read_iari(self):
+ rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
+ uiari_recs = ""
+ for i in range(0, rec_cnt):
+ (res, sw) = self._scc.read_record(
+ EF_ISIM_ADF_map['UICCIARI'], i + 1)
+ if sw == '9000':
+ # Skip the initial tag value ('80') byte and get length of contents
+ length = int(res[2:4], 16)
+ content = h2s(res[4:4+(length*2)])
+ uiari_recs += "\t%s\n" % (len(content)
+ and content or 'Not available')
+ else:
+ uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (
+ sw)
+ return uiari_recs
+
+ def update_ist(self, service, bit=1):
+ (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IST'])
+ if sw == '9000':
+ content = enc_st(res, service, bit)
+ (res, sw) = self._scc.update_binary(
+ EF_ISIM_ADF_map['IST'], content)
+ return sw
+
+
+class MagicSimBase(abc.ABC, SimCard):
+ """
+ Theses cards uses several record based EFs to store the provider infos,
+ each possible provider uses a specific record number in each EF. The
+ indexes used are ( where N is the number of providers supported ) :
+ - [2 .. N+1] for the operator name
+ - [1 .. N] for the programmable EFs
+
+ * 3f00/7f4d/8f0c : Operator Name
+
+ bytes 0-15 : provider name, padded with 0xff
+ byte 16 : length of the provider name
+ byte 17 : 01 for valid records, 00 otherwise
+
+ * 3f00/7f4d/8f0d : Programmable Binary EFs
+
+ * 3f00/7f4d/8f0e : Programmable Record EFs
+
+ """
+
+ _files = {} # type: Dict[str, Tuple[str, int, bool]]
+ _ki_file = None # type: Optional[str]
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ for p, l, t in kls._files.values():
+ if not t:
+ continue
+ if scc.record_size(['3f00', '7f4d', p]) != l:
+ return None
+ except:
+ return None
+
+ return kls(scc)
+
+ def _get_count(self):
+ """
+ Selects the file and returns the total number of entries
+ and entry size
+ """
+ f = self._files['name']
+
+ r = self._scc.select_path(['3f00', '7f4d', f[0]])
+ rec_len = int(r[-1][28:30], 16)
+ tlen = int(r[-1][4:8], 16)
+ rec_cnt = (tlen // rec_len) - 1
+
+ if (rec_cnt < 1) or (rec_len != f[1]):
+ raise RuntimeError('Bad card type')
+
+ return rec_cnt
+
+ def program(self, p):
+ # Go to dir
+ self._scc.select_path(['3f00', '7f4d'])
+
+ # Home PLMN in PLMN_Sel format
+ hplmn = enc_plmn(p['mcc'], p['mnc'])
+
+ # Operator name ( 3f00/7f4d/8f0c )
+ self._scc.update_record(self._files['name'][0], 2,
+ rpad(b2h(p['name']), 32) + ('%02x' %
+ len(p['name'])) + '01'
+ )
+
+ # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
+ v = ''
+
+ # inline Ki
+ if self._ki_file is None:
+ v += p['ki']
+
+ # ICCID
+ v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
+
+ # IMSI
+ v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
+
+ # Ki
+ if self._ki_file:
+ v += self._ki_file + '10' + p['ki']
+
+ # PLMN_Sel
+ v += '6f30' + '18' + rpad(hplmn, 36)
+
+ # ACC
+ # This doesn't work with "fake" SuperSIM cards,
+ # but will hopefully work with real SuperSIMs.
+ if p.get('acc') is not None:
+ v += '6f78' + '02' + lpad(p['acc'], 4)
+
+ self._scc.update_record(self._files['b_ef'][0], 1,
+ rpad(v, self._files['b_ef'][1]*2)
+ )
+
+ # SMSP ( 3f00/7f4d/8f0e )
+ # FIXME
+
+ # Write PLMN_Sel forcefully as well
+ r = self._scc.select_path(['3f00', '7f20', '6f30'])
+ tl = int(r[-1][4:8], 16)
+
+ hplmn = enc_plmn(p['mcc'], p['mnc'])
+ self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
+
+ def erase(self):
+ # Dummy
+ df = {}
+ for k, v in self._files.items():
+ ofs = 1
+ fv = v[1] * 'ff'
+ if k == 'name':
+ ofs = 2
+ fv = fv[0:-4] + '0000'
+ df[v[0]] = (fv, ofs)
+
+ # Write
+ for n in range(0, self._get_count()):
+ for k, (msg, ofs) in df.items():
+ self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
+
+
+class SuperSim(MagicSimBase):
+
+ name = 'supersim'
+
+ _files = {
+ 'name': ('8f0c', 18, True),
+ 'b_ef': ('8f0d', 74, True),
+ 'r_ef': ('8f0e', 50, True),
+ }
+
+ _ki_file = None
+
+
+class MagicSim(MagicSimBase):
+
+ name = 'magicsim'
+
+ _files = {
+ 'name': ('8f0c', 18, True),
+ 'b_ef': ('8f0d', 130, True),
+ 'r_ef': ('8f0e', 102, False),
+ }
+
+ _ki_file = '6f1b'
+
+
+class FakeMagicSim(SimCard):
+ """
+ Theses cards have a record based EF 3f00/000c that contains the provider
+ information. See the program method for its format. The records go from
+ 1 to N.
+ """
+
+ name = 'fakemagicsim'
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ if scc.record_size(['3f00', '000c']) != 0x5a:
+ return None
+ except:
+ return None
+
+ return kls(scc)
+
+ def _get_infos(self):
+ """
+ Selects the file and returns the total number of entries
+ and entry size
+ """
+
+ r = self._scc.select_path(['3f00', '000c'])
+ rec_len = int(r[-1][28:30], 16)
+ tlen = int(r[-1][4:8], 16)
+ rec_cnt = (tlen // rec_len) - 1
+
+ if (rec_cnt < 1) or (rec_len != 0x5a):
+ raise RuntimeError('Bad card type')
+
+ return rec_cnt, rec_len
+
+ def program(self, p):
+ # Home PLMN
+ r = self._scc.select_path(['3f00', '7f20', '6f30'])
+ tl = int(r[-1][4:8], 16)
+
+ hplmn = enc_plmn(p['mcc'], p['mnc'])
+ self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
+
+ # Get total number of entries and entry size
+ rec_cnt, rec_len = self._get_infos()
+
+ # Set first entry
+ entry = (
+ '81' + # 1b Status: Valid & Active
+ rpad(s2h(p['name'][0:14]), 28) + # 14b Entry Name
+ enc_iccid(p['iccid']) + # 10b ICCID
+ enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
+ p['ki'] + # 16b Ki
+ lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
+ )
+ self._scc.update_record('000c', 1, entry)
+
+ def erase(self):
+ # Get total number of entries and entry size
+ rec_cnt, rec_len = self._get_infos()
+
+ # Erase all entries
+ entry = 'ff' * rec_len
+ for i in range(0, rec_cnt):
+ self._scc.update_record('000c', 1+i, entry)
+
+
+class GrcardSim(SimCard):
+ """
+ Greencard (grcard.cn) HZCOS GSM SIM
+ These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
+ and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
+ """
+
+ name = 'grcardsim'
+
+ @classmethod
+ def autodetect(kls, scc):
+ return None
+
+ def program(self, p):
+ # We don't really know yet what ADM PIN 4 is about
+ #self._scc.verify_chv(4, h2b("4444444444444444"))
+
+ # Authenticate using ADM PIN 5
+ if p['pin_adm']:
+ pin = h2b(p['pin_adm'])
+ else:
+ pin = h2b("4444444444444444")
+ self._scc.verify_chv(5, pin)
+
+ # EF.ICCID
+ r = self._scc.select_path(['3f00', '2fe2'])
+ data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
+
+ # EF.IMSI
+ r = self._scc.select_path(['3f00', '7f20', '6f07'])
+ data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
+
+ # EF.ACC
+ if p.get('acc') is not None:
+ data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
+
+ # EF.SMSP
+ if p.get('smsp'):
+ r = self._scc.select_path(['3f00', '7f10', '6f42'])
+ data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
+
+ # Set the Ki using proprietary command
+ pdu = '80d4020010' + p['ki']
+ data, sw = self._scc._tp.send_apdu(pdu)
+
+ # EF.HPLMN
+ r = self._scc.select_path(['3f00', '7f20', '6f30'])
+ size = int(r[-1][4:8], 16)
+ hplmn = enc_plmn(p['mcc'], p['mnc'])
+ self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
+
+ # EF.SPN (Service Provider Name)
+ r = self._scc.select_path(['3f00', '7f20', '6f30'])
+ size = int(r[-1][4:8], 16)
+ # FIXME
+
+ # FIXME: EF.MSISDN
+
+
+class SysmoSIMgr1(GrcardSim):
+ """
+ sysmocom sysmoSIM-GR1
+ These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
+ and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
+ """
+ name = 'sysmosim-gr1'
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 99 18 00 11 88 22 33 44 55 66 77 60"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+
+class SysmoUSIMgr1(UsimCard):
+ """
+ sysmocom sysmoUSIM-GR1
+ """
+ name = 'sysmoUSIM-GR1'
+
+ @classmethod
+ def autodetect(kls, scc):
+ # TODO: Access the ATR
+ return None
+
+ def program(self, p):
+ # TODO: check if verify_chv could be used or what it needs
+ # self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
+ # Unlock the card..
+ data, sw = self._scc._tp.send_apdu_checksw(
+ "0020000A083332323133323332")
+
+ # TODO: move into SimCardCommands
+ par = (p['ki'] + # 16b K
+ p['opc'] + # 32b OPC
+ enc_iccid(p['iccid']) + # 10b ICCID
+ enc_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
+ )
+ data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
+
+
+class SysmoSIMgr2(SimCard):
+ """
+ sysmocom sysmoSIM-GR2
+ """
+
+ name = 'sysmoSIM-GR2'
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def program(self, p):
+
+ # select MF
+ r = self._scc.select_path(['3f00'])
+
+ # authenticate as SUPER ADM using default key
+ self._scc.verify_chv(0x0b, h2b("3838383838383838"))
+
+ # set ADM pin using proprietary command
+ # INS: D4
+ # P1: 3A for PIN, 3B for PUK
+ # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
+ # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
+ if p['pin_adm']:
+ pin = h2b(p['pin_adm'])
+ else:
+ pin = h2b("4444444444444444")
+
+ pdu = 'A0D43A0508' + b2h(pin)
+ data, sw = self._scc._tp.send_apdu(pdu)
+
+ # authenticate as ADM (enough to write file, and can set PINs)
+
+ self._scc.verify_chv(0x05, pin)
+
+ # write EF.ICCID
+ data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
+
+ # select DF_GSM
+ r = self._scc.select_path(['7f20'])
+
+ # write EF.IMSI
+ data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
+
+ # write EF.ACC
+ if p.get('acc') is not None:
+ data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
+
+ # get size and write EF.HPLMN
+ r = self._scc.select_path(['6f30'])
+ size = int(r[-1][4:8], 16)
+ hplmn = enc_plmn(p['mcc'], p['mnc'])
+ self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
+
+ # set COMP128 version 0 in proprietary file
+ data, sw = self._scc.update_binary('0001', '001000')
+
+ # set Ki in proprietary file
+ data, sw = self._scc.update_binary('0001', p['ki'], 3)
+
+ # select DF_TELECOM
+ r = self._scc.select_path(['3f00', '7f10'])
+
+ # write EF.SMSP
+ if p.get('smsp'):
+ data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
+
+
+class SysmoUSIMSJS1(UsimCard):
+ """
+ sysmocom sysmoUSIM-SJS1
+ """
+
+ name = 'sysmoUSIM-SJS1'
+
+ def __init__(self, ssc):
+ super(SysmoUSIMSJS1, self).__init__(ssc)
+ self._scc.cla_byte = "00"
+ self._scc.sel_ctrl = "0004" # request an FCP
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def verify_adm(self, key):
+ # authenticate as ADM using default key (written on the card..)
+ if not key:
+ raise ValueError(
+ "Please provide a PIN-ADM as there is no default one")
+ (res, sw) = self._scc.verify_chv(0x0A, key)
+ return sw
+
+ def program(self, p):
+ self.verify_adm(h2b(p['pin_adm']))
+
+ # select MF
+ r = self._scc.select_path(['3f00'])
+
+ # write EF.ICCID
+ data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
+
+ # select DF_GSM
+ r = self._scc.select_path(['7f20'])
+
+ # set Ki in proprietary file
+ data, sw = self._scc.update_binary('00FF', p['ki'])
+
+ # set OPc in proprietary file
+ if 'opc' in p:
+ content = "01" + p['opc']
+ data, sw = self._scc.update_binary('00F7', content)
+
+ # set Service Provider Name
+ if p.get('name') is not None:
+ self.update_spn(p['name'], True, True)
+
+ if p.get('acc') is not None:
+ self.update_acc(p['acc'])
+
+ # write EF.IMSI
+ data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
+
+ # EF.PLMNsel
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmnsel(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNsel failed with code %s" % sw)
+
+ # EF.PLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNwAcT failed with code %s" % sw)
+
+ # EF.OPLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_oplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming OPLMNwAcT failed with code %s" % sw)
+
+ # EF.HPLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_hplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming HPLMNwAcT failed with code %s" % sw)
+
+ # EF.AD
+ if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
+ if p.get('mcc') and p.get('mnc'):
+ mnc = p['mnc']
+ else:
+ mnc = None
+ sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
+ if sw != '9000':
+ print("Programming AD failed with code %s" % sw)
+
+ # EF.SMSP
+ if p.get('smsp'):
+ r = self._scc.select_path(['3f00', '7f10'])
+ data, sw = self._scc.update_record(
+ '6f42', 1, lpad(p['smsp'], 104), force_len=True)
+
+ # EF.MSISDN
+ # TODO: Alpha Identifier (currently 'ff'O * 20)
+ # TODO: Capability/Configuration1 Record Identifier
+ # TODO: Extension1 Record Identifier
+ if p.get('msisdn') is not None:
+ msisdn = enc_msisdn(p['msisdn'])
+ data = 'ff' * 20 + msisdn
+
+ r = self._scc.select_path(['3f00', '7f10'])
+ data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
+
+
+class FairwavesSIM(UsimCard):
+ """
+ FairwavesSIM
+
+ The SIM card is operating according to the standard.
+ For Ki/OP/OPC programming the following files are additionally open for writing:
+ 3F00/7F20/FF01 – OP/OPC:
+ byte 1 = 0x01, bytes 2-17: OPC;
+ byte 1 = 0x00, bytes 2-17: OP;
+ 3F00/7F20/FF02: Ki
+ """
+
+ name = 'Fairwaves-SIM'
+ # Propriatary files
+ _EF_num = {
+ 'Ki': 'FF02',
+ 'OP/OPC': 'FF01',
+ }
+ _EF = {
+ 'Ki': DF['GSM']+[_EF_num['Ki']],
+ 'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
+ }
+
+ def __init__(self, ssc):
+ super(FairwavesSIM, self).__init__(ssc)
+ self._adm_chv_num = 0x11
+ self._adm2_chv_num = 0x12
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 44 22 06 10 00 00 01 A9"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def verify_adm2(self, key):
+ '''
+ Authenticate with ADM2 key.
+
+ Fairwaves SIM cards support hierarchical key structure and ADM2 key
+ is a key which has access to proprietary files (Ki and OP/OPC).
+ That said, ADM key inherits permissions of ADM2 key and thus we rarely
+ need ADM2 key per se.
+ '''
+ (res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
+ return sw
+
+ def read_ki(self):
+ """
+ Read Ki in proprietary file.
+
+ Requires ADM1 access level
+ """
+ return self._scc.read_binary(self._EF['Ki'])
+
+ def update_ki(self, ki):
+ """
+ Set Ki in proprietary file.
+
+ Requires ADM1 access level
+ """
+ data, sw = self._scc.update_binary(self._EF['Ki'], ki)
+ return sw
+
+ def read_op_opc(self):
+ """
+ Read Ki in proprietary file.
+
+ Requires ADM1 access level
+ """
+ (ef, sw) = self._scc.read_binary(self._EF['OP/OPC'])
+ type = 'OP' if ef[0:2] == '00' else 'OPC'
+ return ((type, ef[2:]), sw)
+
+ def update_op(self, op):
+ """
+ Set OP in proprietary file.
+
+ Requires ADM1 access level
+ """
+ content = '00' + op
+ data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
+ return sw
+
+ def update_opc(self, opc):
+ """
+ Set OPC in proprietary file.
+
+ Requires ADM1 access level
+ """
+ content = '01' + opc
+ data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
+ return sw
+
+ def program(self, p):
+ # For some reason the card programming only works when the card
+ # is handled as a classic SIM, even though it is an USIM, so we
+ # reconfigure the class byte and the select control field on
+ # the fly. When the programming is done the original values are
+ # restored.
+ cla_byte_orig = self._scc.cla_byte
+ sel_ctrl_orig = self._scc.sel_ctrl
+ self._scc.cla_byte = "a0"
+ self._scc.sel_ctrl = "0000"
+
+ try:
+ self._program(p)
+ finally:
+ # restore original cla byte and sel ctrl
+ self._scc.cla_byte = cla_byte_orig
+ self._scc.sel_ctrl = sel_ctrl_orig
+
+ def _program(self, p):
+ # authenticate as ADM1
+ if not p['pin_adm']:
+ raise ValueError(
+ "Please provide a PIN-ADM as there is no default one")
+ self.verify_adm(h2b(p['pin_adm']))
+
+ # TODO: Set operator name
+ if p.get('smsp') is not None:
+ sw = self.update_smsp(p['smsp'])
+ if sw != '9000':
+ print("Programming SMSP failed with code %s" % sw)
+ # This SIM doesn't support changing ICCID
+ if p.get('mcc') is not None and p.get('mnc') is not None:
+ sw = self.update_hplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming MCC/MNC failed with code %s" % sw)
+ if p.get('imsi') is not None:
+ sw = self.update_imsi(p['imsi'])
+ if sw != '9000':
+ print("Programming IMSI failed with code %s" % sw)
+ if p.get('ki') is not None:
+ sw = self.update_ki(p['ki'])
+ if sw != '9000':
+ print("Programming Ki failed with code %s" % sw)
+ if p.get('opc') is not None:
+ sw = self.update_opc(p['opc'])
+ if sw != '9000':
+ print("Programming OPC failed with code %s" % sw)
+ if p.get('acc') is not None:
+ sw = self.update_acc(p['acc'])
+ if sw != '9000':
+ print("Programming ACC failed with code %s" % sw)
+
+
+class OpenCellsSim(SimCard):
+ """
+ OpenCellsSim
+
+ """
+
+ name = 'OpenCells-SIM'
+
+ def __init__(self, ssc):
+ super(OpenCellsSim, self).__init__(ssc)
+ self._adm_chv_num = 0x0A
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 9F 95 80 1F C3 80 31 E0 73 FE 21 13 57 86 81 02 86 98 44 18 A8"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def program(self, p):
+ if not p['pin_adm']:
+ raise ValueError(
+ "Please provide a PIN-ADM as there is no default one")
+ self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
+
+ # select MF
+ r = self._scc.select_path(['3f00'])
+
+ # write EF.ICCID
+ data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
+
+ r = self._scc.select_path(['7ff0'])
+
+ # set Ki in proprietary file
+ data, sw = self._scc.update_binary('FF02', p['ki'])
+
+ # set OPC in proprietary file
+ data, sw = self._scc.update_binary('FF01', p['opc'])
+
+ # select DF_GSM
+ r = self._scc.select_path(['7f20'])
+
+ # write EF.IMSI
+ data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
+
+
+class WavemobileSim(UsimCard):
+ """
+ WavemobileSim
+
+ """
+
+ name = 'Wavemobile-SIM'
+
+ def __init__(self, ssc):
+ super(WavemobileSim, self).__init__(ssc)
+ self._adm_chv_num = 0x0A
+ self._scc.cla_byte = "00"
+ self._scc.sel_ctrl = "0004" # request an FCP
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes("3B 9F 95 80 1F C7 80 31 E0 73 F6 21 13 67 4D 45 16 00 43 01 00 8F"):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def program(self, p):
+ if not p['pin_adm']:
+ raise ValueError(
+ "Please provide a PIN-ADM as there is no default one")
+ self.verify_adm(h2b(p['pin_adm']))
+
+ # EF.ICCID
+ # TODO: Add programming of the ICCID
+ if p.get('iccid'):
+ print(
+ "Warning: Programming of the ICCID is not implemented for this type of card.")
+
+ # KI (Presumably a proprietary file)
+ # TODO: Add programming of KI
+ if p.get('ki'):
+ print(
+ "Warning: Programming of the KI is not implemented for this type of card.")
+
+ # OPc (Presumably a proprietary file)
+ # TODO: Add programming of OPc
+ if p.get('opc'):
+ print(
+ "Warning: Programming of the OPc is not implemented for this type of card.")
+
+ # EF.SMSP
+ if p.get('smsp'):
+ sw = self.update_smsp(p['smsp'])
+ if sw != '9000':
+ print("Programming SMSP failed with code %s" % sw)
+
+ # EF.IMSI
+ if p.get('imsi'):
+ sw = self.update_imsi(p['imsi'])
+ if sw != '9000':
+ print("Programming IMSI failed with code %s" % sw)
+
+ # EF.ACC
+ if p.get('acc'):
+ sw = self.update_acc(p['acc'])
+ if sw != '9000':
+ print("Programming ACC failed with code %s" % sw)
+
+ # EF.PLMNsel
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmnsel(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNsel failed with code %s" % sw)
+
+ # EF.PLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNwAcT failed with code %s" % sw)
+
+ # EF.OPLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_oplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming OPLMNwAcT failed with code %s" % sw)
+
+ # EF.AD
+ if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
+ if p.get('mcc') and p.get('mnc'):
+ mnc = p['mnc']
+ else:
+ mnc = None
+ sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
+ if sw != '9000':
+ print("Programming AD failed with code %s" % sw)
+
+ return None
+
+
+class SysmoISIMSJA2(UsimCard, IsimCard):
+ """
+ sysmocom sysmoISIM-SJA2
+ """
+
+ name = 'sysmoISIM-SJA2'
+
+ def __init__(self, ssc):
+ super(SysmoISIMSJA2, self).__init__(ssc)
+ self._scc.cla_byte = "00"
+ self._scc.sel_ctrl = "0004" # request an FCP
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Try card model #1
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+
+ # Try card model #2
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+
+ # Try card model #3
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+ def verify_adm(self, key):
+ # authenticate as ADM using default key (written on the card..)
+ if not key:
+ raise ValueError(
+ "Please provide a PIN-ADM as there is no default one")
+ (res, sw) = self._scc.verify_chv(0x0A, key)
+ return sw
+
+ def program(self, p):
+ self.verify_adm(h2b(p['pin_adm']))
+
+ # Populate AIDs
+ self.read_aids()
+
+ # This type of card does not allow to reprogram the ICCID.
+ # Reprogramming the ICCID would mess up the card os software
+ # license management, so the ICCID must be kept at its factory
+ # setting!
+ if p.get('iccid'):
+ print(
+ "Warning: Programming of the ICCID is not implemented for this type of card.")
+
+ # select DF_GSM
+ self._scc.select_path(['7f20'])
+
+ # set Service Provider Name
+ if p.get('name') is not None:
+ self.update_spn(p['name'], True, True)
+
+ # write EF.IMSI
+ if p.get('imsi'):
+ self._scc.update_binary('6f07', enc_imsi(p['imsi']))
+
+ # EF.PLMNsel
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmnsel(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNsel failed with code %s" % sw)
+
+ # EF.PLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_plmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming PLMNwAcT failed with code %s" % sw)
+
+ # EF.OPLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_oplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming OPLMNwAcT failed with code %s" % sw)
+
+ # EF.HPLMNwAcT
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_hplmn_act(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming HPLMNwAcT failed with code %s" % sw)
+
+ # EF.AD
+ if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
+ if p.get('mcc') and p.get('mnc'):
+ mnc = p['mnc']
+ else:
+ mnc = None
+ sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
+ if sw != '9000':
+ print("Programming AD failed with code %s" % sw)
+
+ # EF.SMSP
+ if p.get('smsp'):
+ r = self._scc.select_path(['3f00', '7f10'])
+ data, sw = self._scc.update_record(
+ '6f42', 1, lpad(p['smsp'], 104), force_len=True)
+
+ # EF.MSISDN
+ # TODO: Alpha Identifier (currently 'ff'O * 20)
+ # TODO: Capability/Configuration1 Record Identifier
+ # TODO: Extension1 Record Identifier
+ if p.get('msisdn') is not None:
+ msisdn = enc_msisdn(p['msisdn'])
+ content = 'ff' * 20 + msisdn
+
+ r = self._scc.select_path(['3f00', '7f10'])
+ data, sw = self._scc.update_record(
+ '6F40', 1, content, force_len=True)
+
+ # EF.ACC
+ if p.get('acc'):
+ sw = self.update_acc(p['acc'])
+ if sw != '9000':
+ print("Programming ACC failed with code %s" % sw)
+
+ # update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
+ # hard linked to EF-USIM_AUTH_KEY)
+ self._scc.select_path(['3f00'])
+ self._scc.select_path(['a515'])
+ if p.get('ki'):
+ self._scc.update_binary('6f20', p['ki'], 1)
+ if p.get('opc'):
+ self._scc.update_binary('6f20', p['opc'], 17)
+
+ # update EF-USIM_AUTH_KEY in ADF.ISIM
+ if self.adf_present("isim"):
+ self.select_adf_by_aid(adf="isim")
+
+ if p.get('ki'):
+ self._scc.update_binary('af20', p['ki'], 1)
+ if p.get('opc'):
+ self._scc.update_binary('af20', p['opc'], 17)
+
+ # update EF.P-CSCF in ADF.ISIM
+ if self.file_exists(EF_ISIM_ADF_map['PCSCF']):
+ if p.get('pcscf'):
+ sw = self.update_pcscf(p['pcscf'])
+ else:
+ sw = self.update_pcscf("")
+ if sw != '9000':
+ print("Programming P-CSCF failed with code %s" % sw)
+
+ # update EF.DOMAIN in ADF.ISIM
+ if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
+ if p.get('ims_hdomain'):
+ sw = self.update_domain(domain=p['ims_hdomain'])
+ else:
+ sw = self.update_domain()
+
+ if sw != '9000':
+ print(
+ "Programming Home Network Domain Name failed with code %s" % sw)
+
+ # update EF.IMPI in ADF.ISIM
+ # TODO: Validate IMPI input
+ if self.file_exists(EF_ISIM_ADF_map['IMPI']):
+ if p.get('impi'):
+ sw = self.update_impi(p['impi'])
+ else:
+ sw = self.update_impi()
+ if sw != '9000':
+ print("Programming IMPI failed with code %s" % sw)
+
+ # update EF.IMPU in ADF.ISIM
+ # TODO: Validate IMPU input
+ # Support multiple IMPU if there is enough space
+ if self.file_exists(EF_ISIM_ADF_map['IMPU']):
+ if p.get('impu'):
+ sw = self.update_impu(p['impu'])
+ else:
+ sw = self.update_impu()
+ if sw != '9000':
+ print("Programming IMPU failed with code %s" % sw)
+
+ if self.adf_present("usim"):
+ self.select_adf_by_aid(adf="usim")
+
+ # EF.AD in ADF.USIM
+ if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
+ if p.get('mcc') and p.get('mnc'):
+ mnc = p['mnc']
+ else:
+ mnc = None
+ sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'),
+ path=EF_USIM_ADF_map['AD'])
+ if sw != '9000':
+ print("Programming AD failed with code %s" % sw)
+
+ # update EF-USIM_AUTH_KEY in ADF.USIM
+ if p.get('ki'):
+ self._scc.update_binary('af20', p['ki'], 1)
+ if p.get('opc'):
+ self._scc.update_binary('af20', p['opc'], 17)
+
+ # update EF.EHPLMN in ADF.USIM
+ if self.file_exists(EF_USIM_ADF_map['EHPLMN']):
+ if p.get('mcc') and p.get('mnc'):
+ sw = self.update_ehplmn(p['mcc'], p['mnc'])
+ if sw != '9000':
+ print("Programming EHPLMN failed with code %s" % sw)
+
+ # update EF.ePDGId in ADF.USIM
+ if self.file_exists(EF_USIM_ADF_map['ePDGId']):
+ if p.get('epdgid'):
+ sw = self.update_epdgid(p['epdgid'])
+ else:
+ sw = self.update_epdgid("")
+ if sw != '9000':
+ print("Programming ePDGId failed with code %s" % sw)
+
+ # update EF.ePDGSelection in ADF.USIM
+ if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
+ if p.get('epdgSelection'):
+ epdg_plmn = p['epdgSelection']
+ sw = self.update_ePDGSelection(
+ epdg_plmn[:3], epdg_plmn[3:])
+ else:
+ sw = self.update_ePDGSelection("", "")
+ if sw != '9000':
+ print("Programming ePDGSelection failed with code %s" % sw)
+
+ # After successfully programming EF.ePDGId and EF.ePDGSelection,
+ # Set service 106 and 107 as available in EF.UST
+ # Disable service 95, 99, 115 if ISIM application is present
+ if self.file_exists(EF_USIM_ADF_map['UST']):
+ if p.get('epdgSelection') and p.get('epdgid'):
+ sw = self.update_ust(106, 1)
+ if sw != '9000':
+ print("Programming UST failed with code %s" % sw)
+ sw = self.update_ust(107, 1)
+ if sw != '9000':
+ print("Programming UST failed with code %s" % sw)
+
+ sw = self.update_ust(95, 0)
+ if sw != '9000':
+ print("Programming UST failed with code %s" % sw)
+ sw = self.update_ust(99, 0)
+ if sw != '9000':
+ print("Programming UST failed with code %s" % sw)
+ sw = self.update_ust(115, 0)
+ if sw != '9000':
+ print("Programming UST failed with code %s" % sw)
+
+ return
+
+class SysmoISIMSJA5(SysmoISIMSJA2):
+ """
+ sysmocom sysmoISIM-SJA5
+ """
+
+ name = 'sysmoISIM-SJA5'
+
+ @classmethod
+ def autodetect(kls, scc):
+ try:
+ # Try card model #1 (9FJ)
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 51 CC"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+ # Try card model #2 (SLM17)
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 65 F8"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+ # Try card model #3 (9FV)
+ atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 59 C4"
+ if scc.get_atr() == toBytes(atr):
+ return kls(scc)
+ except:
+ return None
+ return None
+
+
+class GialerSim(UsimCard):
+ """
+ Gialer sim cards (www.gialer.com)
+ """
+ name = 'gialersim'
+
+ def __init__(self, ssc):
+ super().__init__(ssc)
+ self._program_handlers = {
+ 'iccid': self.update_iccid,
+ 'imsi': self.update_imsi,
+ 'acc': self.update_acc,
+ 'smsp': self.update_smsp,
+ 'ki': self.update_ki,
+ 'opc': self.update_opc,
+ 'fplmn': self.update_fplmn,
+ }
+
+ @classmethod
+ def autodetect(cls, scc):
+ try:
+ # Look for ATR
+ if scc.get_atr() == toBytes('3B 9F 95 80 1F C7 80 31 A0 73 B6 A1 00 67 CF 32 15 CA 9C D7 09 20'):
+ return cls(scc)
+ except:
+ return None
+ return None
+
+ def program(self, p):
+ self.set_apdu_parameter('00', '0004')
+ # Authenticate
+ self._scc.verify_chv(0xc, h2b('3834373936313533'))
+ for handler in self._program_handlers:
+ if p.get(handler) is not None:
+ self._program_handlers[handler](p[handler])
+
+ mcc = p.get('mcc')
+ mnc = p.get('mnc')
+ has_plmn = mcc is not None and mnc is not None
+ # EF.HPLMN
+ if has_plmn:
+ self.update_hplmn_act(mcc, mnc)
+
+ # EF.AD
+ if has_plmn or (p.get('opmode') is not None):
+ self.update_ad(mnc=mnc, opmode=p.get('opmode'))
+
+ def update_smsp(self, smsp):
+ data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 80))
+ return sw
+
+ def update_ki(self, ki):
+ self._scc.select_path(['3f00', '0001'])
+ self._scc.update_binary('0001', ki)
+
+ def update_opc(self, opc):
+ self._scc.select_path(['3f00', '6002'])
+ # No idea why the '01' is required
+ self._scc.update_binary('6002', '01' + opc)
+
+
+# In order for autodetection ...
+_cards_classes = [FakeMagicSim, SuperSim, MagicSim, GrcardSim,
+ SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
+ FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2,
+ SysmoISIMSJA5, GialerSim]
+
+
+def card_detect(ctype, scc):
+ # Detect type if needed
+ card = None
+ ctypes = dict([(kls.name, kls) for kls in _cards_classes])
+
+ if ctype == "auto":
+ for kls in _cards_classes:
+ card = kls.autodetect(scc)
+ if card:
+ print("Autodetected card type: %s" % card.name)
+ card.reset()
+ break
+
+ if card is None:
+ print("Autodetection failed")
+ return None
+
+ elif ctype in ctypes:
+ card = ctypes[ctype](scc)
+
+ else:
+ raise ValueError("Unknown card type: %s" % ctype)
+
+ return card
diff --git a/pySim/legacy/utils.py b/pySim/legacy/utils.py
new file mode 100644
index 0000000..45c5af9
--- /dev/null
+++ b/pySim/legacy/utils.py
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+
+""" pySim: various utilities only used by legacy tools (pySim-{prog,read})
+"""
+
+# Copyright (C) 2009-2010 Sylvain Munaut <tnt(a)246tNt.com>
+# Copyright (C) 2021 Harald Welte <laforge(a)osmocom.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from pySim.utils import Hexstr, rpad
+from pySim.utils import dec_xplmn_w_act, dec_xplmn, dec_mcc_from_plmn, dec_mnc_from_plmn
+
+def hexstr_to_Nbytearr(s, nbytes):
+ return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
+
+def format_xplmn_w_act(hexstr):
+ s = ""
+ for rec_data in hexstr_to_Nbytearr(hexstr, 5):
+ rec_info = dec_xplmn_w_act(rec_data)
+ if rec_info['mcc'] == "" and rec_info['mnc'] == "":
+ rec_str = "unused"
+ else:
+ rec_str = "MCC: %s MNC: %s AcT: %s" % (
+ rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
+ s += "\t%s # %s\n" % (rec_data, rec_str)
+ return s
+
+
+def format_xplmn(hexstr: Hexstr) -> str:
+ s = ""
+ for rec_data in hexstr_to_Nbytearr(hexstr, 3):
+ rec_info = dec_xplmn(rec_data)
+ if not rec_info['mcc'] and not rec_info['mnc']:
+ rec_str = "unused"
+ else:
+ rec_str = "MCC: %s MNC: %s" % (rec_info['mcc'], rec_info['mnc'])
+ s += "\t%s # %s\n" % (rec_data, rec_str)
+ return s
+
+
+def format_ePDGSelection(hexstr):
+ ePDGSelection_info_tag_chars = 2
+ ePDGSelection_info_tag_str = hexstr[:2]
+ s = ""
+ # Minimum length
+ len_chars = 2
+ # TODO: Need to determine length properly - definite length support only
+ # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
+ # As per spec, length is 5n, n - number of PLMNs
+ # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
+ # Totalling to 6 Bytes, maybe length should be 6n
+ len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
+
+ # Not programmed scenario
+ if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
+ len_chars = 0
+ ePDGSelection_info_tag_chars = 0
+ if len_str[0] == '8':
+ # The bits 7 to 1 denotes the number of length octets if length > 127
+ if int(len_str[1]) > 0:
+ # Update number of length octets
+ len_chars = len_chars * int(len_str[1])
+ len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
+
+ content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
+ # Right pad to prevent index out of range - multiple of 6 bytes
+ content_str = rpad(content_str, len(content_str) +
+ (12 - (len(content_str) % 12)))
+ for rec_data in hexstr_to_Nbytearr(content_str, 6):
+ rec_info = dec_ePDGSelection(rec_data)
+ if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
+ rec_str = "unused"
+ else:
+ rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
+ (rec_info['mcc'], rec_info['mnc'],
+ rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
+ s += "\t%s # %s\n" % (rec_data, rec_str)
+ return s
+
+def enc_st(st, service, state=1):
+ """
+ Encodes the EF S/U/IST/EST and returns the updated Service Table
+
+ Parameters:
+ st - Current value of SIM/USIM/ISIM Service Table
+ service - Service Number to encode as activated/de-activated
+ state - 1 mean activate, 0 means de-activate
+
+ Returns:
+ s - Modified value of SIM/USIM/ISIM Service Table
+
+ Default values:
+ - state: 1 - Sets the particular Service bit to 1
+ """
+ st_bytes = [st[i:i+2] for i in range(0, len(st), 2)]
+
+ s = ""
+ # Check whether the requested service is present in each byte
+ for i in range(0, len(st_bytes)):
+ # Byte i contains info about Services num (8i+1) to num (8i+8)
+ if service in range((8*i) + 1, (8*i) + 9):
+ byte = int(st_bytes[i], 16)
+ # Services in each byte are in order MSB to LSB
+ # MSB - Service (8i+8)
+ # LSB - Service (8i+1)
+ mod_byte = 0x00
+ # Copy bit by bit contents of byte to mod_byte with modified bit
+ # for requested service
+ for j in range(1, 9):
+ mod_byte = mod_byte >> 1
+ if service == (8*i) + j:
+ mod_byte = state == 1 and mod_byte | 0x80 or mod_byte & 0x7f
+ else:
+ mod_byte = byte & 0x01 == 0x01 and mod_byte | 0x80 or mod_byte & 0x7f
+ byte = byte >> 1
+
+ s += ('%02x' % (mod_byte))
+ else:
+ s += st_bytes[i]
+
+ return s
+
+
+def dec_st(st, table="sim") -> str:
+ """
+ Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
+ """
+
+ if table == "isim":
+ from pySim.ts_31_103 import EF_IST_map
+ lookup_map = EF_IST_map
+ elif table == "usim":
+ from pySim.ts_31_102 import EF_UST_map
+ lookup_map = EF_UST_map
+ else:
+ from pySim.ts_51_011 import EF_SST_map
+ lookup_map = EF_SST_map
+
+ st_bytes = [st[i:i+2] for i in range(0, len(st), 2)]
+
+ avail_st = ""
+ # Get each byte and check for available services
+ for i in range(0, len(st_bytes)):
+ # Byte i contains info about Services num (8i+1) to num (8i+8)
+ byte = int(st_bytes[i], 16)
+ # Services in each byte are in order MSB to LSB
+ # MSB - Service (8i+8)
+ # LSB - Service (8i+1)
+ for j in range(1, 9):
+ if byte & 0x01 == 0x01 and ((8*i) + j in lookup_map):
+ # Byte X contains info about Services num (8X-7) to num (8X)
+ # bit = 1: service available
+ # bit = 0: service not available
+ avail_st += '\tService %d - %s\n' % (
+ (8*i) + j, lookup_map[(8*i) + j])
+ byte = byte >> 1
+ return avail_st
+
+
+def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
+ """
+ Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
+ See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
+
+ Default values:
+ - epdg_priority: '0001' - 1st Priority
+ - epdg_fqdn_format: '00' - Operator Identifier FQDN
+ """
+
+ plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
+ # TODO: Handle encoding of Length field for length more than 127 Bytes
+ content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
+ content = rpad(content, len(hexstr))
+ return content
+
+
+def dec_ePDGSelection(sixhexbytes):
+ """
+ Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
+ See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
+ """
+
+ res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
+ plmn_chars = 6
+ epdg_priority_chars = 4
+ epdg_fqdn_format_chars = 2
+ # first three bytes (six ascii hex chars)
+ plmn_str = sixhexbytes[:plmn_chars]
+ # two bytes after first three bytes
+ epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars +
+ epdg_priority_chars]
+ # one byte after first five bytes
+ epdg_fqdn_format_str = sixhexbytes[plmn_chars +
+ epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
+ res['mcc'] = dec_mcc_from_plmn(plmn_str)
+ res['mnc'] = dec_mnc_from_plmn(plmn_str)
+ res['epdg_priority'] = epdg_priority_str
+ res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
+ return res
diff --git a/pySim/utils.py b/pySim/utils.py
index 9defaf3..c25560c 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -435,30 +435,6 @@
return res
-def dec_spn(ef):
- """Obsolete, kept for API compatibility"""
- from ts_51_011 import EF_SPN
- abstract_data = EF_SPN().decode_hex(ef)
- show_in_hplmn = abstract_data['show_in_hplmn']
- hide_in_oplmn = abstract_data['hide_in_oplmn']
- name = abstract_data['spn']
- return (name, show_in_hplmn, hide_in_oplmn)
-
-
-def enc_spn(name: str, show_in_hplmn=False, hide_in_oplmn=False):
- """Obsolete, kept for API compatibility"""
- from ts_51_011 import EF_SPN
- abstract_data = {
- 'hide_in_oplmn': hide_in_oplmn,
- 'show_in_hplmn': show_in_hplmn,
- 'spn': name,
- }
- return EF_SPN().encode_hex(abstract_data)
-
-
-def hexstr_to_Nbytearr(s, nbytes):
- return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2))]
-
# Accepts hex string representing three bytes
@@ -544,53 +520,6 @@
return res
-def format_xplmn_w_act(hexstr):
- s = ""
- for rec_data in hexstr_to_Nbytearr(hexstr, 5):
- rec_info = dec_xplmn_w_act(rec_data)
- if rec_info['mcc'] == "" and rec_info['mnc'] == "":
- rec_str = "unused"
- else:
- rec_str = "MCC: %s MNC: %s AcT: %s" % (
- rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
- s += "\t%s # %s\n" % (rec_data, rec_str)
- return s
-
-
-def dec_loci(hexstr):
- res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
- res['tmsi'] = hexstr[:8]
- res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
- res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
- res['lac'] = hexstr[14:18]
- res['status'] = h2i(hexstr[20:22])
- return res
-
-
-def dec_psloci(hexstr):
- res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0,
- 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
- res['p-tmsi'] = hexstr[:8]
- res['p-tmsi-sig'] = hexstr[8:14]
- res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
- res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
- res['lac'] = hexstr[20:24]
- res['rac'] = hexstr[24:26]
- res['status'] = h2i(hexstr[26:28])
- return res
-
-
-def dec_epsloci(hexstr):
- res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
- res['guti'] = hexstr[:24]
- res['tai'] = hexstr[24:34]
- res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
- res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
- res['tac'] = hexstr[30:34]
- res['status'] = h2i(hexstr[34:36])
- return res
-
-
def dec_xplmn(threehexbytes: Hexstr) -> dict:
res = {'mcc': 0, 'mnc': 0, 'act': []}
plmn_chars = 6
@@ -601,18 +530,6 @@
return res
-def format_xplmn(hexstr: Hexstr) -> str:
- s = ""
- for rec_data in hexstr_to_Nbytearr(hexstr, 3):
- rec_info = dec_xplmn(rec_data)
- if not rec_info['mcc'] and not rec_info['mnc']:
- rec_str = "unused"
- else:
- rec_str = "MCC: %s MNC: %s" % (rec_info['mcc'], rec_info['mnc'])
- s += "\t%s # %s\n" % (rec_data, rec_str)
- return s
-
-
def derive_milenage_opc(ki_hex: Hexstr, op_hex: Hexstr) -> Hexstr:
"""
Run the milenage algorithm to calculate OPC from Ki and OP
@@ -787,42 +704,6 @@
return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd + ("ff" * 2)
-def dec_st(st, table="sim") -> str:
- """
- Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
- """
-
- if table == "isim":
- from pySim.ts_31_103 import EF_IST_map
- lookup_map = EF_IST_map
- elif table == "usim":
- from pySim.ts_31_102 import EF_UST_map
- lookup_map = EF_UST_map
- else:
- from pySim.ts_51_011 import EF_SST_map
- lookup_map = EF_SST_map
-
- st_bytes = [st[i:i+2] for i in range(0, len(st), 2)]
-
- avail_st = ""
- # Get each byte and check for available services
- for i in range(0, len(st_bytes)):
- # Byte i contains info about Services num (8i+1) to num (8i+8)
- byte = int(st_bytes[i], 16)
- # Services in each byte are in order MSB to LSB
- # MSB - Service (8i+8)
- # LSB - Service (8i+1)
- for j in range(1, 9):
- if byte & 0x01 == 0x01 and ((8*i) + j in lookup_map):
- # Byte X contains info about Services num (8X-7) to num (8X)
- # bit = 1: service available
- # bit = 0: service not available
- avail_st += '\tService %d - %s\n' % (
- (8*i) + j, lookup_map[(8*i) + j])
- byte = byte >> 1
- return avail_st
-
-
def first_TLV_parser(bytelist):
'''
first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
@@ -864,50 +745,6 @@
return ret
-def enc_st(st, service, state=1):
- """
- Encodes the EF S/U/IST/EST and returns the updated Service Table
-
- Parameters:
- st - Current value of SIM/USIM/ISIM Service Table
- service - Service Number to encode as activated/de-activated
- state - 1 mean activate, 0 means de-activate
-
- Returns:
- s - Modified value of SIM/USIM/ISIM Service Table
-
- Default values:
- - state: 1 - Sets the particular Service bit to 1
- """
- st_bytes = [st[i:i+2] for i in range(0, len(st), 2)]
-
- s = ""
- # Check whether the requested service is present in each byte
- for i in range(0, len(st_bytes)):
- # Byte i contains info about Services num (8i+1) to num (8i+8)
- if service in range((8*i) + 1, (8*i) + 9):
- byte = int(st_bytes[i], 16)
- # Services in each byte are in order MSB to LSB
- # MSB - Service (8i+8)
- # LSB - Service (8i+1)
- mod_byte = 0x00
- # Copy bit by bit contents of byte to mod_byte with modified bit
- # for requested service
- for j in range(1, 9):
- mod_byte = mod_byte >> 1
- if service == (8*i) + j:
- mod_byte = state == 1 and mod_byte | 0x80 or mod_byte & 0x7f
- else:
- mod_byte = byte & 0x01 == 0x01 and mod_byte | 0x80 or mod_byte & 0x7f
- byte = byte >> 1
-
- s += ('%02x' % (mod_byte))
- else:
- s += st_bytes[i]
-
- return s
-
-
def dec_addr_tlv(hexstr):
"""
Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
@@ -1038,88 +875,6 @@
return pin_adm
-def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
- """
- Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
- See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
-
- Default values:
- - epdg_priority: '0001' - 1st Priority
- - epdg_fqdn_format: '00' - Operator Identifier FQDN
- """
-
- plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
- # TODO: Handle encoding of Length field for length more than 127 Bytes
- content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
- content = rpad(content, len(hexstr))
- return content
-
-
-def dec_ePDGSelection(sixhexbytes):
- """
- Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
- See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
- """
-
- res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
- plmn_chars = 6
- epdg_priority_chars = 4
- epdg_fqdn_format_chars = 2
- # first three bytes (six ascii hex chars)
- plmn_str = sixhexbytes[:plmn_chars]
- # two bytes after first three bytes
- epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars +
- epdg_priority_chars]
- # one byte after first five bytes
- epdg_fqdn_format_str = sixhexbytes[plmn_chars +
- epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
- res['mcc'] = dec_mcc_from_plmn(plmn_str)
- res['mnc'] = dec_mnc_from_plmn(plmn_str)
- res['epdg_priority'] = epdg_priority_str
- res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
- return res
-
-
-def format_ePDGSelection(hexstr):
- ePDGSelection_info_tag_chars = 2
- ePDGSelection_info_tag_str = hexstr[:2]
- s = ""
- # Minimum length
- len_chars = 2
- # TODO: Need to determine length properly - definite length support only
- # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
- # As per spec, length is 5n, n - number of PLMNs
- # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
- # Totalling to 6 Bytes, maybe length should be 6n
- len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
-
- # Not programmed scenario
- if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
- len_chars = 0
- ePDGSelection_info_tag_chars = 0
- if len_str[0] == '8':
- # The bits 7 to 1 denotes the number of length octets if length > 127
- if int(len_str[1]) > 0:
- # Update number of length octets
- len_chars = len_chars * int(len_str[1])
- len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
-
- content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
- # Right pad to prevent index out of range - multiple of 6 bytes
- content_str = rpad(content_str, len(content_str) +
- (12 - (len(content_str) % 12)))
- for rec_data in hexstr_to_Nbytearr(content_str, 6):
- rec_info = dec_ePDGSelection(rec_data)
- if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
- rec_str = "unused"
- else:
- rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
- (rec_info['mcc'], rec_info['mnc'],
- rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
- s += "\t%s # %s\n" % (rec_data, rec_str)
- return s
-
-
def get_addr_type(addr):
"""
Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index b7f790d..764cf71 100755
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,6 +2,7 @@
import unittest
from pySim import utils
+from pySim.legacy import utils as legacy_utils
from pySim.ts_31_102 import EF_SUCI_Calc_Info
# we don't really want to thest TS 102 221, but the underlying DataObject codebase
@@ -45,7 +46,7 @@
"ffffff0002",
"ffffff0001",
]
- self.assertEqual(utils.hexstr_to_Nbytearr(input_str, 5), expected)
+ self.assertEqual(legacy_utils.hexstr_to_Nbytearr(input_str, 5), expected)
def testDecMCCfromPLMN(self):
self.assertEqual(utils.dec_mcc_from_plmn("92f501"), 295)
@@ -130,7 +131,7 @@
expected += "\tffffff0000 # unused\n"
expected += "\tffffff0000 # unused\n"
expected += "\tffffff0000 # unused\n"
- self.assertEqual(utils.format_xplmn_w_act(input_str), expected)
+ self.assertEqual(legacy_utils.format_xplmn_w_act(input_str), expected)
def testDecodeSuciCalcInfo(self):
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/33691
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ia8cf831929730c48f90679a83d69049475cc5077
Gerrit-Change-Number: 33691
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
1
0
[S] Change in pysim[master]: cards: all UICC should use sel_ctrl="0400" and SIM "0000"
by laforge 11 Jul '23
by laforge 11 Jul '23
11 Jul '23
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/33697 )
Change subject: cards: all UICC should use sel_ctrl="0400" and SIM "0000"
......................................................................
cards: all UICC should use sel_ctrl="0400" and SIM "0000"
Hence move this from the derived classes into the respective base
classes SimCardBase and UiccCardBase
Change-Id: Iad197c2b560c5ea05c54a122144361de5742aafd
---
M pySim/cards.py
M pySim/legacy/cards.py
2 files changed, 14 insertions(+), 6 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/97/33697/1
diff --git a/pySim/cards.py b/pySim/cards.py
index 5ee8b4a..4598217 100644
--- a/pySim/cards.py
+++ b/pySim/cards.py
@@ -79,6 +79,7 @@
def __init__(self, scc: LinkBase):
super(SimCardBase, self).__init__(scc)
self._scc.cla_byte = "A0"
+ self._scc.sel_ctrl = "0000"
name = 'SIM'
@@ -89,6 +90,7 @@
def __init__(self, scc: LinkBase):
super(UiccCardBase, self).__init__(scc)
self._scc.cla_byte = "00"
+ self._scc.sel_ctrl = "0004" # request an FCP
# See also: ETSI TS 102 221, Table 9.3
self._adm_chv_num = 0xA0
diff --git a/pySim/legacy/cards.py b/pySim/legacy/cards.py
index b865d4b..6a63182 100644
--- a/pySim/legacy/cards.py
+++ b/pySim/legacy/cards.py
@@ -892,7 +892,6 @@
def __init__(self, ssc):
super(SysmoUSIMSJS1, self).__init__(ssc)
- self._scc.sel_ctrl = "0004" # request an FCP
@classmethod
def autodetect(kls, scc):
@@ -1204,7 +1203,6 @@
def __init__(self, ssc):
super(WavemobileSim, self).__init__(ssc)
self._adm_chv_num = 0x0A
- self._scc.sel_ctrl = "0004" # request an FCP
@classmethod
def autodetect(kls, scc):
@@ -1296,10 +1294,6 @@
name = 'sysmoISIM-SJA2'
- def __init__(self, ssc):
- super(SysmoISIMSJA2, self).__init__(ssc)
- self._scc.sel_ctrl = "0004" # request an FCP
-
@classmethod
def autodetect(kls, scc):
try:
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/33697
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Iad197c2b560c5ea05c54a122144361de5742aafd
Gerrit-Change-Number: 33697
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
1
0
[S] Change in pysim[master]: cards: card_detect for pure UICC/SIM differentiation
by laforge 11 Jul '23
by laforge 11 Jul '23
11 Jul '23
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/33698 )
Change subject: cards: card_detect for pure UICC/SIM differentiation
......................................................................
cards: card_detect for pure UICC/SIM differentiation
Change-Id: I01009955fe1bef811a30d21ef347fb1bbccc7619
---
M pySim-shell.py
M pySim/cards.py
2 files changed, 34 insertions(+), 2 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/98/33698/1
diff --git a/pySim-shell.py b/pySim-shell.py
index 061ac36..4d4f2e2 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -95,7 +95,7 @@
return None, None
generic_card = False
- card = card_detect("auto", scc)
+ card = card_detect(scc)
if card is None:
print("Warning: Could not detect card type - assuming a generic card type...")
card = SimCardBase(scc)
diff --git a/pySim/cards.py b/pySim/cards.py
index 4598217..c6aee69 100644
--- a/pySim/cards.py
+++ b/pySim/cards.py
@@ -24,6 +24,7 @@
from typing import Optional, Dict, Tuple
from pySim.ts_102_221 import EF_DIR, EF_ICCID
+from pySim.ts_51_011 import DF_GSM
from pySim.transport import LinkBase
import abc
@@ -75,13 +76,17 @@
class SimCardBase(CardBase):
"""Here we only add methods for commands specified in TS 51.011, without
any higher-layer processing."""
+ name = 'SIM'
def __init__(self, scc: LinkBase):
super(SimCardBase, self).__init__(scc)
self._scc.cla_byte = "A0"
self._scc.sel_ctrl = "0000"
- name = 'SIM'
+ def probe(self):
+ # EF.DIR is a mandatory EF on all ICCIDs; however it *may* also exist on a TS 51.011 SIM
+ df_gsm = DF_GSM()
+ return self.file_exists(df_gsm.fid)
class UiccCardBase(SimCardBase):
@@ -94,6 +99,11 @@
# See also: ETSI TS 102 221, Table 9.3
self._adm_chv_num = 0xA0
+ def probe(self):
+ # EF.DIR is a mandatory EF on all ICCIDs; however it *may* also exist on a TS 51.011 SIM
+ ef_dir = EF_DIR()
+ return self.file_exists(ef_dir.fid)
+
def read_aids(self) -> List[Hexstr]:
"""Fetch all the AIDs present on UICC"""
self._aids = []
@@ -155,3 +165,16 @@
# If we cannot get the full AID, try with short AID
return self._scc.select_adf(aid)
return (None, None)
+
+def card_detect(scc: LinkBase) -> Optional[CardBase]:
+ # UICC always has higher preference, as a UICC might also contain a SIM application
+ uicc = UiccCardBase(scc)
+ if uicc.probe():
+ return uicc
+
+ # this is for detecting a real, classic TS 11.11 SIM card without any UICC support
+ sim = SimCardBase(scc)
+ if sim.probe():
+ return sim
+
+ return None
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/33698
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I01009955fe1bef811a30d21ef347fb1bbccc7619
Gerrit-Change-Number: 33698
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
1
0
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/33699 )
Change subject: Introduce concept of CardProfileAddon
......................................................................
Introduce concept of CardProfileAddon
We have a strict "one CardProfile per card" rule. For a modern UICC
without legacy SIM support, that works great, as all applications
have AID and ADF and can hence be enumerated/detected that way.
However, in reality there are mostly UICC that have legacy SIM, GSM-R
or even CDMA support, all of which are not proper UICC applications
for historical reasons.
So instead of having hard-coded hacks in various places, let's introduce
the new concept of a CardProfileAddon. Every profile can have any
number of those. When building up the RuntimeState, we iterate over the
CardProfile addons, and probe which of those are actually on the card.
For those discovered, we add their files to the filesystem hierarchy.
Change-Id: I5866590b6d48f85eb889c9b1b8ab27936d2378b9
---
M pySim-shell.py
M pySim/cdma_ruim.py
M pySim/filesystem.py
M pySim/gsm_r.py
M pySim/profile.py
M pySim/ts_102_221.py
M pySim/ts_51_011.py
7 files changed, 123 insertions(+), 12 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/99/33699/1
diff --git a/pySim-shell.py b/pySim-shell.py
index 4d4f2e2..b48abc7 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -137,11 +137,6 @@
# Create runtime state with card profile
rs = RuntimeState(card, profile)
- # FIXME: This is an GSM-R related file, it needs to be added throughout,
- # the profile. At the moment we add it for all cards, this won't hurt,
- # but regular SIM and UICC will not have it and fail to select it.
- rs.mf.add_file(DF_EIRENE())
-
CardModel.apply_matching_models(scc, rs)
# inform the transport that we can do context-specific SW interpretation
diff --git a/pySim/cdma_ruim.py b/pySim/cdma_ruim.py
index 3fab558..62dbb22 100644
--- a/pySim/cdma_ruim.py
+++ b/pySim/cdma_ruim.py
@@ -22,7 +22,7 @@
from pySim.utils import *
from pySim.filesystem import *
from pySim.profile import match_ruim
-from pySim.profile import CardProfile
+from pySim.profile import CardProfile, CardProfileAddon
from pySim.ts_51_011 import CardProfileSIM
from pySim.ts_51_011 import DF_TELECOM, DF_GSM
from pySim.ts_51_011 import EF_ServiceTable
@@ -191,3 +191,14 @@
@staticmethod
def match_with_card(scc: SimCardCommands) -> bool:
return match_ruim(scc)
+
+class AddonRUIM(CardProfileAddon):
+ """An Addon that can be found on either classic GSM SIM or on UICC to support GSM-R."""
+ def __init__(self):
+ files = [
+ DF_CDMA()
+ ]
+ super().__init__('RUIM', desc='CDMA RUIM', files_in_mf=files)
+
+ def probe(self, card: 'CardBase') -> bool:
+ return card.file_exists(self.files_in_mf[0].fid)
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index 22ff60d..cb3b403 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -1307,6 +1307,16 @@
self.card.set_apdu_parameter(
cla=self.profile.cla, sel_ctrl=self.profile.sel_ctrl)
+ for addon_cls in self.profile.addons:
+ addon = addon_cls()
+ if addon.probe(self.card):
+ print("Detected %s Add-on \"%s\"" % (self.profile, addon))
+ for f in addon.files_in_mf:
+ self.mf.add_file(f)
+
+ # go back to MF before the next steps (addon probing might have changed DF)
+ self.card._scc.select_file('3F00')
+
# add application ADFs + MF-files from profile
apps = self._match_applications()
for a in apps:
diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py
index 389a8cb..cd111d6 100644
--- a/pySim/gsm_r.py
+++ b/pySim/gsm_r.py
@@ -35,8 +35,8 @@
from pySim.construct import *
import enum
+from pySim.profile import CardProfileAddon
from pySim.filesystem import *
-import pySim.ts_102_221
import pySim.ts_51_011
######################################################################
@@ -362,3 +362,15 @@
desc='Free Number Call Type 0 and 8'),
]
self.add_files(files)
+
+
+class AddonGSMR(CardProfileAddon):
+ """An Addon that can be found on either classic GSM SIM or on UICC to support GSM-R."""
+ def __init__(self):
+ files = [
+ DF_EIRENE()
+ ]
+ super().__init__('GSM-R', desc='Railway GSM', files_in_mf=files)
+
+ def probe(self, card: 'CardBase') -> bool:
+ return card.file_exists(self.files_in_mf[0].fid)
diff --git a/pySim/profile.py b/pySim/profile.py
index e464e1f..e89eeb3 100644
--- a/pySim/profile.py
+++ b/pySim/profile.py
@@ -88,6 +88,7 @@
shell_cmdsets : List of cmd2 shell command sets of profile-specific commands
cla : class byte that should be used with cards of this profile
sel_ctrl : selection control bytes class byte that should be used with cards of this profile
+ addons: List of optional CardAddons that a card of this profile might have
"""
self.name = name
self.desc = kw.get("desc", None)
@@ -97,6 +98,8 @@
self.shell_cmdsets = kw.get("shell_cmdsets", [])
self.cla = kw.get("cla", "00")
self.sel_ctrl = kw.get("sel_ctrl", "0004")
+ # list of optional addons that a card of this profile might have
+ self.addons = kw.get("addons", [])
def __str__(self):
return self.name
@@ -161,3 +164,33 @@
return p()
return None
+
+ def add_addon(self, addon: 'CardProfileAddon'):
+ assert(addon not in self.addons)
+ # we don't install any additional files, as that is happening in the RuntimeState.
+ self.addons.append(addon)
+
+class CardProfileAddon:
+ """A Card Profile Add-on is something that is not a card application or a full stand-alone
+ card profile, but an add-on to an existing profile. Think of GSM-R specific files existing
+ on what is otherwise a SIM or USIM+SIM card."""
+
+ def __init__(self, name: str, **kw):
+ """
+ Args:
+ desc (str) : Description
+ files_in_mf : List of CardEF instances present in MF
+ shell_cmdsets : List of cmd2 shell command sets of profile-specific commands
+ """
+ self.name = name
+ self.desc = kw.get("desc", None)
+ self.files_in_mf = kw.get("files_in_mf", [])
+ self.shell_cmdsets = kw.get("shell_cmdsets", [])
+ pass
+
+ def __str__(self):
+ return self.name
+
+ def probe(self, card: 'CardBase') -> bool:
+ """Probe a given card to determine whether or not this add-on is present/supported."""
+ return False
diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py
index b6c003b..df8b842 100644
--- a/pySim/ts_102_221.py
+++ b/pySim/ts_102_221.py
@@ -31,7 +31,9 @@
# A UICC will usually also support 2G functionality. If this is the case, we
# need to add DF_GSM and DF_TELECOM along with the UICC related files
-from pySim.ts_51_011 import DF_GSM, DF_TELECOM
+from pySim.ts_51_011 import DF_GSM, DF_TELECOM, AddonSIM
+from pySim.gsm_r import AddonGSMR
+from pySim.cdma_ruim import AddonRUIM
ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [
# TS 102 221 Section 10.1.2 Table 10.5 "Coding of Instruction Byte"
@@ -768,6 +770,11 @@
# FIXME: DF.CD
EF_UMPC(),
]
+ addons = [
+ AddonSIM,
+ AddonGSMR,
+ AddonRUIM,
+ ]
sw = {
'Normal': {
'9000': 'Normal ending of the command',
@@ -839,7 +846,7 @@
super().__init__(name, desc='ETSI TS 102 221', cla="00",
sel_ctrl="0004", files_in_mf=files, sw=sw,
- shell_cmdsets = [self.AddlShellCommands()])
+ shell_cmdsets = [self.AddlShellCommands()], addons = addons)
@staticmethod
def decode_select_response(resp_hex: str) -> object:
@@ -895,4 +902,5 @@
@staticmethod
def match_with_card(scc: SimCardCommands) -> bool:
- return match_uicc(scc) and match_sim(scc)
+ # don't ever select this profile, we only use this from pySim-trace
+ return False
diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py
index 1f98e72..c81bfdf 100644
--- a/pySim/ts_51_011.py
+++ b/pySim/ts_51_011.py
@@ -30,9 +30,10 @@
#
from pySim.profile import match_sim
-from pySim.profile import CardProfile
+from pySim.profile import CardProfile, CardProfileAddon
from pySim.filesystem import *
from pySim.ts_31_102_telecom import DF_PHONEBOOK, DF_MULTIMEDIA, DF_MCS, DF_V2X
+from pySim.gsm_r import AddonGSMR
import enum
from pySim.construct import *
from construct import Optional as COptional
@@ -1047,8 +1048,12 @@
},
}
+ addons = [
+ AddonGSMR,
+ ]
+
super().__init__('SIM', desc='GSM SIM Card', cla="a0",
- sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw)
+ sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw, addons = addons)
@staticmethod
def decode_select_response(resp_hex: str) -> object:
@@ -1104,3 +1109,17 @@
@staticmethod
def match_with_card(scc: SimCardCommands) -> bool:
return match_sim(scc)
+
+
+class AddonSIM(CardProfileAddon):
+ """An add-on that can be found on a UICC in order to support classic GSM SIM."""
+ def __init__(self):
+ files = [
+ DF_GSM(),
+ DF_TELECOM(),
+ ]
+ super().__init__('SIM', desc='GSM SIM', files_in_mf=files)
+
+ def probe(self, card:'CardBase') -> bool:
+ # we assume the add-on to be present in case DF.GSM is found on the card
+ return card.file_exists(self.files_in_mf[0].fid)
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/33699
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I5866590b6d48f85eb889c9b1b8ab27936d2378b9
Gerrit-Change-Number: 33699
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
1
0
11 Jul '23
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/33689 )
Change subject: filesystem: Support selecting MF from MF
......................................................................
filesystem: Support selecting MF from MF
This was currently not handled in build_select_path_to(), resulting in
weird exceptions like 'Cannot determine path from MF(3f00) to MF(3f00)'
Change-Id: I41b9f047ee5dc6b91b487f370f011af994aaca04
---
M pySim/filesystem.py
1 file changed, 15 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/89/33689/1
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index 04e849b..22ff60d 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -149,6 +149,9 @@
def build_select_path_to(self, target: 'CardFile') -> Optional[List['CardFile']]:
"""Build the relative sequence of files we need to traverse to get from us to 'target'."""
+ # special-case handling for selecting MF while we MF is selected
+ if target == target.get_mf():
+ return [target]
cur_fqpath = self.fully_qualified_path_fobj()
target_fqpath = target.fully_qualified_path_fobj()
inter_path = []
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/33689
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I41b9f047ee5dc6b91b487f370f011af994aaca04
Gerrit-Change-Number: 33689
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
1
0
[S] Change in libosmocore[master]: osmo_io: Add function to change the maximum length of the tx_queue
by pespin 11 Jul '23
by pespin 11 Jul '23
11 Jul '23
Attention is currently required from: daniel.
pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmocore/+/33686 )
Change subject: osmo_io: Add function to change the maximum length of the tx_queue
......................................................................
Patch Set 2: Code-Review+1
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/33686
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: If3d1de8bffe1123002515878655ea9e02b482888
Gerrit-Change-Number: 33686
Gerrit-PatchSet: 2
Gerrit-Owner: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-Attention: daniel <dwillmann(a)sysmocom.de>
Gerrit-Comment-Date: Tue, 11 Jul 2023 15:39:22 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment
1
0
[S] Change in libosmocore[master]: osmo_io: Document expectation that segmentation_cb() can modify msgb
by pespin 11 Jul '23
by pespin 11 Jul '23
11 Jul '23
Attention is currently required from: daniel.
pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmocore/+/33685 )
Change subject: osmo_io: Document expectation that segmentation_cb() can modify msgb
......................................................................
Patch Set 1: Code-Review+1
(1 comment)
File src/core/osmo_io.c:
https://gerrit.osmocom.org/c/libosmocore/+/33685/comment/e410e6d3_e3654b59
PS1, Line 264: memcpy(msgb_data(msg_pending), data + expected_len, extra_len);
> No - msg_pending is a new msg allocation, data is from msg->data
Ack
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/33685
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Idd2115baae98a7818aabb26232d4423d2d48fb5c
Gerrit-Change-Number: 33685
Gerrit-PatchSet: 1
Gerrit-Owner: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-Attention: daniel <dwillmann(a)sysmocom.de>
Gerrit-Comment-Date: Tue, 11 Jul 2023 15:38:47 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Comment-In-Reply-To: pespin <pespin(a)sysmocom.de>
Comment-In-Reply-To: daniel <dwillmann(a)sysmocom.de>
Gerrit-MessageType: comment
1
0
[S] Change in libosmocore[master]: osmo_io: Add function to change the maximum length of the tx_queue
by daniel 11 Jul '23
by daniel 11 Jul '23
11 Jul '23
Attention is currently required from: pespin.
daniel has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmocore/+/33686 )
Change subject: osmo_io: Add function to change the maximum length of the tx_queue
......................................................................
Patch Set 2:
(1 comment)
File src/core/osmo_io.c:
https://gerrit.osmocom.org/c/libosmocore/+/33686/comment/10f9a401_6f364f85
PS1, Line 549: void osmo_iofd_set_txqueue_size(struct osmo_io_fd *iofd, unsigned int size)
> osmo_iofd_set_txqueue_max_length()?
Ack
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/33686
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: If3d1de8bffe1123002515878655ea9e02b482888
Gerrit-Change-Number: 33686
Gerrit-PatchSet: 2
Gerrit-Owner: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-Attention: pespin <pespin(a)sysmocom.de>
Gerrit-Comment-Date: Tue, 11 Jul 2023 15:37:29 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: comment
1
0
[S] Change in libosmocore[master]: osmo_io: Add function to change the maximum length of the tx_queue
by daniel 11 Jul '23
by daniel 11 Jul '23
11 Jul '23
Attention is currently required from: pespin.
Hello Jenkins Builder, pespin,
I'd like you to reexamine a change. Please visit
https://gerrit.osmocom.org/c/libosmocore/+/33686
to look at the new patch set (#2).
Change subject: osmo_io: Add function to change the maximum length of the tx_queue
......................................................................
osmo_io: Add function to change the maximum length of the tx_queue
Change-Id: If3d1de8bffe1123002515878655ea9e02b482888
---
M include/osmocom/core/osmo_io.h
M src/core/libosmocore.map
M src/core/osmo_io.c
3 files changed, 20 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/86/33686/2
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/33686
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: If3d1de8bffe1123002515878655ea9e02b482888
Gerrit-Change-Number: 33686
Gerrit-PatchSet: 2
Gerrit-Owner: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-Attention: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: newpatchset
1
0
Attention is currently required from: pespin.
daniel has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmocore/+/32536 )
Change subject: osmo_io: Add io_uring backend
......................................................................
Set Ready For Review
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/32536
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I5152129eb84b31ccc9e02bc2a5c5bdb046d331bc
Gerrit-Change-Number: 32536
Gerrit-PatchSet: 6
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-CC: daniel <dwillmann(a)sysmocom.de>
Gerrit-CC: laforge <laforge(a)osmocom.org>
Gerrit-Attention: pespin <pespin(a)sysmocom.de>
Gerrit-Comment-Date: Tue, 11 Jul 2023 15:37:08 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: No
Gerrit-MessageType: comment
1
0