5 Commits

Author SHA1 Message Date
Holger Hans Peter Freyther
d56eb30eea WIP.. EF.auth changes 2015-11-05 09:35:10 +01:00
Jan Balke
533a0dce3a Add provision support for sysmoUSIM-SJS1 cards
The PIN-ADM has to given on the command line as it is provisioned
different for each card.
Currently only Ki, Op and IMSI are provisioned.
2015-02-23 08:10:30 +01:00
Jan Balke
29474b0d5b Add PIN-ADM argument to the command line
Allow overwriting the default PIN-ADM set in the card implementation.
2015-02-23 08:10:18 +01:00
Jan Balke
800c9eb097 Do not return the FCI information while selecting a file
The sysmoUSIM-SJS1 card does not support returning the FCI
information.
Plus, the FCI information are not used anyway.
2015-02-23 08:10:13 +01:00
Jan Balke
73b686f7ee Allow changing the class byte for pdu messages 2015-02-23 08:09:53 +01:00
44 changed files with 313 additions and 4540 deletions

View File

@@ -1,3 +0,0 @@
[gerrit]
host=gerrit.osmocom.org
project=pysim

38
README Normal file
View File

@@ -0,0 +1,38 @@
This utility allows to :
* Program customizable SIMs. Two modes are possible:
- one where you specify every parameter manually :
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -i <IMSI> -s <ICCID>
- one where they are generated from some minimal set :
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -z <random_string_of_choice> -j <card_num>
With <random_string_of_choice> and <card_num>, the soft will generate
'predictable' IMSI and ICCID, so make sure you choose them so as not to
conflict with anyone. (for eg. your name as <random_string_of_choice> and
0 1 2 ... for <card num>).
You also need to enter some parameters to select the device :
-t TYPE : type of card (supersim, magicsim, fakemagicsim or try 'auto')
-d DEV : Serial port device (default /dev/ttyUSB0)
-b BAUD : Baudrate (default 9600)
* Interact with SIMs from a python interactive shell (ipython for eg :)
from pySim.transport.serial import SerialSimLink
from pySim.commands import SimCardCommands
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
sc = SimCardCommands(sl)
sl.wait_for_card()
# Print IMSI
print sc.read_binary(['3f00', '7f20', '6f07'])
# Run A3/A8
print sc.run_gsm('00112233445566778899aabbccddeeff')

107
README.md
View File

@@ -1,107 +0,0 @@
pySim-prog - Utility for programmable SIM/USIM-Cards
====================================================
This repository contains a Python-language program that can be used
to program (write) certain fields/parameters on so-called programmable
SIM/USIM cards.
Such SIM/USIM cards are special cards, which - unlike those issued by
regular commercial operators - come with the kind of keys that allow you
to write the files/fields that normally only an operator can program.
This is useful particularly if you are running your own cellular
network, and want to issue your own SIM/USIM cards for that network.
Homepage
--------
The official homepage of the project is
<http://osmocom.org/projects/pysim/wiki>
GIT Repository
--------------
You can clone from the official libosmocore.git repository using
git clone git://git.osmocom.org/pysim.git
There is a cgit interface at <http://git.osmocom.org/pysim/>
Dependencies
------------
pysim requires:
- pyscard
- serial
- pytlv (for specific card types)
Example for Debian:
apt-get install python-pyscard python-serial python-pip
pip install pytlv
Mailing List
------------
There is no separate mailing list for this project. However,
discussions related to pysim-prog are happening on the
openbsc@lists.osmocom.org mailing list, please see
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We are currently accepting patches by e-mail to the above-mentioned
mailing list.
Usage
-----
* Program customizable SIMs. Two modes are possible:
- one where you specify every parameter manually :
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -i <IMSI> -s <ICCID>
- one where they are generated from some minimal set :
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -z <random_string_of_choice> -j <card_num>
With <random_string_of_choice> and <card_num>, the soft will generate
'predictable' IMSI and ICCID, so make sure you choose them so as not to
conflict with anyone. (for eg. your name as <random_string_of_choice> and
0 1 2 ... for <card num>).
You also need to enter some parameters to select the device :
-t TYPE : type of card (supersim, magicsim, fakemagicsim or try 'auto')
-d DEV : Serial port device (default /dev/ttyUSB0)
-b BAUD : Baudrate (default 9600)
* Interact with SIMs from a python interactive shell (ipython for eg :)
from pySim.transport.serial import SerialSimLink
from pySim.commands import SimCardCommands
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
sc = SimCardCommands(sl)
sl.wait_for_card()
# Print IMSI
print(sc.read_binary(['3f00', '7f20', '6f07']))
# Run A3/A8
print(sc.run_gsm('00112233445566778899aabbccddeeff'))

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -e
if [ ! -d "./pysim-testdata/" ] ; then
echo "###############################################"
echo "Please call from pySim-prog top directory"
echo "###############################################"
exit 1
fi
virtualenv -p python2 venv --system-site-packages
. venv/bin/activate
pip install pytlv
pip install pyyaml
cd pysim-testdata
../tests/pysim-test.sh

View File

@@ -1,16 +0,0 @@
This file aims to describe the format of the CSV file pySim uses.
The first line contains the fieldnames which will be used by pySim. This
avoids having a specific order.
The field names are the following:
iccid: ICCID of the card. Used to identify the cards (with --read-iccid)
imsi: IMSI of the card
mcc: Mobile Country Code (optional)
mnc: Mobile Network Code (optional)
smsp: MSISDN of the SMSC (optional)
ki: Ki
opc: OPc
acc: Access class of the SIM (optional)
pin_adm: Admin PIN of the SIM. Needed to reprogram various files

View File

@@ -1,141 +0,0 @@
#!/usr/bin/python3
from pySim.transport.pcsc import PcscSimLink
from pySim.utils import enc_iccid, enc_imsi
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
from smartcard.System import readers
from lark import Lark, Transformer, Token, Tree
import sys
from format_ipr import ScriptFormatIPR
class DataTransform():
''' Transform raw/logical input data into the format use on the SIM card,
like encoding the PIN from '1234' -> 3132334 or IMSI encoding'''
def transform(self, inp):
outp = {}
for k in inp.keys():
f = getattr(self, 'xfrm_'+k, None)
if f != None:
outp[k] = f(inp[k])
else:
outp[k] = inp[k]
return outp
def xfrm_PIN(self, pin):
ret = ''
for c in str(pin):
ret += '3%c' % c
return ret
def xfrm_PIN1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PIN2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PUK1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_PUK2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_ADM1(self, pin):
return self.xfrm_PIN(pin)
def xfrm_ADM2(self, pin):
return self.xfrm_PIN(pin)
def xfrm_IMSI(self, imsi):
return enc_imsi(imsi)
def xfrm_ICCID(self, iccid):
# TODO: calculate luhn check digit
return enc_iccid(iccid)
def expand_cmd_template(cmd, templates):
''' Take a single command, supstituting all [] template keys with data from 'template' '''
ret = ""
for e in cmd:
if e[0] == 'hexstr':
ret += e[1]
if e[0] == 'key':
ret += templates[e[1]]
return ret
def match_sw(actual_sw, sw_match):
''' Check if actual_sw matches any of the templates given in sw_match'''
def match_sw_single(actual_sw, match):
match = match.lower()
if 'x' in match:
FIXME
else:
if actual_sw.lower() == match:
return True
return False
if sw_match == []:
return True
for m in sw_match:
if match_sw_single(actual_sw, m):
return True
return False
def execute_ipr_raw(s, sl, dynamic_data_raw = {}):
""" translate a single LDR statement to IPR format. """
if s == None:
None
elif s == 'reset':
print("RESET")
sl.reset_card()
elif s[0] == 'rem':
print("REM %s" % (s[1]))
elif s[0] == 'cmd':
d = s[1]
req = expand_cmd_template(d['req'], dynamic_data_raw)
rsp = d['rsp']
print("\tREQ: %s, EXP: %s" % (req, rsp))
(data, sw) = sl.send_apdu_raw(req)
if not match_sw(sw, rsp):
raise ValueError("SW %s doesn't match expected %s" % (sw, rsp))
print("\tRSP: %s\n" % (sw))
def execute_ipr(s, sl, dynamic_data = {}):
""" translate a single LDR statement to IPR format; optionally substitute dynamic_data. """
xf = DataTransform()
return execute_ipr_raw(s, sl, xf.transform(dynamic_data))
'''Dictionaries like this must be generated for each card to be programmed'''
demo_dict = {
'PIN1': '1234',
'PIN2': '1234',
'PUK1': '12345678',
'PUK2': '12345678',
'ADM1': '11111111',
'KIC1': '100102030405060708090a0b0c0d0e0f',
'KID1': '101102030405060708090a0b0c0d0e0f',
'KIK1': '102102030405060708090a0b0c0d0e0f',
'KIC2': '200102030405060708090a0b0c0d0e0f',
'KID2': '201102030405060708090a0b0c0d0e0f',
'KIK2': '202102030405060708090a0b0c0d0e0f',
'KIC3': '300102030405060708090a0b0c0d0e0f',
'KID3': '301102030405060708090a0b0c0d0e0f',
'KIK3': '302102030405060708090a0b0c0d0e0f',
'ICCID': '012345678901234567',
'IMSI': '001010123456789',
'ACC': '0200',
'KI': '000102030405060708090a0b0c0d0e0f',
'OPC': '101112131415161718191a1b1c1d1e1f',
'VERIFY_ICCID': '0001020304050608090a0b0c0d0e0f',
}
sl = PcscSimLink(0)
infile_name = sys.argv[1]
fmt = ScriptFormatIPR()
fmt.parse_process_file(infile_name, execute_ipr, {'sl':sl, 'dynamic_data':demo_dict})

View File

@@ -1,55 +0,0 @@
from lark import Lark, Transformer, Token, Tree
from script_format import ScriptFormat
from format_ldr import LdrXfrm
class IprXfrm(LdrXfrm):
""" transform the parse tree into a more easily consumable form """
def key(self, items):
return ('key', ''.join(list(items)))
def req(self, items):
return items[:-1]
def rsp(self, items):
return items[:-1]
#def NEWLINE(self, items):
#return None
class ScriptFormatIPR(ScriptFormat):
# parser for the IPR file format as used by the SIM card factory
ipr_parser = Lark(r"""
script: statement*
?statement: cmd | rst | rem | NEWLINE
NONL: /[^\n]/+
rem: "//" NONL? NEWLINE
ALNUM: DIGIT | LETTER | "_"
key: "[" ALNUM+ "]"
cmd: req rsp
req: "I:" [hexstr|key]+ NEWLINE
hexstr: HEX_ITEM+
HEX_ITEM: HEXDIGIT ~ 2
rsp: "O:" swpattern? NEWLINE
swpattern: HEX_OR_X ~ 4
HEX_OR_X: HEXDIGIT | "X" | "x"
rst: "RESET" NEWLINE
%import common.ESCAPED_STRING -> STRING
%import common.WS_INLINE
%import common.HEXDIGIT
%import common.DIGIT
%import common.LETTER
%import common.NEWLINE
%ignore WS_INLINE
""", start='script', parser='lalr')#, lexer='standard')
def parse_xform(self, text):
tree = self.ipr_parser.parse(text)
#print(tree.pretty())
p = IprXfrm().transform(tree)
return p

View File

@@ -1,74 +0,0 @@
from lark import Lark, Transformer, Token, Tree
from script_format import ScriptFormat
class LdrXfrm(Transformer):
""" transform the parse tree into a more easily consumable form """
def rst(self, items):
return ('reset')
def NONL(self, items):
return ''.join([i for i in items.value])
def rem(self, items):
return ('rem', items[0])
def swmatch(self, items):
return ('swmatch', items)
def cmd(self, items):
return ('cmd', {'req': items[0], 'rsp': items[1]})
def todigit(self, item):
""" convert from Token/Tree to raw hex-digit """
if isinstance(item, Token):
return item.value
elif isinstance(item, Tree):
return item.data
def hex_item(self, items):
""" return one byte as two-digit HEX string """
return "%s%s" % (items[0].value, items[1].value)
def hexstr(self, items):
""" return list of two-digit HEX strings """
return ('hexstr', ''.join(list(items)))
def swpattern(self, items):
""" return list of four HEX nibbles (or 'x' as wildcard) """
arr = [self.todigit(x) for x in items]
return ''.join(arr)
class ScriptFormatLDR(ScriptFormat):
# parser for the LDR file format as generated by Simulity Profile Editor
ldr_parser = Lark(r"""
script: statement*
?statement: cmd | rst | rem | NEWLINE
BSLASH: "\\\n"
%ignore BSLASH
NONL: /[^\n]/+
rem: "REM" NONL? NEWLINE
ALNUM: DIGIT | LETTER | "_"
key: "[" ALNUM+ "]"
cmd: "CMD" hexstr [swmatch] NEWLINE
cmd_item: hexstr | key
hexstr: hex_item+
hex_item: HEXDIGIT HEXDIGIT
swmatch: "(" swpattern ("," swpattern)* ")"
swpattern: hex_or_x hex_or_x hex_or_x hex_or_x
?hex_or_x: HEXDIGIT | "X" -> x | "x" -> x
rst: "RST" NEWLINE
%import common.ESCAPED_STRING -> STRING
%import common.WS_INLINE
%import common.HEXDIGIT
%import common.DIGIT
%import common.LETTER
%import common.NEWLINE
%ignore WS_INLINE
""", start='script')
def parse_xform(self, text):
tree = self.ldr_parser.parse(text)
#print(tree.pretty())
p = LdrXfrm().transform(tree)
return p

View File

@@ -1,84 +0,0 @@
#!/usr/bin/python3
from lark import Lark, Transformer, Token, Tree
import sys
from format_ldr import ScriptFormatLDR
from format_ipr import ScriptFormatIPR
def split_hex(value):
""" split a string of hex digits into groups (bytes) of two digits. """
return ' '.join(value[i:i+2] for i in range(0, len(value), 2))
def expand_cmd(cmd):
ret = ""
for e in cmd:
if e[0] == 'hexstr':
ret += e[1]
else:
raise ValueError("Unsupported '%s'" % (e[0]))
return ret
def ldr_stmt_to_ipr(s):
""" translate a single LDR statement to IPR format. """
if s == None:
None
elif s == 'reset':
print("RESET")
print("")
elif s[0] == 'rem':
print("//\t%s" % s[1])
elif s[0] == 'cmd':
cmd = s[1]
req = cmd['req']
rsp = cmd['rsp']
print("I: %s" % split_hex(expand_cmd([req])))
if rsp != None and len(rsp) != 1:
if rsp[0] != 'swmatch' or len(rsp[1]) != 1:
raise ValueError("Unsupported '%s'" % (rsp))
print("O: %s" % rsp[1][0])
else:
print("O:")
print("")
else:
print("Unknown %s" % (s.pretty()))
raise ValueError()
test_text = '''
RST
CMD E0 CA DF 1F 13
CMD E0 CA DF 1F (90 00)
CMD E0 CA DF 1F (61 XX, 90 00)
REM foo bar
CMD E4 DA DF 20 09 EA 53 F8 D7 64 1E D9 88 00 \\
(90 00 , 6B 00)
'''
def run_statement(s):
print(s)
def fii(s):
if s.data == 'rst':
print("=> RESET")
# FIXME: actually perform card reset
elif s.data == 'rem':
print(s)
elif s.data == 'cmd':
#print(s)
cmd = s.children[0]
print(s.pretty())
# FIXME: if swmatch: match all contained swpattern
else:
print("Unknown %s" % (s.pretty()))
raise ValueError()
#process_ldr(test_text, run_statement)
#process_ldr(test_text, ldr_stmt_to_ipr)
fmt = ScriptFormatLDR()
fmt.parse_process_file(sys.argv[1], ldr_stmt_to_ipr)
#fmt.parse_process_file(sys.argv[1], run_statement)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
#
# Utility to deal with sim cards and program the 'magic' ones easily
@@ -30,7 +30,6 @@ import os
import random
import re
import sys
import traceback
try:
import json
@@ -39,11 +38,9 @@ except ImportError:
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes, card_detect
from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn, dec_iccid, init_reader
from pySim.ts_51_011 import EF
from pySim.card_handler import *
from pySim.utils import *
from pySim.cards import _cards_classes
from pySim.utils import h2b, swap_nibbles, rpad
def parse_options():
@@ -61,43 +58,18 @@ def parse_options():
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("--modem-device", dest="modem_dev", metavar="DEV",
help="Serial port of modem for Generic SIM Access (3GPP TS 27.007)",
default=None,
)
parser.add_option("--modem-baud", dest="modem_baud", type="int", metavar="BAUD",
help="Baudrate used for modem's port [default: %default]",
default=115200,
)
parser.add_option("--osmocon", dest="osmocon_sock", metavar="PATH",
help="Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)",
default=None,
)
parser.add_option("-t", "--type", dest="type",
help="Card type (user -t list to view) [default: %default]",
default="auto",
)
parser.add_option("-T", "--probe", dest="probe",
help="Determine card type",
default=False, action="store_true"
)
parser.add_option("-a", "--pin-adm", dest="pin_adm",
help="ADM PIN used for provisioning (overwrites default)",
)
parser.add_option("-A", "--pin-adm-hex", dest="pin_adm_hex",
help="ADM PIN used for provisioning, as hex string (16 characters long",
)
parser.add_option("-e", "--erase", dest="erase", action='store_true',
help="Erase beforehand [default: %default]",
default=False,
)
parser.add_option("-S", "--source", dest="source",
help="Data Source[default: %default]",
default="cmdline",
)
# if mode is "cmdline"
parser.add_option("-n", "--name", dest="name",
help="Operator name [default: %default]",
default="Magic",
@@ -106,21 +78,16 @@ def parse_options():
help="Country code [default: %default]",
default=1,
)
parser.add_option("-x", "--mcc", dest="mcc", type="string",
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default="901",
default=901,
)
parser.add_option("-y", "--mnc", dest="mnc", type="string",
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default="55",
)
parser.add_option("--mnclen", dest="mnclen", type="choice",
help="Length of Mobile Network Code [default: %default]",
default=2,
choices=[2, 3],
default=55,
)
parser.add_option("-m", "--smsc", dest="smsc",
help="SMSC number (Start with + for international no.) [default: '00 + country code + 5555']",
help="SMSP [default: '00 + country code + 5555']",
)
parser.add_option("-M", "--smsp", dest="smsp",
help="Raw SMSP content in hex [default: auto from SMSC]",
@@ -132,9 +99,6 @@ def parse_options():
parser.add_option("-i", "--imsi", dest="imsi",
help="International Mobile Subscriber Identity",
)
parser.add_option("--msisdn", dest="msisdn",
help="Mobile Subscriber Integrated Services Digital Number",
)
parser.add_option("-k", "--ki", dest="ki",
help="Ki (default is to randomize)",
)
@@ -147,12 +111,8 @@ def parse_options():
parser.add_option("--acc", dest="acc",
help="Set ACC bits (Access Control Code). not all card types are supported",
)
parser.add_option("--read-imsi", dest="read_imsi", action="store_true",
help="Read the IMSI from the CARD", default=False
)
parser.add_option("--read-iccid", dest="read_iccid", action="store_true",
help="Read the ICCID from the CARD", default=False
)
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
help="Secret used for ICCID/IMSI autogen",
)
@@ -167,47 +127,20 @@ def parse_options():
help="Optional batch state file",
)
# if mode is "csv"
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
help="Read parameters from CSV file rather than command line")
parser.add_option("--write-csv", dest="write_csv", metavar="FILE",
help="Append generated parameters in CSV file",
)
parser.add_option("--write-hlr", dest="write_hlr", metavar="FILE",
help="Append generated parameters to OpenBSC HLR sqlite3",
)
parser.add_option("--dry-run", dest="dry_run",
help="Perform a 'dry run', don't actually program the card",
default=False, action="store_true")
parser.add_option("--card_handler", dest="card_handler", metavar="FILE",
help="Use automatic card handling machine")
(options, args) = parser.parse_args()
if options.type == 'list':
for kls in _cards_classes:
print(kls.name)
print kls.name
sys.exit(0)
if options.probe:
return options
if options.source == 'csv':
if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False) and (options.read_iccid is False):
parser.error("CSV mode needs either an IMSI, --read-imsi, --read-iccid or batch mode")
if options.read_csv is None:
parser.error("CSV mode requires a CSV input file")
elif options.source == 'cmdline':
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
parser.error("If either IMSI or ICCID isn't specified, num is required")
else:
parser.error("Only `cmdline' and `csv' sources supported")
if (options.read_csv is not None) and (options.source != 'csv'):
parser.error("You cannot specify a CSV input file in source != csv")
if (options.batch_mode) and (options.num is None):
options.num = 0
@@ -215,6 +148,9 @@ def parse_options():
if (options.imsi is not None) or (options.iccid is not None):
parser.error("Can't give ICCID/IMSI for batch mode, need to use automatic parameters ! see --num and --secret for more informations")
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
parser.error("If either IMSI or ICCID isn't specified, num is required")
if args:
parser.error("Extraneous arguments")
@@ -227,7 +163,7 @@ def _digits(secret, usage, len, num):
return d[0:len]
def _mcc_mnc_digits(mcc, mnc):
return '%s%s' % (mcc, mnc)
return ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
def _cc_digits(cc):
return ('%03d' if cc > 100 else '%02d') % cc
@@ -272,6 +208,24 @@ def _dbi_binary_quote(s):
return ''.join(out)
def calculate_luhn(cc):
num = map(int, str(cc))
check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
return 0 if check_digit == 10 else check_digit
def derive_milenage_opc(ki_hex, op_hex):
"""
Run the milenage algorithm.
"""
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from pySim.utils import b2h
# We pass in hex string and now need to work on bytes
aes = AES.new(h2b(ki_hex))
opc_bytes = aes.encrypt(h2b(op_hex))
return b2h(strxor(opc_bytes, h2b(op_hex)))
def gen_parameters(opts):
"""Generates Name, ICCID, MCC, MNC, IMSI, SMSP, Ki, PIN-ADM from the
options given by the user"""
@@ -280,17 +234,8 @@ def gen_parameters(opts):
mcc = opts.mcc
mnc = opts.mnc
if not mcc.isdigit() or not mnc.isdigit():
raise ValueError('mcc & mnc must only contain decimal digits')
if len(mcc) < 1 or len(mcc) > 3:
raise ValueError('mcc must be between 1 .. 3 digits')
if len(mnc) < 1 or len(mnc) > 3:
raise ValueError('mnc must be between 1 .. 3 digits')
# MCC always has 3 digits
mcc = lpad(mcc, 3, "0")
# MNC must be at least 2 digits
mnc = lpad(mnc, 2, "0")
if not ((0 < mcc < 999) and (0 < mnc < 999)):
raise ValueError('mcc & mnc must be between 0 and 999')
# Digitize country code (2 or 3 digits)
cc_digits = _cc_digits(opts.country)
@@ -298,26 +243,11 @@ def gen_parameters(opts):
# Digitize MCC/MNC (5 or 6 digits)
plmn_digits = _mcc_mnc_digits(mcc, mnc)
if opts.name is not None:
if len(opts.name) > 16:
raise ValueError('Service Provider Name must max 16 characters!');
if opts.msisdn is not None:
msisdn = opts.msisdn
if msisdn[0] == '+':
msisdn = msisdn[1:]
if not msisdn.isdigit():
raise ValueError('MSISDN must be digits only! '
'Start with \'+\' for international numbers.')
if len(msisdn) > 10 * 2:
# TODO: Support MSISDN of length > 20 (10 Bytes)
raise ValueError('MSISDNs longer than 20 digits are not (yet) supported.')
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
if opts.iccid is not None:
iccid = opts.iccid
if not _isnum(iccid, 19) and not _isnum(iccid, 20):
raise ValueError('ICCID must be 19 or 20 digits !');
if not _isnum(iccid, 19):
raise ValueError('ICCID must be 19 digits !');
else:
if opts.num is None:
@@ -326,7 +256,7 @@ def gen_parameters(opts):
iccid = (
'89' + # Common prefix (telecom)
cc_digits + # Country Code on 2/3 digits
plmn_digits # MCC/MNC on 5/6 digits
plmn_digits # MCC/MNC on 5/6 digits
)
ml = 18 - len(iccid)
@@ -374,19 +304,14 @@ def gen_parameters(opts):
raise ValueError('SMSP must be at least 28 bytes')
else:
ton = "81"
if opts.smsc is not None:
smsc = opts.smsc
if smsc[0] == '+':
ton = "91"
smsc = smsc[1:]
if not _isnum(smsc):
raise ValueError('SMSC must be digits only!\n \
Start with \'+\' for international numbers')
raise ValueError('SMSC must be digits only !')
else:
smsc = '00%d' % opts.country + '5555' # Hack ...
smsc = '%02d' % ((len(smsc) + 3)//2,) + ton + swap_nibbles(rpad(smsc, 20))
smsc = '%02d' % ((len(smsc) + 3)//2,) + "81" + swap_nibbles(rpad(smsc, 20))
smsp = (
'e1' + # Parameters indicator
@@ -416,7 +341,7 @@ def gen_parameters(opts):
else:
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
# OPC (random)
# Ki (random)
if opts.opc is not None:
opc = opts.opc
if not re.match('^[0-9a-fA-F]{32}$', opc):
@@ -427,7 +352,14 @@ def gen_parameters(opts):
else:
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
pin_adm = sanitize_pin_adm(opts)
if opts.pin_adm is not None:
if len(opts.pin_adm) > 8:
raise ValueError("PIN-ADM needs to be <=8 digits")
pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
pin_adm = rpad(pin_adm, 16)
else:
pin_adm = None
# Return that
return {
@@ -441,30 +373,25 @@ def gen_parameters(opts):
'opc' : opc,
'acc' : acc,
'pin_adm' : pin_adm,
'msisdn' : opts.msisdn,
}
def print_parameters(params):
s = ["Generated card parameters :"]
if 'name' in params:
s.append(" > Name : %(name)s")
if 'smsp' in params:
s.append(" > SMSP : %(smsp)s")
s.append(" > ICCID : %(iccid)s")
s.append(" > MCC/MNC : %(mcc)s/%(mnc)s")
s.append(" > IMSI : %(imsi)s")
s.append(" > Ki : %(ki)s")
s.append(" > OPC : %(opc)s")
if 'acc' in params:
s.append(" > ACC : %(acc)s")
s.append(" > ADM1(hex): %(pin_adm)s")
print("\n".join(s) % params)
print """Generated card parameters :
> Name : %(name)s
> SMSP : %(smsp)s
> ICCID : %(iccid)s
> MCC/MNC : %(mcc)d/%(mnc)d
> IMSI : %(imsi)s
> Ki : %(ki)s
> OPC : %(opc)s
> ACC : %(acc)s
""" % params
def write_params_csv(opts, params):
# csv
def write_parameters(opts, params):
# CSV
if opts.write_csv:
import csv
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
@@ -473,68 +400,6 @@ def write_params_csv(opts, params):
cw.writerow([params[x] for x in row])
f.close()
def _read_params_csv(opts, iccid=None, imsi=None):
import csv
f = open(opts.read_csv, 'r')
cr = csv.DictReader(f)
# Lower-case fieldnames
cr.fieldnames = [ field.lower() for field in cr.fieldnames ]
i = 0
if not 'iccid' in cr.fieldnames:
raise Exception("CSV file in wrong format!")
for row in cr:
if opts.num is not None and opts.read_iccid is False and opts.read_imsi is False:
if opts.num == i:
f.close()
return row;
i += 1
if row['iccid'] == iccid:
f.close()
return row;
if row['imsi'] == imsi:
f.close()
return row;
f.close()
return None
def read_params_csv(opts, imsi=None, iccid=None):
row = _read_params_csv(opts, iccid=iccid, imsi=imsi)
if row is not None:
row['mcc'] = row.get('mcc', mcc_from_imsi(row.get('imsi')))
row['mnc'] = row.get('mnc', mnc_from_imsi(row.get('imsi')))
pin_adm = None
# We need to escape the pin_adm we get from the csv
if 'pin_adm' in row:
pin_adm = ''.join(['%02x'%(ord(x)) for x in row['pin_adm']])
# Stay compatible to the odoo csv format
elif 'adm1' in row:
pin_adm = ''.join(['%02x'%(ord(x)) for x in row['adm1']])
if pin_adm:
row['pin_adm'] = rpad(pin_adm, 16)
# If the CSV-File defines a pin_adm_hex field use this field to
# generate pin_adm from that.
pin_adm_hex = row.get('pin_adm_hex')
if pin_adm_hex:
if len(pin_adm_hex) == 16:
row['pin_adm'] = pin_adm_hex
# Ensure that it's hex-encoded
try:
try_encode = h2b(pin_adm)
except ValueError:
raise ValueError("pin_adm_hex needs to be hex encoded using this option")
else:
raise ValueError("pin_adm_hex needs to be exactly 16 digits (hex encoded)")
return row
def write_params_hlr(opts, params):
# SQLite3 OpenBSC HLR
if opts.write_hlr:
import sqlite3
@@ -548,7 +413,7 @@ def write_params_hlr(opts, params):
[
params['imsi'],
params['name'],
'9' + params['iccid'][-5:-1]
'9' + params['iccid'][-5:]
],
)
sub_id = c.lastrowid
@@ -565,10 +430,6 @@ def write_params_hlr(opts, params):
conn.commit()
conn.close()
def write_parameters(opts, params):
write_params_csv(opts, params)
write_params_hlr(opts, params)
BATCH_STATE = [ 'name', 'country', 'mcc', 'mnc', 'smsp', 'secret', 'num' ]
BATCH_INCOMPATIBLE = ['iccid', 'imsi', 'ki']
@@ -580,7 +441,7 @@ def init_batch(opts):
for k in BATCH_INCOMPATIBLE:
if getattr(opts, k):
print("Incompatible option with batch_state: %s" % (k,))
print "Incompatible option with batch_state: %s" % (k,)
sys.exit(-1)
# Don't load state if there is none ...
@@ -588,7 +449,7 @@ def init_batch(opts):
return
if not os.path.isfile(opts.batch_state):
print("No state file yet")
print "No state file yet"
return
# Get stored data
@@ -611,72 +472,34 @@ def save_batch(opts):
fh.close()
def process_card(opts, first, card_handler):
def card_detect(opts, scc):
if opts.dry_run is False:
# Connect transport
card_handler.get(first)
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if opts.type in ("auto", "auto_once"):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card:
print "Autodetected card type %s" % card.name
card.reset()
break
if opts.dry_run is False:
# Get card
card = card_detect(opts.type, scc)
if card is None:
print("No card detected!")
return -1
print "Autodetection failed"
return
# Probe only
if opts.probe:
return 0
if opts.type == "auto_once":
opts.type = card.name
# Erase if requested
if opts.erase:
print("Formatting ...")
card.erase()
card.reset()
elif opts.type in ctypes:
card = ctypes[opts.type](scc)
# Generate parameters
if opts.source == 'cmdline':
cp = gen_parameters(opts)
elif opts.source == 'csv':
imsi = None
iccid = None
if opts.read_iccid:
if opts.dry_run:
# Connect transport
card_handler.get(false)
(res,_) = scc.read_binary(['3f00', '2fe2'], length=10)
iccid = dec_iccid(res)
elif opts.read_imsi:
if opts.dry_run:
# Connect transport
card_handler.get(false)
(res,_) = scc.read_binary(EF['IMSI'])
imsi = swap_nibbles(res)[3:]
else:
imsi = opts.imsi
cp = read_params_csv(opts, imsi=imsi, iccid=iccid)
if cp is None:
print("Error reading parameters from CSV file!\n")
return 2
print_parameters(cp)
if opts.dry_run is False:
# Program the card
print("Programming ...")
card.program(cp)
else:
print("Dry Run: NOT PROGRAMMING!")
raise ValueError("Unknown card type %s" % opts.type)
# Write parameters permanently
write_parameters(opts, cp)
# Batch mode state update and save
if opts.num is not None:
opts.num += 1
save_batch(opts)
card_handler.done()
return 0
return card
if __name__ == '__main__':
@@ -684,57 +507,67 @@ if __name__ == '__main__':
# Parse options
opts = parse_options()
# Init card reader driver
sl = init_reader(opts)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
# If we use a CSV file as data input, check if the CSV file exists.
if opts.source == 'csv':
print("Using CSV file as data input: " + str(opts.read_csv))
if not os.path.isfile(opts.read_csv):
print("CSV file not found!")
sys.exit(1)
# Batch mode init
init_batch(opts)
if opts.card_handler:
card_handler = card_handler_auto(sl, opts.card_handler)
else:
card_handler = card_handler(sl)
# Iterate
done = False
first = True
card = None
while 1:
try:
rc = process_card(opts, first, card_handler)
except (KeyboardInterrupt):
print("")
print("Terminated by user!")
sys.exit(0)
except (SystemExit):
raise
except:
print("")
print("Card programming failed with an execption:")
print("---------------------8<---------------------")
traceback.print_exc()
print("---------------------8<---------------------")
print("")
rc = -1
# Something did not work as well as expected, however, lets
# make sure the card is pulled from the reader.
if rc != 0:
card_handler.error()
# If we are not in batch mode we are done in any case, so lets
# exit here.
if not opts.batch_mode:
sys.exit(rc)
while not done:
# Connect transport
print "Insert card now (or CTRL-C to cancel)"
sl.wait_for_card(newcardonly=not first)
# Not the first anymore !
first = False
# Get card
card = card_detect(opts, scc)
if card is None:
if opts.batch_mode:
first = False
continue
else:
sys.exit(-1)
# Erase if requested
if opts.erase:
print "Formatting ..."
card.erase()
card.reset()
# Generate parameters
cp = gen_parameters(opts)
print_parameters(cp)
# Program the card
print "Programming ..."
card.program(cp)
# Write parameters permanently
write_parameters(opts, cp)
# Batch mode state update and save
if opts.num is not None:
opts.num += 1
save_batch(opts)
# Done for this card and maybe for everything ?
print "Done !\n"
if not opts.batch_mode:
done = True

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
#
# Utility to display some informations about a SIM card
@@ -28,14 +28,16 @@ import os
import random
import re
import sys
from pySim.ts_51_011 import EF, DF, EF_SST_map
from pySim.ts_31_102 import EF_UST_map, EF_USIM_ADF_map
from pySim.ts_31_103 import EF_IST_map
try:
import json
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.cards import card_detect, Card
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_msisdn
from pySim.utils import format_xplmn_w_act, dec_spn, dec_st, init_reader, dec_epdgid
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid
def parse_options():
@@ -53,18 +55,6 @@ def parse_options():
help="Which PC/SC reader number for SIM access",
default=None,
)
parser.add_option("--modem-device", dest="modem_dev", metavar="DEV",
help="Serial port of modem for Generic SIM Access (3GPP TS 27.007)",
default=None,
)
parser.add_option("--modem-baud", dest="modem_baud", type="int", metavar="BAUD",
help="Baudrate used for modem's port [default: %default]",
default=115200,
)
parser.add_option("--osmocon", dest="osmocon_sock", metavar="PATH",
help="Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)",
default=None,
)
(options, args) = parser.parse_args()
@@ -79,8 +69,13 @@ if __name__ == '__main__':
# Parse options
opts = parse_options()
# Init card reader driver
sl = init_reader(opts)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
@@ -88,121 +83,41 @@ if __name__ == '__main__':
# Wait for SIM card
sl.wait_for_card()
# Assuming UICC SIM
scc.cla_byte = "00"
scc.sel_ctrl = "0004"
# Testing for Classic SIM or UICC
(res, sw) = sl.send_apdu(scc.cla_byte + "a4" + scc.sel_ctrl + "02" + "3f00")
if sw == '6e00':
# Just a Classic SIM
scc.cla_byte = "a0"
scc.sel_ctrl = "0000"
# Program the card
print("Reading ...")
# Initialize Card object by auto detecting the card
card = card_detect("auto", scc) or Card(scc)
# Read all AIDs on the UICC
card.read_aids()
# EF.ICCID
(res, sw) = card.read_iccid()
(res, sw) = scc.read_binary(['3f00', '2fe2'])
if sw == '9000':
print("ICCID: %s" % (res,))
print("ICCID: %s" % (dec_iccid(res),))
else:
print("ICCID: Can't read, response code = %s" % (sw,))
# EF.IMSI
(res, sw) = card.read_imsi()
(res, sw) = scc.read_binary(['3f00', '7f20', '6f07'])
if sw == '9000':
print("IMSI: %s" % (res,))
print("IMSI: %s" % (dec_imsi(res),))
else:
print("IMSI: Can't read, response code = %s" % (sw,))
# EF.GID1
try:
(res, sw) = card.read_gid1()
if sw == '9000':
print("GID1: %s" % (res,))
else:
print("GID1: Can't read, response code = %s" % (sw,))
except Exception as e:
print("GID1: Can't read file -- %s" % (str(e),))
# EF.GID2
try:
(res, sw) = card.read_binary('GID2')
if sw == '9000':
print("GID2: %s" % (res,))
else:
print("GID2: Can't read, response code = %s" % (sw,))
except Exception as e:
print("GID2: Can't read file -- %s" % (str(e),))
# EF.SMSP
(res, sw) = card.read_record('SMSP', 1)
(res, sw) = scc.read_record(['3f00', '7f10', '6f42'], 1)
if sw == '9000':
print("SMSP: %s" % (res,))
else:
print("SMSP: Can't read, response code = %s" % (sw,))
# EF.SPN
try:
(res, sw) = card.read_spn()
if sw == '9000':
print("SPN: %s" % (res[0] or "Not available"))
print("Display HPLMN: %s" % (res[1],))
print("Display OPLMN: %s" % (res[2],))
else:
print("SPN: Can't read, response code = %s" % (sw,))
except Exception as e:
print("SPN: Can't read file -- %s" % (str(e),))
# EF.PLMNsel
try:
(res, sw) = card.read_binary('PLMNsel')
if sw == '9000':
print("PLMNsel: %s" % (res))
else:
print("PLMNsel: Can't read, response code = %s" % (sw,))
except Exception as e:
print("PLMNsel: Can't read file -- " + str(e))
# EF.PLMNwAcT
try:
(res, sw) = card.read_plmn_act()
if sw == '9000':
print("PLMNwAcT:\n%s" % (res))
else:
print("PLMNwAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("PLMNwAcT: Can't read file -- " + str(e))
# EF.OPLMNwAcT
try:
(res, sw) = card.read_oplmn_act()
if sw == '9000':
print("OPLMNwAcT:\n%s" % (res))
else:
print("OPLMNwAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("OPLMNwAcT: Can't read file -- " + str(e))
# EF.HPLMNAcT
try:
(res, sw) = card.read_hplmn_act()
if sw == '9000':
print("HPLMNAcT:\n%s" % (res))
else:
print("HPLMNAcT: Can't read, response code = %s" % (sw,))
except Exception as e:
print("HPLMNAcT: Can't read file -- " + str(e))
# EF.HPLMN
# (res, sw) = scc.read_binary(['3f00', '7f20', '6f30'])
# if sw == '9000':
# print("HPLMN: %s" % (res))
# print("HPLMN: %s" % (dec_hplmn(res),))
# else:
# print("HPLMN: Can't read, response code = %s" % (sw,))
# FIXME
# EF.ACC
(res, sw) = card.read_binary('ACC')
(res, sw) = scc.read_binary(['3f00', '7f20', '6f78'])
if sw == '9000':
print("ACC: %s" % (res,))
else:
@@ -210,75 +125,17 @@ if __name__ == '__main__':
# EF.MSISDN
try:
(res, sw) = card.read_msisdn()
# print(scc.record_size(['3f00', '7f10', '6f40']))
(res, sw) = scc.read_record(['3f00', '7f10', '6f40'], 1)
if sw == '9000':
# (npi, ton, msisdn) = res
if res is not None:
print("MSISDN (NPI=%d ToN=%d): %s" % res)
if res[1] != 'f':
print("MSISDN: %s" % (res,))
else:
print("MSISDN: Not available")
else:
print("MSISDN: Can't read, response code = %s" % (sw,))
except Exception as e:
print("MSISDN: Can't read file -- " + str(e))
# EF.AD
(res, sw) = card.read_binary('AD')
if sw == '9000':
print("AD: %s" % (res,))
else:
print("AD: Can't read, response code = %s" % (sw,))
# EF.SST
(res, sw) = card.read_binary('SST')
if sw == '9000':
print("SIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res))
else:
print("SIM Service Table: Can't read, response code = %s" % (sw,))
# Check whether we have th AID of USIM, if so select it by its AID
# EF.UST - File Id in ADF USIM : 6f38
if '9000' == card.select_adf_by_aid():
# EF.EHPLMN
if card.file_exists(EF_USIM_ADF_map['EHPLMN']):
(res, sw) = card.read_ehplmn()
if sw == '9000':
print("EHPLMN:\n%s" % (res))
else:
print("EHPLMN: Can't read, response code = %s" % (sw,))
# EF.UST
(res, sw) = card.read_binary(EF_USIM_ADF_map['UST'])
if sw == '9000':
print("USIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res, table="usim"))
else:
print("USIM Service Table: Can't read, response code = %s" % (sw,))
#EF.ePDGId - Home ePDG Identifier
try:
(res, sw) = card.read_binary(EF_USIM_ADF_map['ePDGId'])
if sw == '9000':
content = dec_epdgid(res)
print("ePDGId:\n%s" % (len(content) and content or '\tNot available\n',))
else:
print("ePDGId: Can't read, response code = %s" % (sw,))
except Exception as e:
print("ePDGId: Can't read file -- " + str(e))
# Check whether we have th AID of ISIM, if so select it by its AID
# EF.IST - File Id in ADF ISIM : 6f07
if '9000' == card.select_adf_by_aid(adf="isim"):
# EF.IST
(res, sw) = card.read_binary('6f07')
if sw == '9000':
print("ISIM Service Table: %s" % res)
# Print those which are available
print("%s" % dec_st(res, table="isim"))
else:
print("ISIM Service Table: Can't read, response code = %s" % (sw,))
except:
print "MSISDN: Can't read. Probably not existing file"
# Done for this card and maybe for everything ?
print("Done !\n")
print "Done !\n"

View File

@@ -1 +0,0 @@

View File

@@ -1,108 +0,0 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
""" pySim: card handler utilities
"""
#
# (C) 2019 by Sysmocom s.f.m.c. GmbH
# All Rights Reserved
#
# 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/>.
#
import subprocess
import sys
import yaml
# Manual card handler: User is prompted to insert/remove card from the reader.
class card_handler:
sl = None
def __init__(self, sl):
self.sl = sl
def get(self, first = False):
print("Ready for Programming: Insert card now (or CTRL-C to cancel)")
self.sl.wait_for_card(newcardonly=not first)
def error(self):
print("Programming failed: Remove card from reader")
print("")
def done(self):
print("Programming successful: Remove card from reader")
print("")
# Automatic card handler: A machine is used to handle the cards.
class card_handler_auto:
sl = None
cmds = None
verbose = True
def __init__(self, sl, config_file):
print("Card handler Config-file: " + str(config_file))
self.sl = sl
with open(config_file) as cfg:
self.cmds = yaml.load(cfg, Loader=yaml.FullLoader)
self.verbose = (self.cmds.get('verbose') == True)
def __print_outout(self,out):
print("")
print("Card handler output:")
print("---------------------8<---------------------")
stdout = out[0].strip()
if len(stdout) > 0:
print("stdout:")
print(stdout)
stderr = out[1].strip()
if len(stderr) > 0:
print("stderr:")
print(stderr)
print("---------------------8<---------------------")
print("")
def __exec_cmd(self, command):
print("Card handler Commandline: " + str(command))
proc = subprocess.Popen([command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out = proc.communicate()
rc = proc.returncode
if rc != 0 or self.verbose:
self.__print_outout(out)
if rc != 0:
print("")
print("Error: Card handler failure! (rc=" + str(rc) + ")")
sys.exit(rc)
def get(self, first = False):
print("Ready for Programming: Transporting card into the reader-bay...")
self.__exec_cmd(self.cmds['get'])
self.sl.connect()
def error(self):
print("Programming failed: Transporting card to the error-bin...")
self.__exec_cmd(self.cmds['error'])
print("")
def done(self):
print("Programming successful: Transporting card into the collector bin...")
self.__exec_cmd(self.cmds['done'])
print("")

View File

@@ -7,7 +7,6 @@
#
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
# Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com>
#
# 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
@@ -23,272 +22,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pySim.ts_51_011 import EF, DF
from pySim.ts_31_102 import EF_USIM_ADF_map
from pySim.utils import *
from smartcard.util import toBytes
from pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad, enc_imsi, enc_iccid, enc_plmn
class Card(object):
def __init__(self, scc):
self._scc = scc
self._adm_chv_num = 4
self._aids = []
def reset(self):
self._scc.reset_card()
def erase(self):
print("warning: erasing is not supported for specified card type!")
return
def file_exists(self, fid):
res_arr = self._scc.try_select_file(fid)
for res in res_arr:
if res[1] != '9000':
return False
return True
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 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))
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 technologues 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'):
"""
See note in update_hplmn_act()
"""
# get size and write EF.OPLMNwAcT
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'):
"""
See note in update_hplmn_act()
"""
# get size and write EF.PLMNwAcT
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):
#See also: 3GPP TS 31.102, chapter 4.2.18
mnclen = len(str(mnc))
if mnclen == 1:
mnclen = 2
if mnclen > 3:
raise RuntimeError('unable to calculate proper mnclen')
data, sw = self._scc.read_binary(EF['AD'], length=None, offset=0)
# Reset contents to EF.AD in case the file is uninintalized
if data.lower() == "ffffffff":
data = "00000000"
content = data[0:6] + "%02X" % mnclen
data, sw = self._scc.update_binary(EF['AD'], content)
return sw
def read_spn(self):
(spn, sw) = self._scc.read_binary(EF['SPN'])
if sw == '9000':
return (dec_spn(spn), sw)
else:
return (None, sw)
def update_spn(self, name, hplmn_disp=False, oplmn_disp=False):
content = enc_spn(name, hplmn_disp, oplmn_disp)
data, sw = self._scc.update_binary(EF['SPN'], rpad(content, 32))
return sw
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 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)
# Read the (full) AID for either ISIM or USIM or ISIM application
def read_aid(self, isim = False):
# First (known) halves of the AID
aid_usim = "a0000000871002"
aid_isim = "a0000000871004"
# Select which one to look for
if isim:
aid = aid_isim
else:
aid = aid_usim
# Find out how many records the EF.DIR has, then go through
# all records and try to find the AID we are looking for
aid_record_count = self._scc.record_count(['2F00'])
for i in range(0, aid_record_count):
record = self._scc.read_record(['2F00'], i + 1)
if aid in record[0]:
aid_len = int(record[0][6:8], 16)
return record[0][8:8 + aid_len * 2]
return None
# Fetch all the AIDs present on UICC
def read_aids(self):
try:
# 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'])
for i in range(0, rec_cnt):
rec = self._scc.read_record(EF['DIR'], 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])
except Exception as e:
print("Can't read AIDs from SIM -- %s" % (str(e),))
# Select ADF.U/ISIM in the Card using its full AID
def select_adf_by_aid(self, adf="usim"):
# Check for valid ADF name
if adf not in ["usim", "isim"]:
return None
# First (known) halves of the U/ISIM AID
aid_map = {}
aid_map["usim"] = "a0000000871002"
aid_map["isim"] = "a0000000871004"
for aid in self._aids:
if aid_map[adf] in aid:
(res, sw) = self._scc.select_adf(aid)
return sw
return None
# Erase the contents of a file
def erase_binary(self, ef):
len = self._scc.binary_size(ef)
self._scc.update_binary(ef, "ff" * len, offset=0, verify=True)
# Erase the contents of a single record
def erase_record(self, ef, rec_no):
len = self._scc.record_size(ef)
self._scc.update_record(ef, rec_no, "ff" * len, force_len=False, verify=True)
class UsimCard(Card):
def __init__(self, ssc):
super(UsimCard, self).__init__(ssc)
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
class _MagicSimBase(Card):
"""
@@ -296,7 +40,7 @@ class _MagicSimBase(Card):
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 programable EFs
- [1 .. N] for the programable EFs
* 3f00/7f4d/8f0c : Operator Name
@@ -483,12 +227,12 @@ class FakeMagicSim(Card):
# Set first entry
entry = (
'81' + # 1b Status: Valid & Active
'81' + # 1b Status: Valid & Active
rpad(b2h(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)
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)
@@ -521,7 +265,7 @@ class GrcardSim(Card):
# Authenticate using ADM PIN 5
if p['pin_adm']:
pin = h2b(p['pin_adm'])
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
self._scc.verify_chv(5, pin)
@@ -539,9 +283,8 @@ class GrcardSim(Card):
data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['3f00', '7f10', '6f42'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
r = self._scc.select_file(['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']
@@ -560,6 +303,8 @@ class GrcardSim(Card):
# FIXME: EF.MSISDN
def erase(self):
return
class SysmoSIMgr1(GrcardSim):
"""
@@ -569,17 +314,8 @@ class SysmoSIMgr1(GrcardSim):
"""
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):
class SysmoUSIMgr1(Card):
"""
sysmocom sysmoUSIM-GR1
"""
@@ -604,6 +340,9 @@ class SysmoUSIMgr1(UsimCard):
)
data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
def erase(self):
return
class SysmoSIMgr2(Card):
"""
@@ -614,12 +353,7 @@ class SysmoSIMgr2(Card):
@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
# TODO: look for ATR 3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68
return None
def program(self, p):
@@ -636,7 +370,7 @@ class SysmoSIMgr2(Card):
# 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'])
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
@@ -676,11 +410,12 @@ class SysmoSIMgr2(Card):
r = self._scc.select_file(['3f00', '7f10'])
# write EF.SMSP
if p.get('smsp'):
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
def erase(self):
return
class SysmoUSIMSJS1(UsimCard):
class SysmoUSIMSJS1(Card):
"""
sysmocom sysmoUSIM-SJS1
"""
@@ -690,543 +425,50 @@ class SysmoUSIMSJS1(UsimCard):
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
# TODO: look for ATR 3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5
return None
def program(self, p):
# authenticate as ADM using default key (written on the card..)
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_file(['3f00'])
# write EF.ICCID
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
# select DF_GSM
r = self._scc.select_file(['7f20'])
# authenticate as ADM using default key (written on the card..)
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']))
# 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:
content = enc_spn(p['name'], True, True)
data, sw = self._scc.update_binary('6F46', rpad(content, 32))
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'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['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 + 'ff' * 2
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
class FairwavesSIM(Card):
"""
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):
# authenticate as ADM1
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
sw = self.verify_adm(h2b(p['pin_adm']))
if sw != '9000':
raise RuntimeError('Failed to authenticate with ADM key %s'%(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(Card):
"""
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_file(['3f00'])
# write EF.ICCID
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
r = self._scc.select_file(['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_file(['7f20'])
content = "01" + p['opc']
data, sw = self._scc.update_binary('00F7', content)
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
class WavemobileSim(Card):
"""
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")
sw = self.verify_adm(h2b(p['pin_adm']))
if sw != '9000':
raise RuntimeError('Failed to authenticate with ADM key %s'%(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 propritary 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 propritary 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'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
return None
# write EF.AUTH
content = "0101"
r = self._scc.select_file(['7FCC', '6f00'])
data, sw = self._scc.update_binary('6f00', content)
class SysmoISIMSJA2(UsimCard):
"""
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 program(self, p):
# authenticate as ADM using default key (written on the card..)
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']))
# 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_file(['7f20'])
# 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'):
sw = self.update_ad(p['mnc'])
if sw != '9000':
print("Programming AD failed with code %s"%sw)
# EF.SMSP
if p.get('smsp'):
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
# update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
# hard linked to EF-USIM_AUTH_KEY)
self._scc.select_file(['3f00'])
self._scc.select_file(['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
self._scc.select_file(['3f00'])
aid = self.read_aid(isim = True)
if (aid):
self._scc.select_adf(aid)
if p.get('ki'):
self._scc.update_binary('af20', p['ki'], 1)
if p.get('opc'):
self._scc.update_binary('af20', p['opc'], 17)
self._scc.select_file(['3f00'])
aid = self.read_aid()
if (aid):
# update EF-USIM_AUTH_KEY in ADF.USIM
self._scc.select_adf(aid)
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)
def erase(self):
return
# In order for autodetection ...
# In order for autodetection ...
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2 ]
def card_autodetect(scc):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card is not None:
card.reset()
return card
return None
def card_detect(ctype, scc):
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if ctype in ("auto", "auto_once"):
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
if ctype == "auto_once":
ctype = card.name
elif ctype in ctypes:
card = ctypes[ctype](scc)
else:
raise ValueError("Unknown card type: %s" % ctype)
return card
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1 ]

View File

@@ -24,67 +24,11 @@
from pySim.utils import rpad, b2h
class SimCardCommands(object):
def __init__(self, transport):
self._tp = transport;
self._cla_byte = "a0"
self.sel_ctrl = "0000"
# Extract a single FCP item from TLV
def __parse_fcp(self, fcp):
# see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF,
# DF or ADF
from pytlv.TLV import TLV
tlvparser = TLV(['82', '83', '84', 'a5', '8a', '8b', '8c', '80', 'ab', 'c6', '81', '88'])
# pytlv is case sensitive!
fcp = fcp.lower()
if fcp[0:2] != '62':
raise ValueError('Tag of the FCP template does not match, expected 62 but got %s'%fcp[0:2])
# Unfortunately the spec is not very clear if the FCP length is
# coded as one or two byte vale, so we have to try it out by
# checking if the length of the remaining TLV string matches
# what we get in the length field.
# See also ETSI TS 102 221, chapter 11.1.1.3.0 Base coding.
exp_tlv_len = int(fcp[2:4], 16)
if len(fcp[4:]) // 2 == exp_tlv_len:
skip = 4
else:
exp_tlv_len = int(fcp[2:6], 16)
if len(fcp[4:]) // 2 == exp_tlv_len:
skip = 6
# Skip FCP tag and length
tlv = fcp[skip:]
return tlvparser.parse(tlv)
# Tell the length of a record by the card response
# USIMs respond with an FCP template, which is different
# from what SIMs responds. See also:
# USIM: ETSI TS 102 221, chapter 11.1.1.3 Response Data
# SIM: GSM 11.11, chapter 9.2.1 SELECT
def __record_len(self, r):
if self.sel_ctrl == "0004":
tlv_parsed = self.__parse_fcp(r[-1])
file_descriptor = tlv_parsed['82']
# See also ETSI TS 102 221, chapter 11.1.1.4.3 File Descriptor
return int(file_descriptor[4:8], 16)
else:
return int(r[-1][28:30], 16)
# Tell the length of a binary file. See also comment
# above.
def __len(self, r):
if self.sel_ctrl == "0004":
tlv_parsed = self.__parse_fcp(r[-1])
return int(tlv_parsed['80'], 16)
else:
return int(r[-1][4:8], 16)
def get_atr(self):
return self._tp.get_atr()
@property
def cla_byte(self):
@@ -93,104 +37,58 @@ class SimCardCommands(object):
def cla_byte(self, value):
self._cla_byte = value
@property
def sel_ctrl(self):
return self._sel_ctrl
@sel_ctrl.setter
def sel_ctrl(self, value):
self._sel_ctrl = value
def try_select_file(self, dir_list):
rv = []
if type(dir_list) is not list:
dir_list = [dir_list]
for i in dir_list:
data, sw = self._tp.send_apdu(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
rv.append((data, sw))
if sw != '9000':
return rv
return rv
def select_file(self, dir_list):
rv = []
if type(dir_list) is not list:
dir_list = [dir_list]
for i in dir_list:
data, sw = self._tp.send_apdu_checksw(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
data, sw = self._tp.send_apdu_checksw(self.cla_byte + "a4000C02" + i)
rv.append(data)
return rv
def select_adf(self, aid):
aidlen = ("0" + format(len(aid) // 2, 'x'))[-2:]
return self._tp.send_apdu_checksw(self.cla_byte + "a4" + "0404" + aidlen + aid)
def read_binary(self, ef, length=None, offset=0):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
if len(r[-1]) == 0:
return (None, None)
if length is None:
length = self.__len(r) - offset
total_data = ''
while offset < length:
chunk_len = min(255, length-offset)
pdu = self.cla_byte + 'b0%04x%02x' % (offset, chunk_len)
data,sw = self._tp.send_apdu(pdu)
if sw == '9000':
total_data += data
offset += chunk_len
else:
raise ValueError('Failed to read (offset %d)' % (offset))
return total_data, sw
length = int(r[-1][4:8], 16) - offset
pdu = self.cla_byte + 'b0%04x%02x' % (offset, (min(256, length) & 0xff))
return self._tp.send_apdu(pdu)
def update_binary(self, ef, data, offset=0, verify=False):
def update_binary(self, ef, data, offset=0):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
self.select_file(ef)
pdu = self.cla_byte + 'd6%04x%02x' % (offset, len(data) // 2) + data
res = self._tp.send_apdu_checksw(pdu)
if verify:
self.verify_binary(ef, data, offset)
return res
def verify_binary(self, ef, data, offset=0):
res = self.read_binary(ef, len(data) // 2, offset)
if res[0].lower() != data.lower():
raise ValueError('Binary verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
pdu = self.cla_byte + 'd6%04x%02x' % (offset, len(data)/2) + data
return self._tp.send_apdu_checksw(pdu)
def read_record(self, ef, rec_no):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
rec_length = self.__record_len(r)
rec_length = int(r[-1][28:30], 16)
pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length)
return self._tp.send_apdu(pdu)
def update_record(self, ef, rec_no, data, force_len=False, verify=False):
def update_record(self, ef, rec_no, data, force_len=False):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
if not force_len:
rec_length = self.__record_len(r)
if (len(data) // 2 != rec_length):
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data) // 2))
rec_length = int(r[-1][28:30], 16)
if (len(data)/2 != rec_length):
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data)/2))
else:
rec_length = len(data) // 2
rec_length = len(data)/2
pdu = (self.cla_byte + 'dc%02x04%02x' % (rec_no, rec_length)) + data
res = self._tp.send_apdu_checksw(pdu)
if verify:
self.verify_record(ef, rec_no, data)
return res
def verify_record(self, ef, rec_no, data):
res = self.read_record(ef, rec_no)
if res[0].lower() != data.lower():
raise ValueError('Record verification failed (expected %s, got %s)' % (data.lower(), res[0].lower()))
return self._tp.send_apdu_checksw(pdu)
def record_size(self, ef):
r = self.select_file(ef)
return self.__record_len(r)
return int(r[-1][28:30], 16)
def record_count(self, ef):
r = self.select_file(ef)
return self.__len(r) // self.__record_len(r)
def binary_size(self, ef):
r = self.select_file(ef)
return self.__len(r)
return int(r[-1][4:8], 16) // int(r[-1][28:30], 16)
def run_gsm(self, rand):
if len(rand) != 32:
@@ -203,4 +101,4 @@ class SimCardCommands(object):
def verify_chv(self, chv_no, code):
fc = rpad(b2h(code), 16)
return self._tp.send_apdu_checksw(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)
return self._tp.send_apdu_checksw(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)

View File

@@ -23,17 +23,11 @@
from __future__ import absolute_import
try:
# This is for compatibility with python 2 and 3
from exceptions import Exception
except:
import exceptions
class NoCardError(exceptions.Exception):
pass
class NoCardError(Exception):
pass
class ProtocolError(Exception):
pass
class ReaderError(Exception):
class ProtocolError(exceptions.Exception):
pass

View File

@@ -28,7 +28,7 @@ class LinkBase(object):
timeout : Maximum wait time (None=no timeout)
newcardonly : Should we wait for a new card, or an already
inserted one ?
inserted one ?
"""
pass
@@ -52,8 +52,8 @@ class LinkBase(object):
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
pass
@@ -62,18 +62,12 @@ class LinkBase(object):
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
data, sw = self.send_apdu_raw(pdu)
# When whe have sent the first APDU, the SW may indicate that there are response bytes
# available. There are two SWs commonly used for this 9fxx (sim) and 61xx (usim), where
# xx is the number of response bytes available.
# See also:
# SW1=9F: 3GPP TS 51.011 9.4.1, Responses to commands which are correctly executed
# SW1=61: ISO/IEC 7816-4, Table 5 — General meaning of the interindustry values of SW1-SW2
if (sw is not None) and ((sw[0:2] == '9f') or (sw[0:2] == '61')):
if (sw is not None) and (sw[0:2] == '9f'):
pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
data, sw = self.send_apdu_raw(pdu_gr)
@@ -83,23 +77,12 @@ class LinkBase(object):
"""send_apdu_checksw(pdu,sw): Sends an APDU and check returned SW
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
sw : string of 4 hexadecimal characters (ex. "9000"). The
user may mask out certain digits using a '?' to add some
ambiguity if needed.
sw : string of 4 hexadecimal characters (ex. "9000")
return : tuple(data, sw), where
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
data : string (in hex) of returned data (ex. "074F4EFFFF")
sw : string (in hex) of status word (ex. "9000")
"""
rv = self.send_apdu(pdu)
# Create a masked version of the returned status word
sw_masked = ""
for i in range(0, 4):
if sw.lower()[i] == '?':
sw_masked = sw_masked + '?'
else:
sw_masked = sw_masked + rv[1][i].lower()
if sw.lower() != sw_masked:
raise RuntimeError("SW match failed! Expected %s and got %s." % (sw.lower(), rv[1]))
if sw.lower() != rv[1]:
raise RuntimeError("SW match failed ! Expected %s and got %s." % (sw.lower(), rv[1]))
return rv

View File

@@ -1,157 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" pySim: Transport Link for Calypso bases phones
"""
#
# Copyright (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
#
# 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 __future__ import absolute_import
import select
import struct
import socket
import os
from pySim.transport import LinkBase
from pySim.exceptions import *
from pySim.utils import h2b, b2h
class L1CTLMessage(object):
# Every (encoded) L1CTL message has the following structure:
# - msg_length (2 bytes, net order)
# - l1ctl_hdr (packed structure)
# - msg_type
# - flags
# - padding (2 spare bytes)
# - ... payload ...
def __init__(self, msg_type, flags = 0x00):
# Init L1CTL message header
self.data = struct.pack("BBxx", msg_type, flags)
def gen_msg(self):
return struct.pack("!H", len(self.data)) + self.data
class L1CTLMessageReset(L1CTLMessage):
# L1CTL message types
L1CTL_RESET_REQ = 0x0d
L1CTL_RESET_IND = 0x07
L1CTL_RESET_CONF = 0x0e
# Reset types
L1CTL_RES_T_BOOT = 0x00
L1CTL_RES_T_FULL = 0x01
L1CTL_RES_T_SCHED = 0x02
def __init__(self, type = L1CTL_RES_T_FULL):
super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
self.data += struct.pack("Bxxx", type)
class L1CTLMessageSIM(L1CTLMessage):
# SIM related message types
L1CTL_SIM_REQ = 0x16
L1CTL_SIM_CONF = 0x17
def __init__(self, pdu):
super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
self.data += pdu
class CalypsoSimLink(LinkBase):
def __init__(self, sock_path = "/tmp/osmocom_l2"):
# Make sure that a given socket path exists
if not os.path.exists(sock_path):
raise ReaderError("There is no such ('%s') UNIX socket" % sock_path)
print("Connecting to osmocon at '%s'..." % sock_path)
# Establish a client connection
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(sock_path)
def __del__(self):
self.sock.close()
def wait_for_rsp(self, exp_len = 128):
# Wait for incoming data (timeout is 3 seconds)
s, _, _ = select.select([self.sock], [], [], 3.0)
if not s:
raise ReaderError("Timeout waiting for card response")
# Receive expected amount of bytes from osmocon
rsp = self.sock.recv(exp_len)
return rsp
def reset_card(self):
# Request FULL reset
req_msg = L1CTLMessageReset()
self.sock.send(req_msg.gen_msg())
# Wait for confirmation
rsp = self.wait_for_rsp()
rsp_msg = struct.unpack_from("!HB", rsp)
if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
raise ReaderError("Failed to reset Calypso PHY")
def connect(self):
self.reset_card()
def disconnect(self):
pass # Nothing to do really ...
def wait_for_card(self, timeout = None, newcardonly = False):
pass # Nothing to do really ...
def send_apdu_raw(self, pdu):
"""see LinkBase.send_apdu_raw"""
# Request FULL reset
req_msg = L1CTLMessageSIM(h2b(pdu))
self.sock.send(req_msg.gen_msg())
# Read message length first
rsp = self.wait_for_rsp(struct.calcsize("!H"))
msg_len = struct.unpack_from("!H", rsp)[0]
if msg_len < struct.calcsize("BBxx"):
raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
# Read the whole message then
rsp = self.sock.recv(msg_len)
# Verify L1CTL header
hdr = struct.unpack_from("BBxx", rsp)
if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
raise ReaderError("Unexpected L1CTL message received")
# Verify the payload length
offset = struct.calcsize("BBxx")
if len(rsp) <= offset:
raise ProtocolError("Empty response from SIM?!?")
# Omit L1CTL header
rsp = rsp[offset:]
# Unpack data and SW
data = rsp[:-2]
sw = rsp[-2:]
return b2h(data), b2h(sw)

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" pySim: Transport Link for 3GPP TS 27.007 compliant modems
"""
# Copyright (C) 2020 Vadim Yanitskiy <axilirator@gmail.com>
#
# 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 __future__ import absolute_import
import logging as log
import serial
import time
import re
from pySim.transport import LinkBase
from pySim.exceptions import *
# HACK: if somebody needs to debug this thing
# log.root.setLevel(log.DEBUG)
class ModemATCommandLink(LinkBase):
def __init__(self, device='/dev/ttyUSB0', baudrate=115200):
self._sl = serial.Serial(device, baudrate, timeout=5)
self._device = device
self._atr = None
# Trigger initial reset
self.reset_card()
def __del__(self):
self._sl.close()
def send_at_cmd(self, cmd):
# Convert from string to bytes, if needed
bcmd = cmd if type(cmd) is bytes else cmd.encode()
bcmd += b'\r'
# Send command to the modem
log.debug('Sending AT command: %s' % cmd)
try:
wlen = self._sl.write(bcmd)
assert(wlen == len(bcmd))
except:
raise ReaderError('Failed to send AT command: %s' % cmd)
# Give the modem some time...
time.sleep(0.3)
# Read the response
try:
# Skip characters sent back
self._sl.read(wlen)
# Read the rest
rsp = self._sl.read_all()
# Strip '\r\n'
rsp = rsp.strip()
# Split into a list
rsp = rsp.split(b'\r\n\r\n')
except:
raise ReaderError('Failed parse response to AT command: %s' % cmd)
log.debug('Got response from modem: %s' % rsp)
return rsp
def reset_card(self):
# Make sure that we can talk to the modem
if self.send_at_cmd('AT') != [b'OK']:
raise ReaderError('Failed to connect to modem')
# Reset the modem, just to be sure
if self.send_at_cmd('ATZ') != [b'OK']:
raise ReaderError('Failed to reset the modem')
# Make sure that generic SIM access is supported
if self.send_at_cmd('AT+CSIM=?') != [b'OK']:
raise ReaderError('The modem does not seem to support SIM access')
log.info('Modem at \'%s\' is ready!' % self._device)
def connect(self):
pass # Nothing to do really ...
def disconnect(self):
pass # Nothing to do really ...
def wait_for_card(self, timeout=None, newcardonly=False):
pass # Nothing to do really ...
def send_apdu_raw(self, pdu):
# Prepare the command as described in 8.17
cmd = 'AT+CSIM=%d,\"%s\"' % (len(pdu), pdu)
# Send AT+CSIM command to the modem
# TODO: also handle +CME ERROR: <err>
rsp = self.send_at_cmd(cmd)
if len(rsp) != 2 or rsp[-1] != b'OK':
raise ReaderError('APDU transfer failed: %s' % str(rsp))
rsp = rsp[0] # Get rid of b'OK'
# Make sure that the response has format: b'+CSIM: %d,\"%s\"'
try:
result = re.match(b'\+CSIM: (\d+),\"([0-9A-F]+)\"', rsp)
(rsp_pdu_len, rsp_pdu) = result.groups()
except:
raise ReaderError('Failed to parse response from modem: %s' % rsp)
# TODO: make sure we have at least SW
data = rsp_pdu[:-4].decode()
sw = rsp_pdu[-4:].decode()
return data, sw

View File

@@ -22,7 +22,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from smartcard.CardConnection import CardConnection
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
from smartcard.System import readers
@@ -53,22 +52,19 @@ class PcscSimLink(LinkBase):
def connect(self):
try:
# Explicitly select T=0 communication protocol
self._con.connect(CardConnection.T0_protocol)
except CardConnectionException:
raise ProtocolError()
self._con.connect()
except NoCardException:
raise NoCardError()
def get_atr(self):
return self._con.getATR()
def disconnect(self):
self._con.disconnect()
def reset_card(self):
self.disconnect()
self.connect()
self._con.disconnect()
try:
self._con.connect()
except NoCardException:
raise NoCardError()
return 1
def send_apdu_raw(self, pdu):

View File

@@ -46,7 +46,6 @@ class SerialSimLink(LinkBase):
)
self._rst_pin = rst
self._debug = debug
self._atr = None
def __del__(self):
self._sl.close()
@@ -92,9 +91,6 @@ class SerialSimLink(LinkBase):
def connect(self):
self.reset_card()
def get_atr(self):
return self._atr
def disconnect(self):
pass # Nothing to do really ...
@@ -106,7 +102,6 @@ class SerialSimLink(LinkBase):
raise ProtocolError()
def _reset_card(self):
self._atr = None
rst_meth_map = {
'rts': self._sl.setRTS,
'dtr': self._sl.setDTR,
@@ -138,31 +133,25 @@ class SerialSimLink(LinkBase):
return -1
t0 = ord(b)
self._dbg_print("T0: 0x%x" % t0)
self._atr = [0x3b, ord(b)]
for i in range(4):
if t0 & (0x10 << i):
b = self._rx_byte()
self._atr.append(ord(b))
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(self._rx_byte())))
for i in range(0, t0 & 0xf):
b = self._rx_byte()
self._atr.append(ord(b))
self._dbg_print("Historical = %x" % ord(b))
self._dbg_print("Historical = %x" % ord(self._rx_byte()))
while True:
x = self._rx_byte()
if not x:
break
self._atr.append(ord(x))
self._dbg_print("Extra: %x" % ord(x))
return 1
def _dbg_print(self, s):
if self._debug:
print(s)
print s
def _tx_byte(self, b):
self._sl.write(b)
@@ -213,7 +202,7 @@ class SerialSimLink(LinkBase):
self._tx_string(pdu[5:])
# Receive data (including SW !)
# length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
# length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ]
to_recv = data_len - len(pdu) + 5 + 2
data = ''

View File

@@ -1,248 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Various constants from ETSI TS 131 102
"""
#
# Copyright (C) 2020 Supreeth Herle <herlesupreeth@gmail.com>
#
# 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/>.
#
# Mapping between USIM Service Number and its description
EF_UST_map = {
1: 'Local Phone Book',
2: 'Fixed Dialling Numbers (FDN)',
3: 'Extension 2',
4: 'Service Dialling Numbers (SDN)',
5: 'Extension3',
6: 'Barred Dialling Numbers (BDN)',
7: 'Extension4',
8: 'Outgoing Call Information (OCI and OCT)',
9: 'Incoming Call Information (ICI and ICT)',
10: 'Short Message Storage (SMS)',
11: 'Short Message Status Reports (SMSR)',
12: 'Short Message Service Parameters (SMSP)',
13: 'Advice of Charge (AoC)',
14: 'Capability Configuration Parameters 2 (CCP2)',
15: 'Cell Broadcast Message Identifier',
16: 'Cell Broadcast Message Identifier Ranges',
17: 'Group Identifier Level 1',
18: 'Group Identifier Level 2',
19: 'Service Provider Name',
20: 'User controlled PLMN selector with Access Technology',
21: 'MSISDN',
22: 'Image (IMG)',
23: 'Support of Localised Service Areas (SoLSA)',
24: 'Enhanced Multi-Level Precedence and Pre-emption Service',
25: 'Automatic Answer for eMLPP',
26: 'RFU',
27: 'GSM Access',
28: 'Data download via SMS-PP',
29: 'Data download via SMS-CB',
30: 'Call Control by USIM',
31: 'MO-SMS Control by USIM',
32: 'RUN AT COMMAND command',
33: 'shall be set to 1',
34: 'Enabled Services Table',
35: 'APN Control List (ACL)',
36: 'Depersonalisation Control Keys',
37: 'Co-operative Network List',
38: 'GSM security context',
39: 'CPBCCH Information',
40: 'Investigation Scan',
41: 'MexE',
42: 'Operator controlled PLMN selector with Access Technology',
43: 'HPLMN selector with Access Technology',
44: 'Extension 5',
45: 'PLMN Network Name',
46: 'Operator PLMN List',
47: 'Mailbox Dialling Numbers',
48: 'Message Waiting Indication Status',
49: 'Call Forwarding Indication Status',
50: 'Reserved and shall be ignored',
51: 'Service Provider Display Information',
52: 'Multimedia Messaging Service (MMS)',
53: 'Extension 8',
54: 'Call control on GPRS by USIM',
55: 'MMS User Connectivity Parameters',
56: 'Network\'s indication of alerting in the MS (NIA)',
57: 'VGCS Group Identifier List (EFVGCS and EFVGCSS)',
58: 'VBS Group Identifier List (EFVBS and EFVBSS)',
59: 'Pseudonym',
60: 'User Controlled PLMN selector for I-WLAN access',
61: 'Operator Controlled PLMN selector for I-WLAN access',
62: 'User controlled WSID list',
63: 'Operator controlled WSID list',
64: 'VGCS security',
65: 'VBS security',
66: 'WLAN Reauthentication Identity',
67: 'Multimedia Messages Storage',
68: 'Generic Bootstrapping Architecture (GBA)',
69: 'MBMS security',
70: 'Data download via USSD and USSD application mode',
71: 'Equivalent HPLMN',
72: 'Additional TERMINAL PROFILE after UICC activation',
73: 'Equivalent HPLMN Presentation Indication',
74: 'Last RPLMN Selection Indication',
75: 'OMA BCAST Smart Card Profile',
76: 'GBA-based Local Key Establishment Mechanism',
77: 'Terminal Applications',
78: 'Service Provider Name Icon',
79: 'PLMN Network Name Icon',
80: 'Connectivity Parameters for USIM IP connections',
81: 'Home I-WLAN Specific Identifier List',
82: 'I-WLAN Equivalent HPLMN Presentation Indication',
83: 'I-WLAN HPLMN Priority Indication',
84: 'I-WLAN Last Registered PLMN',
85: 'EPS Mobility Management Information',
86: 'Allowed CSG Lists and corresponding indications',
87: 'Call control on EPS PDN connection by USIM',
88: 'HPLMN Direct Access',
89: 'eCall Data',
90: 'Operator CSG Lists and corresponding indications',
91: 'Support for SM-over-IP',
92: 'Support of CSG Display Control',
93: 'Communication Control for IMS by USIM',
94: 'Extended Terminal Applications',
95: 'Support of UICC access to IMS',
96: 'Non-Access Stratum configuration by USIM',
97: 'PWS configuration by USIM',
98: 'RFU',
99: 'URI support by UICC',
100: 'Extended EARFCN support',
101: 'ProSe',
102: 'USAT Application Pairing',
103: 'Media Type support',
104: 'IMS call disconnection cause',
105: 'URI support for MO SHORT MESSAGE CONTROL',
106: 'ePDG configuration Information support',
107: 'ePDG configuration Information configured',
108: 'ACDC support',
109: 'MCPTT',
110: 'ePDG configuration Information for Emergency Service support',
111: 'ePDG configuration Information for Emergency Service configured',
}
LOCI_STATUS_map = {
0: 'updated',
1: 'not updated',
2: 'plmn not allowed',
3: 'locatation area not allowed'
}
EF_USIM_ADF_map = {
'LI': '6F05',
'ARR': '6F06',
'IMSI': '6F07',
'Keys': '6F08',
'KeysPS': '6F09',
'DCK': '6F2C',
'HPPLMN': '6F31',
'CNL': '6F32',
'ACMmax': '6F37',
'UST': '6F38',
'ACM': '6F39',
'FDN': '6F3B',
'SMS': '6F3C',
'GID1': '6F3E',
'GID2': '6F3F',
'MSISDN': '6F40',
'PUCT': '6F41',
'SMSP': '6F42',
'SMSS': '6F42',
'CBMI': '6F45',
'SPN': '6F46',
'SMSR': '6F47',
'CBMID': '6F48',
'SDN': '6F49',
'EXT2': '6F4B',
'EXT3': '6F4C',
'BDN': '6F4D',
'EXT5': '6F4E',
'CCP2': '6F4F',
'CBMIR': '6F50',
'EXT4': '6F55',
'EST': '6F56',
'ACL': '6F57',
'CMI': '6F58',
'START-HFN': '6F5B',
'THRESHOLD': '6F5C',
'PLMNwAcT': '6F60',
'OPLMNwAcT': '6F61',
'HPLMNwAcT': '6F62',
'PSLOCI': '6F73',
'ACC': '6F78',
'FPLMN': '6F7B',
'LOCI': '6F7E',
'ICI': '6F80',
'OCI': '6F81',
'ICT': '6F82',
'OCT': '6F83',
'AD': '6FAD',
'VGCS': '6FB1',
'VGCSS': '6FB2',
'VBS': '6FB3',
'VBSS': '6FB4',
'eMLPP': '6FB5',
'AAeM': '6FB6',
'ECC': '6FB7',
'Hiddenkey': '6FC3',
'NETPAR': '6FC4',
'PNN': '6FC5',
'OPL': '6FC6',
'MBDN': '6FC7',
'EXT6': '6FC8',
'MBI': '6FC9',
'MWIS': '6FCA',
'CFIS': '6FCB',
'EXT7': '6FCC',
'SPDI': '6FCD',
'MMSN': '6FCE',
'EXT8': '6FCF',
'MMSICP': '6FD0',
'MMSUP': '6FD1',
'MMSUCP': '6FD2',
'NIA': '6FD3',
'VGCSCA': '6FD4',
'VBSCA': '6FD5',
'GBAP': '6FD6',
'MSK': '6FD7',
'MUK': '6FD8',
'EHPLMN': '6FD9',
'GBANL': '6FDA',
'EHPLMNPI': '6FDB',
'LRPLMNSI': '6FDC',
'NAFKCA': '6FDD',
'SPNI': '6FDE',
'PNNI': '6FDF',
'NCP-IP': '6FE2',
'EPSLOCI': '6FE3',
'EPSNSC': '6FE4',
'UFC': '6FE6',
'UICCIARI': '6FE7',
'NASCONFIG': '6FE8',
'PWC': '6FEC',
'FDNURI': '6FED',
'BDNURI': '6FEE',
'SDNURI': '6FEF',
'IWL': '6FF0',
'IPS': '6FF1',
'IPD': '6FF2',
'ePDGId': '6FF3',
'ePDGSelection': '6FF4',
'ePDGIdEm': '6FF5',
'ePDGSelectionEm': '6FF6',
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Various constants from ETSI TS 131 103 V14.2.0
"""
#
# Copyright (C) 2020 Supreeth Herle <herlesupreeth@gmail.com>
#
# 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/>.
#
# Mapping between ISIM Service Number and its description
EF_IST_map = {
1: 'P-CSCF address',
2: 'Generic Bootstrapping Architecture (GBA)',
3: 'HTTP Digest',
4: 'GBA-based Local Key Establishment Mechanism',
5: 'Support of P-CSCF discovery for IMS Local Break Out',
6: 'Short Message Storage (SMS)',
7: 'Short Message Status Reports (SMSR)',
8: 'Support for SM-over-IP including data download via SMS-PP as defined in TS 31.111 [31]',
9: 'Communication Control for IMS by ISIM',
10: 'Support of UICC access to IMS',
11: 'URI support by UICC',
12: 'Media Type support',
13: 'IMS call disconnection cause',
14: 'URI support for MO SHORT MESSAGE CONTROL',
15: 'MCPTT',
16: 'URI support for SMS-PP DOWNLOAD as defined in 3GPP TS 31.111 [31]',
17: 'From Preferred',
18: 'IMS configuration data',
19: 'XCAP Configuration Data'
}

View File

@@ -1,316 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Various constants from ETSI TS 151.011
"""
#
# Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com>
#
# 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/>.
#
MF_num = '3F00'
DF_num = {
'TELECOM': '7F10',
'GSM': '7F20',
'IS-41': '7F22',
'FP-CTS': '7F23',
'GRAPHICS': '5F50',
'IRIDIUM': '5F30',
'GLOBST': '5F31',
'ICO': '5F32',
'ACeS': '5F33',
'EIA/TIA-553': '5F40',
'CTS': '5F60',
'SOLSA': '5F70',
'MExE': '5F3C',
}
EF_num = {
# MF
'ICCID': '2FE2',
'ELP': '2F05',
'DIR': '2F00',
# DF_TELECOM
'ADN': '6F3A',
'FDN': '6F3B',
'SMS': '6F3C',
'CCP': '6F3D',
'MSISDN': '6F40',
'SMSP': '6F42',
'SMSS': '6F43',
'LND': '6F44',
'SMSR': '6F47',
'SDN': '6F49',
'EXT1': '6F4A',
'EXT2': '6F4B',
'EXT3': '6F4C',
'BDN': '6F4D',
'EXT4': '6F4E',
'CMI': '6F58',
'ECCP': '6F4F',
# DF_GRAPHICS
'IMG': '4F20',
# DF_SoLSA
'SAI': '4F30',
'SLL': '4F31',
# DF_MExE
'MExE-ST': '4F40',
'ORPK': '4F41',
'ARPK': '4F42',
'TPRPK': '4F43',
# DF_GSM
'LP': '6F05',
'IMSI': '6F07',
'Kc': '6F20',
'DCK': '6F2C',
'PLMNsel': '6F30',
'HPPLMN': '6F31',
'CNL': '6F32',
'ACMmax': '6F37',
'SST': '6F38',
'ACM': '6F39',
'GID1': '6F3E',
'GID2': '6F3F',
'PUCT': '6F41',
'CBMI': '6F45',
'SPN': '6F46',
'CBMID': '6F48',
'BCCH': '6F74',
'ACC': '6F78',
'FPLMN': '6F7B',
'LOCI': '6F7E',
'AD': '6FAD',
'PHASE': '6FAE',
'VGCS': '6FB1',
'VGCSS': '6FB2',
'VBS': '6FB3',
'VBSS': '6FB4',
'eMLPP': '6FB5',
'AAeM': '6FB6',
'ECC': '6FB7',
'CBMIR': '6F50',
'NIA': '6F51',
'KcGPRS': '6F52',
'LOCIGPRS': '6F53',
'SUME': '6F54',
'PLMNwAcT': '6F60',
'OPLMNwAcT': '6F61',
# Figure 8 names it HPLMNAcT, but in the text it's names it HPLMNwAcT
'HPLMNAcT': '6F62',
'HPLMNwAcT': '6F62',
'CPBCCH': '6F63',
'INVSCAN': '6F64',
'PNN': '6FC5',
'OPL': '6FC6',
'MBDN': '6FC7',
'EXT6': '6FC8',
'MBI': '6FC9',
'MWIS': '6FCA',
'CFIS': '6FCB',
'EXT7': '6FCC',
'SPDI': '6FCD',
'MMSN': '6FCE',
'EXT8': '6FCF',
'MMSICP': '6FD0',
'MMSUP': '6FD1',
'MMSUCP': '6FD2',
}
DF = {
'TELECOM': [MF_num, DF_num['TELECOM']],
'GSM': [MF_num, DF_num['GSM']],
'IS-41': [MF_num, DF_num['IS-41']],
'FP-CTS': [MF_num, DF_num['FP-CTS']],
'GRAPHICS': [MF_num, DF_num['GRAPHICS']],
'IRIDIUM': [MF_num, DF_num['IRIDIUM']],
'GLOBST': [MF_num, DF_num['GLOBST']],
'ICO': [MF_num, DF_num['ICO']],
'ACeS': [MF_num, DF_num['ACeS']],
'EIA/TIA-553': [MF_num, DF_num['EIA/TIA-553']],
'CTS': [MF_num, DF_num['CTS']],
'SoLSA': [MF_num, DF_num['SOLSA']],
'MExE': [MF_num, DF_num['MExE']],
}
EF = {
'ICCID': [MF_num, EF_num['ICCID']],
'ELP': [MF_num, EF_num['ELP']],
'DIR': [MF_num, EF_num['DIR']],
'ADN': DF['TELECOM']+[EF_num['ADN']],
'FDN': DF['TELECOM']+[EF_num['FDN']],
'SMS': DF['TELECOM']+[EF_num['SMS']],
'CCP': DF['TELECOM']+[EF_num['CCP']],
'MSISDN': DF['TELECOM']+[EF_num['MSISDN']],
'SMSP': DF['TELECOM']+[EF_num['SMSP']],
'SMSS': DF['TELECOM']+[EF_num['SMSS']],
'LND': DF['TELECOM']+[EF_num['LND']],
'SMSR': DF['TELECOM']+[EF_num['SMSR']],
'SDN': DF['TELECOM']+[EF_num['SDN']],
'EXT1': DF['TELECOM']+[EF_num['EXT1']],
'EXT2': DF['TELECOM']+[EF_num['EXT2']],
'EXT3': DF['TELECOM']+[EF_num['EXT3']],
'BDN': DF['TELECOM']+[EF_num['BDN']],
'EXT4': DF['TELECOM']+[EF_num['EXT4']],
'CMI': DF['TELECOM']+[EF_num['CMI']],
'ECCP': DF['TELECOM']+[EF_num['ECCP']],
'IMG': DF['GRAPHICS']+[EF_num['IMG']],
'SAI': DF['SoLSA']+[EF_num['SAI']],
'SLL': DF['SoLSA']+[EF_num['SLL']],
'MExE-ST': DF['MExE']+[EF_num['MExE-ST']],
'ORPK': DF['MExE']+[EF_num['ORPK']],
'ARPK': DF['MExE']+[EF_num['ARPK']],
'TPRPK': DF['MExE']+[EF_num['TPRPK']],
'LP': DF['GSM']+[EF_num['LP']],
'IMSI': DF['GSM']+[EF_num['IMSI']],
'Kc': DF['GSM']+[EF_num['Kc']],
'DCK': DF['GSM']+[EF_num['DCK']],
'PLMNsel': DF['GSM']+[EF_num['PLMNsel']],
'HPPLMN': DF['GSM']+[EF_num['HPPLMN']],
'CNL': DF['GSM']+[EF_num['CNL']],
'ACMmax': DF['GSM']+[EF_num['ACMmax']],
'SST': DF['GSM']+[EF_num['SST']],
'ACM': DF['GSM']+[EF_num['ACM']],
'GID1': DF['GSM']+[EF_num['GID1']],
'GID2': DF['GSM']+[EF_num['GID2']],
'PUCT': DF['GSM']+[EF_num['PUCT']],
'CBMI': DF['GSM']+[EF_num['CBMI']],
'SPN': DF['GSM']+[EF_num['SPN']],
'CBMID': DF['GSM']+[EF_num['CBMID']],
'BCCH': DF['GSM']+[EF_num['BCCH']],
'ACC': DF['GSM']+[EF_num['ACC']],
'FPLMN': DF['GSM']+[EF_num['FPLMN']],
'LOCI': DF['GSM']+[EF_num['LOCI']],
'AD': DF['GSM']+[EF_num['AD']],
'PHASE': DF['GSM']+[EF_num['PHASE']],
'VGCS': DF['GSM']+[EF_num['VGCS']],
'VGCSS': DF['GSM']+[EF_num['VGCSS']],
'VBS': DF['GSM']+[EF_num['VBS']],
'VBSS': DF['GSM']+[EF_num['VBSS']],
'eMLPP': DF['GSM']+[EF_num['eMLPP']],
'AAeM': DF['GSM']+[EF_num['AAeM']],
'ECC': DF['GSM']+[EF_num['ECC']],
'CBMIR': DF['GSM']+[EF_num['CBMIR']],
'NIA': DF['GSM']+[EF_num['NIA']],
'KcGPRS': DF['GSM']+[EF_num['KcGPRS']],
'LOCIGPRS': DF['GSM']+[EF_num['LOCIGPRS']],
'SUME': DF['GSM']+[EF_num['SUME']],
'PLMNwAcT': DF['GSM']+[EF_num['PLMNwAcT']],
'OPLMNwAcT': DF['GSM']+[EF_num['OPLMNwAcT']],
# Figure 8 names it HPLMNAcT, but in the text it's names it HPLMNwAcT
'HPLMNAcT': DF['GSM']+[EF_num['HPLMNAcT']],
'HPLMNwAcT': DF['GSM']+[EF_num['HPLMNAcT']],
'CPBCCH': DF['GSM']+[EF_num['CPBCCH']],
'INVSCAN': DF['GSM']+[EF_num['INVSCAN']],
'PNN': DF['GSM']+[EF_num['PNN']],
'OPL': DF['GSM']+[EF_num['OPL']],
'MBDN': DF['GSM']+[EF_num['MBDN']],
'EXT6': DF['GSM']+[EF_num['EXT6']],
'MBI': DF['GSM']+[EF_num['MBI']],
'MWIS': DF['GSM']+[EF_num['MWIS']],
'CFIS': DF['GSM']+[EF_num['CFIS']],
'EXT7': DF['GSM']+[EF_num['EXT7']],
'SPDI': DF['GSM']+[EF_num['SPDI']],
'MMSN': DF['GSM']+[EF_num['MMSN']],
'EXT8': DF['GSM']+[EF_num['EXT8']],
'MMSICP': DF['GSM']+[EF_num['MMSICP']],
'MMSUP': DF['GSM']+[EF_num['MMSUP']],
'MMSUCP': DF['GSM']+[EF_num['MMSUCP']],
}
# Mapping between SIM Service Number and its description
EF_SST_map = {
1: 'CHV1 disable function',
2: 'Abbreviated Dialling Numbers (ADN)',
3: 'Fixed Dialling Numbers (FDN)',
4: 'Short Message Storage (SMS)',
5: 'Advice of Charge (AoC)',
6: 'Capability Configuration Parameters (CCP)',
7: 'PLMN selector',
8: 'RFU',
9: 'MSISDN',
10: 'Extension1',
11: 'Extension2',
12: 'SMS Parameters',
13: 'Last Number Dialled (LND)',
14: 'Cell Broadcast Message Identifier',
15: 'Group Identifier Level 1',
16: 'Group Identifier Level 2',
17: 'Service Provider Name',
18: 'Service Dialling Numbers (SDN)',
19: 'Extension3',
20: 'RFU',
21: 'VGCS Group Identifier List (EFVGCS and EFVGCSS)',
22: 'VBS Group Identifier List (EFVBS and EFVBSS)',
23: 'enhanced Multi-Level Precedence and Pre-emption Service',
24: 'Automatic Answer for eMLPP',
25: 'Data download via SMS-CB',
26: 'Data download via SMS-PP',
27: 'Menu selection',
28: 'Call control',
29: 'Proactive SIM',
30: 'Cell Broadcast Message Identifier Ranges',
31: 'Barred Dialling Numbers (BDN)',
32: 'Extension4',
33: 'De-personalization Control Keys',
34: 'Co-operative Network List',
35: 'Short Message Status Reports',
36: 'Network\'s indication of alerting in the MS',
37: 'Mobile Originated Short Message control by SIM',
38: 'GPRS',
39: 'Image (IMG)',
40: 'SoLSA (Support of Local Service Area)',
41: 'USSD string data object supported in Call Control',
42: 'RUN AT COMMAND command',
43: 'User controlled PLMN Selector with Access Technology',
44: 'Operator controlled PLMN Selector with Access Technology',
45: 'HPLMN Selector with Access Technology',
46: 'CPBCCH Information',
47: 'Investigation Scan',
48: 'Extended Capability Configuration Parameters',
49: 'MExE',
50: 'Reserved and shall be ignored',
51: 'PLMN Network Name',
52: 'Operator PLMN List',
53: 'Mailbox Dialling Numbers',
54: 'Message Waiting Indication Status',
55: 'Call Forwarding Indication Status',
56: 'Service Provider Display Information',
57: 'Multimedia Messaging Service (MMS)',
58: 'Extension 8',
59: 'MMS User Connectivity Parameters',
}

View File

@@ -34,17 +34,6 @@ def h2i(s):
def i2h(s):
return ''.join(['%02x'%(x) for x in s])
def h2s(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
if int(x + y, 16) != 0xff])
def s2h(s):
return b2h(s)
# List of bytes to string
def i2s(s):
return ''.join([chr(x) for x in s])
def swap_nibbles(s):
return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
@@ -54,47 +43,25 @@ def rpad(s, l, c='f'):
def lpad(s, l, c='f'):
return c * (l - len(s)) + s
def half_round_up(n):
return (n + 1)//2
# IMSI encoded format:
# For IMSI 0123456789ABCDE:
#
# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
#
# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
#
# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
# encode itself.
#
# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
# even length IMSI only uses half of the last byte.
def enc_imsi(imsi):
"""Converts a string imsi into the value of the EF"""
l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
l = (len(imsi) + 1) // 2 # Required bytes
oe = len(imsi) & 1 # Odd (1) / Even (0)
ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
return ei
def dec_imsi(ef):
"""Converts an EF value to the imsi string representation"""
if len(ef) < 4:
return None
l = int(ef[0:2], 16) * 2 # Length of the IMSI string
l = l - 1 # Encoded length byte includes oe nibble
swapped = swap_nibbles(ef[2:]).rstrip('f')
if len(swapped) < 1:
return None
l = int(ef[0:2]) * 2 # Length of the IMSI string
swapped = swap_nibbles(ef[2:])
oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
if not oe:
# if even, only half of last byte was used
if oe:
l = l-1
if l != len(swapped) - 1:
if l+1 > len(swapped):
return None
imsi = swapped[1:]
imsi = swapped[1:l+2]
return imsi
def dec_iccid(ef):
@@ -104,468 +71,5 @@ def enc_iccid(iccid):
return swap_nibbles(rpad(iccid, 20))
def enc_plmn(mcc, mnc):
"""Converts integer MCC/MNC into 3 bytes for EF"""
if len(mnc) == 2:
mnc = "F%s" % mnc
return swap_nibbles("%s%s" % (mcc, mnc))
def dec_spn(ef):
byte1 = int(ef[0:2])
hplmn_disp = (byte1&0x01 == 0x01)
oplmn_disp = (byte1&0x02 == 0x02)
name = h2s(ef[2:])
return (name, hplmn_disp, oplmn_disp)
def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
byte1 = 0x00
if hplmn_disp: byte1 = byte1|0x01
if oplmn_disp: byte1 = byte1|0x02
return i2h([byte1])+s2h(name)
def hexstr_to_fivebytearr(s):
return [s[i:i+10] for i in range(0, len(s), 10) ]
def hexstr_to_threebytearr(s):
return [s[i:i+6] for i in range(0, len(s), 6) ]
# Accepts hex string representing three bytes
def dec_mcc_from_plmn(plmn):
ia = h2i(plmn)
digit1 = ia[0] & 0x0F # 1st byte, LSB
digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
digit3 = ia[1] & 0x0F # 2nd byte, LSB
if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
return 0xFFF # 4095
return derive_mcc(digit1, digit2, digit3)
def dec_mnc_from_plmn(plmn):
ia = h2i(plmn)
digit1 = ia[2] & 0x0F # 3rd byte, LSB
digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
return 0xFFF # 4095
return derive_mnc(digit1, digit2, digit3)
def dec_act(twohexbytes):
act_list = [
{'bit': 15, 'name': "UTRAN"},
{'bit': 14, 'name': "E-UTRAN"},
{'bit': 7, 'name': "GSM"},
{'bit': 6, 'name': "GSM COMPACT"},
{'bit': 5, 'name': "cdma2000 HRPD"},
{'bit': 4, 'name': "cdma2000 1xRTT"},
]
ia = h2i(twohexbytes)
u16t = (ia[0] << 8)|ia[1]
sel = []
for a in act_list:
if u16t & (1 << a['bit']):
sel.append(a['name'])
return sel
def dec_xplmn_w_act(fivehexbytes):
res = {'mcc': 0, 'mnc': 0, 'act': []}
plmn_chars = 6
act_chars = 4
plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
res['mcc'] = dec_mcc_from_plmn(plmn_str)
res['mnc'] = dec_mnc_from_plmn(plmn_str)
res['act'] = dec_act(act_str)
return res
def format_xplmn_w_act(hexstr):
s = ""
for rec_data in hexstr_to_fivebytearr(hexstr):
rec_info = dec_xplmn_w_act(rec_data)
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
rec_str = "unused"
else:
rec_str = "MCC: %03d MNC: %03d 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):
res = {'mcc': 0, 'mnc': 0, 'act': []}
plmn_chars = 6
plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
res['mcc'] = dec_mcc_from_plmn(plmn_str)
res['mnc'] = dec_mnc_from_plmn(plmn_str)
return res
def format_xplmn(hexstr):
s = ""
for rec_data in hexstr_to_threebytearr(hexstr):
rec_info = dec_xplmn(rec_data)
if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
rec_str = "unused"
else:
rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
s += "\t%s # %s\n" % (rec_data, rec_str)
return s
def derive_milenage_opc(ki_hex, op_hex):
"""
Run the milenage algorithm to calculate OPC from Ki and OP
"""
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from pySim.utils import b2h
# We pass in hex string and now need to work on bytes
aes = AES.new(h2b(ki_hex))
opc_bytes = aes.encrypt(h2b(op_hex))
return b2h(strxor(opc_bytes, h2b(op_hex)))
def calculate_luhn(cc):
"""
Calculate Luhn checksum used in e.g. ICCID and IMEI
"""
num = map(int, str(cc))
check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
return 0 if check_digit == 10 else check_digit
def mcc_from_imsi(imsi):
"""
Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
"""
if imsi == None:
return None
if len(imsi) > 3:
return imsi[:3]
else:
return None
def mnc_from_imsi(imsi, long=False):
"""
Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
"""
if imsi == None:
return None
if len(imsi) > 3:
if long:
return imsi[3:6]
else:
return imsi[3:5]
else:
return None
def derive_mcc(digit1, digit2, digit3):
"""
Derive decimal representation of the MCC (Mobile Country Code)
from three given digits.
"""
mcc = 0
if digit1 != 0x0f:
mcc += digit1 * 100
if digit2 != 0x0f:
mcc += digit2 * 10
if digit3 != 0x0f:
mcc += digit3
return mcc
def derive_mnc(digit1, digit2, digit3=0x0f):
"""
Derive decimal representation of the MNC (Mobile Network Code)
from two or (optionally) three given digits.
"""
mnc = 0
# 3-rd digit is optional for the MNC. If present
# the algorythm is the same as for the MCC.
if digit3 != 0x0f:
return derive_mcc(digit1, digit2, digit3)
if digit1 != 0x0f:
mnc += digit1 * 10
if digit2 != 0x0f:
mnc += digit2
return mnc
def dec_msisdn(ef_msisdn):
"""
Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
"""
# Convert from str to (kind of) 'bytes'
ef_msisdn = h2b(ef_msisdn)
# Make sure mandatory fields are present
if len(ef_msisdn) < 14:
raise ValueError("EF.MSISDN is too short")
# Skip optional Alpha Identifier
xlen = len(ef_msisdn) - 14
msisdn_lhv = ef_msisdn[xlen:]
# Parse the length (in bytes) of the BCD encoded number
bcd_len = ord(msisdn_lhv[0])
# BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
if bcd_len == 0xff:
return None
elif bcd_len > 11 or bcd_len < 1:
raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
# Parse ToN / NPI
ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
npi = ord(msisdn_lhv[1]) & 0x0f
bcd_len -= 1
# No MSISDN?
if not bcd_len:
return (npi, ton, None)
msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
# International number 10.5.118/3GPP TS 24.008
if ton == 0x01:
msisdn = '+' + msisdn
return (npi, ton, msisdn)
def enc_msisdn(msisdn, npi=0x01, ton=0x03):
"""
Encode MSISDN as LHV so it can be stored to EF.MSISDN.
See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
Default NPI / ToN values:
- NPI: ISDN / telephony numbering plan (E.164 / E.163),
- ToN: network specific or international number (if starts with '+').
"""
# Leading '+' indicates International Number
if msisdn[0] == '+':
msisdn = msisdn[1:]
ton = 0x01
# Append 'f' padding if number of digits is odd
if len(msisdn) % 2 > 0:
msisdn += 'f'
# BCD length also includes NPI/ToN header
bcd_len = len(msisdn) // 2 + 1
npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
def dec_st(st, table="sim"):
"""
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])
parses first TLV format record in a list of bytelist
returns a 3-Tuple: Tag, Length, Value
Value is a list of bytes
parsing of length is ETSI'style 101.220
'''
Tag = bytelist[0]
if bytelist[1] == 0xFF:
Len = bytelist[2]*256 + bytelist[3]
Val = bytelist[4:4+Len]
else:
Len = bytelist[1]
Val = bytelist[2:2+Len]
return (Tag, Len, Val)
def TLV_parser(bytelist):
'''
TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
loops on the input list of bytes with the "first_TLV_parser()" function
returns a list of 3-Tuples
'''
ret = []
while len(bytelist) > 0:
T, L, V = first_TLV_parser(bytelist)
if T == 0xFF:
# padding bytes
break
ret.append( (T, L, V) )
# need to manage length of L
if L > 0xFE:
bytelist = bytelist[ L+4 : ]
else:
bytelist = bytelist[ L+2 : ]
return ret
def dec_epdgid(hexstr):
"""
Decode ePDG Id to get EF.ePDGId or EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
"""
# Convert from hex str to int bytes list
epdgid_bytes = h2i(hexstr)
s = ""
# Get list of tuples containing parsed TLVs
tlvs = TLV_parser(epdgid_bytes)
for tlv in tlvs:
# tlv = (T, L, [V])
# T = Tag
# L = Length
# [V] = List of value
# Invalid Tag value scenario
if tlv[0] != 0x80:
continue
# Empty field - Zero length
if tlv[1] == 0:
continue
# First byte in the value has the address type
addr_type = tlv[2][0]
# TODO: Support parsing of IPv4 and IPv6
if addr_type == 0x00: #FQDN
# Skip address tye byte i.e. first byte in value list
content = tlv[2][1:]
s += "\t%s # %s\n" % (i2h(content), i2s(content))
return s
def enc_epdgid(epdg_addr, addr_type='00'):
"""
Encode ePDG Id so it can be stored to EF.ePDGId or EF.ePDGIdEm.
See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
Default values:
- addr_type: 00 - FQDN format of ePDG Address
"""
s = ""
# TODO: Encoding of IPv4 and IPv6 address
if addr_type == '00':
hex_str = s2h(epdg_addr)
s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
return s
def sanitize_pin_adm(opts):
"""
The ADM pin can be supplied either in its hexadecimal form or as
ascii string. This function checks the supplied opts parameter and
returns the pin_adm as hex encoded string, regardles in which form
it was originally supplied by the user
"""
pin_adm = None
if opts.pin_adm is not None:
if len(opts.pin_adm) <= 8:
pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
pin_adm = rpad(pin_adm, 16)
else:
raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
if opts.pin_adm_hex is not None:
if len(opts.pin_adm_hex) == 16:
pin_adm = opts.pin_adm_hex
# Ensure that it's hex-encoded
try:
try_encode = h2b(pin_adm)
except ValueError:
raise ValueError("PIN-ADM needs to be hex encoded using this option")
else:
raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
return pin_adm
def init_reader(opts):
"""
Init card reader driver
"""
if opts.pcsc_dev is not None:
print("Using PC/SC reader interface")
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
elif opts.osmocon_sock is not None:
print("Using Calypso-based (OsmocomBB) reader interface")
from pySim.transport.calypso import CalypsoSimLink
sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
elif opts.modem_dev is not None:
print("Using modem for Generic SIM Access (3GPP TS 27.007)")
from pySim.transport.modem_atcmd import ModemATCommandLink
sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
else: # Serial reader is default
print("Using serial reader interface")
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
return sl
"""Converts integer MCC/MNC into 6 bytes for EF"""
return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))

View File

@@ -1,76 +0,0 @@
#!/usr/bin/pyton
import unittest
import utils
class DecTestCase(unittest.TestCase):
def testSplitHexStringToListOf5ByteEntries(self):
input_str = "ffffff0003ffffff0002ffffff0001"
expected = [
"ffffff0003",
"ffffff0002",
"ffffff0001",
]
self.assertEqual(utils.hexstr_to_fivebytearr(input_str), expected)
def testDecMCCfromPLMN(self):
self.assertEqual(utils.dec_mcc_from_plmn("92f501"), 295)
def testDecMCCfromPLMN_unused(self):
self.assertEqual(utils.dec_mcc_from_plmn("ff0f00"), 4095)
def testDecMNCfromPLMN_twoDigitMNC(self):
self.assertEqual(utils.dec_mnc_from_plmn("92f501"), 10)
def testDecMNCfromPLMN_threeDigitMNC(self):
self.assertEqual(utils.dec_mnc_from_plmn("031263"), 361)
def testDecMNCfromPLMN_unused(self):
self.assertEqual(utils.dec_mnc_from_plmn("00f0ff"), 4095)
def testDecAct_noneSet(self):
self.assertEqual(utils.dec_act("0000"), [])
def testDecAct_onlyUtran(self):
self.assertEqual(utils.dec_act("8000"), ["UTRAN"])
def testDecAct_onlyEUtran(self):
self.assertEqual(utils.dec_act("4000"), ["E-UTRAN"])
def testDecAct_onlyGsm(self):
self.assertEqual(utils.dec_act("0080"), ["GSM"])
def testDecAct_onlyGsmCompact(self):
self.assertEqual(utils.dec_act("0040"), ["GSM COMPACT"])
def testDecAct_onlyCdma2000HRPD(self):
self.assertEqual(utils.dec_act("0020"), ["cdma2000 HRPD"])
def testDecAct_onlyCdma20001xRTT(self):
self.assertEqual(utils.dec_act("0010"), ["cdma2000 1xRTT"])
def testDecAct_allSet(self):
self.assertEqual(utils.dec_act("ffff"), ["UTRAN", "E-UTRAN", "GSM", "GSM COMPACT", "cdma2000 HRPD", "cdma2000 1xRTT"])
def testDecxPlmn_w_act(self):
expected = {'mcc': 295, 'mnc': 10, 'act': ["UTRAN"]}
self.assertEqual(utils.dec_xplmn_w_act("92f5018000"), expected)
def testFormatxPlmn_w_act(self):
input_str = "92f501800092f5508000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000ffffff0000"
expected = '''92f5018000 # MCC: 295 MNC: 10 AcT: UTRAN
92f5508000 # MCC: 295 MNC: 5 AcT: UTRAN
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
'''
self.assertEqual(utils.format_xplmn_w_act(input_str), expected)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000111
ADM_HEX=CAE743DB9C5B5A58

View File

@@ -1,119 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: Fairwaves-SIM
ICCID: 8988219000000117833
IMSI: 001010000000111
GID1: ffffffffffffffff
GID2: ffffffffffffffff
SMSP: e1ffffffffffffffffffffffff0581005155f5ffffffffffff000000ffffffffffffffffffffffffffff
SPN: Fairwaves
Display HPLMN: False
Display OPLMN: False
PLMNsel: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ACC: 0008
MSISDN: Not available
AD: 00000002
SIM Service Table: ff3cc3ff030fff0f000fff03f0c0
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 41 - USSD string data object supported in Call Control
Service 42 - RUN AT COMMAND command
Service 43 - User controlled PLMN Selector with Access Technology
Service 44 - Operator controlled PLMN Selector with Access Technology
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 01ea1ffc21360480010000
Service 1 - Local Phone Book
Service 10 - Short Message Storage (SMS)
Service 12 - Short Message Service Parameters (SMSP)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 31 - MO-SMS Control by USIM
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 38 - GSM security context
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 51 - Service Provider Display Information
Service 64 - VGCS security
Service 65 - VBS security
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000102
ADM_HEX=15E31383624FDC8A

View File

@@ -1,135 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: Wavemobile-SIM
ICCID: 89445310150011013678
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 6a82.
GID2: Can't read file -- SW match failed! Expected 9000 and got 6a82.
SMSP: e1ffffffffffffffffffffffff0581005155f5ffffffffffff000000ffffffffffffffffffffffffffff
SPN: wavemobile
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 6a82.
ACC: abce
MSISDN: Not available
AD: 00ffff02
SIM Service Table: ff33ff0f3c00ff0f000cf0c0f0030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 9eff1b3c37fe5900000000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 11 - Short Message Status Reports (SMSR)
Service 12 - Short Message Service Parameters (SMSP)
Service 13 - Advice of Charge (AoC)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 37 - Co-operative Network List
Service 38 - GSM security context
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 44 - Extension 5
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 47 - Mailbox Dialling Numbers
Service 48 - Message Waiting Indication Status
Service 49 - Call Forwarding Indication Status
Service 52 - Multimedia Messaging Service (MMS)
Service 53 - Extension 8
Service 55 - MMS User Connectivity Parameters
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -1,6 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102

View File

@@ -1,57 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: fakemagicsim
Can't read AIDs from SIM -- SW match failed! Expected 9000 and got 9404.
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 9404.
GID2: Can't read file -- SW match failed! Expected 9000 and got 9404.
SMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Magic
Display HPLMN: True
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
OPLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
ACC: ffff
MSISDN: Not available
AD: 000000
SIM Service Table: ff3fff0f0300f003000c
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 55 - Call Forwarding Indication Status
Service 56 - Service Provider Display Information
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Done !

View File

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=AABBCCDDEEFFAABBCCDDEEFFAABBCCDD
OPC=12345678901234567890123456789012
IMSI=001010000000102
ADM=72273953

View File

@@ -1,169 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmoISIM-SJA2
ICCID: 8988211900000000004
IMSI: 001010000000102
GID1: ffffffffffffffffffff
GID2: ffffffffffffffffffff
SMSP: ffffffffffffffffffffffffffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Not available
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ACC: 0001
MSISDN (NPI=1 ToN=1): +1234
AD: 00000002
SIM Service Table: ff33ffff3f003f0f300cf0c3f00000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
EHPLMN:
00f110 # MCC: 001 MNC: 001
ffffff # unused
ffffff # unused
ffffff # unused
USIM Service Table: beff9f9de73e0408400170730000002e00000000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 6 - Barred Dialling Numbers (BDN)
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 11 - Short Message Status Reports (SMSR)
Service 12 - Short Message Service Parameters (SMSP)
Service 13 - Advice of Charge (AoC)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 16 - Cell Broadcast Message Identifier Ranges
Service 17 - Group Identifier Level 1
Service 18 - Group Identifier Level 2
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 24 - Enhanced Multi-Level Precedence and Pre-emption Service
Service 25 - Automatic Answer for eMLPP
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 38 - GSM security context
Service 39 - CPBCCH Information
Service 40 - Investigation Scan
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 44 - Extension 5
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 51 - Service Provider Display Information
Service 60 - User Controlled PLMN selector for I-WLAN access
Service 71 - Equivalent HPLMN
Service 73 - Equivalent HPLMN Presentation Indication
Service 85 - EPS Mobility Management Information
Service 86 - Allowed CSG Lists and corresponding indications
Service 87 - Call control on EPS PDN connection by USIM
Service 89 - eCall Data
Service 90 - Operator CSG Lists and corresponding indications
Service 93 - Communication Control for IMS by USIM
Service 94 - Extended Terminal Applications
Service 95 - Support of UICC access to IMS
ePDGId:
Not available
ISIM Service Table: 190200
Service 1 - P-CSCF address
Service 4 - GBA-based Local Key Establishment Mechanism
Service 5 - Support of P-CSCF discovery for IMS Local Break Out
Service 10 - Support of UICC access to IMS
Done !

View File

@@ -1,8 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
MSISDN=+77776336143
ADM=55538407

View File

@@ -1,143 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmoUSIM-SJS1
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: ffffffffffffffffffff
GID2: ffffffffffffffffffff
SMSP: ffffffffffffffffffffffffffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Magic
Display HPLMN: True
Display OPLMN: True
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
OPLMNwAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
HPLMNAcT:
00f110ffff # MCC: 001 MNC: 001 AcT: UTRAN, E-UTRAN, GSM, GSM COMPACT, cdma2000 HRPD, cdma2000 1xRTT
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ffffff0000 # unused
ACC: 0008
MSISDN (NPI=1 ToN=1): +77776336143
AD: 00000002
SIM Service Table: ff3fffff3f003f1ff00c00c0f00000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 29 - Proactive SIM
Service 30 - Cell Broadcast Message Identifier Ranges
Service 31 - Barred Dialling Numbers (BDN)
Service 32 - Extension4
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 37 - Mobile Originated Short Message control by SIM
Service 38 - GPRS
Service 49 - MExE
Service 50 - Reserved and shall be ignored
Service 51 - PLMN Network Name
Service 52 - Operator PLMN List
Service 53 - Mailbox Dialling Numbers
Service 54 - Message Waiting Indication Status
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Service 59 - MMS User Connectivity Parameters
USIM Service Table: 9e6b1dfc67f6580000
Service 2 - Fixed Dialling Numbers (FDN)
Service 3 - Extension 2
Service 4 - Service Dialling Numbers (SDN)
Service 5 - Extension3
Service 8 - Outgoing Call Information (OCI and OCT)
Service 9 - Incoming Call Information (ICI and ICT)
Service 10 - Short Message Storage (SMS)
Service 12 - Short Message Service Parameters (SMSP)
Service 14 - Capability Configuration Parameters 2 (CCP2)
Service 15 - Cell Broadcast Message Identifier
Service 17 - Group Identifier Level 1
Service 19 - Service Provider Name
Service 20 - User controlled PLMN selector with Access Technology
Service 21 - MSISDN
Service 27 - GSM Access
Service 28 - Data download via SMS-PP
Service 29 - Data download via SMS-CB
Service 30 - Call Control by USIM
Service 31 - MO-SMS Control by USIM
Service 32 - RUN AT COMMAND command
Service 33 - shall be set to 1
Service 34 - Enabled Services Table
Service 35 - APN Control List (ACL)
Service 38 - GSM security context
Service 39 - CPBCCH Information
Service 42 - Operator controlled PLMN selector with Access Technology
Service 43 - HPLMN selector with Access Technology
Service 45 - PLMN Network Name
Service 46 - Operator PLMN List
Service 47 - Mailbox Dialling Numbers
Service 48 - Message Waiting Indication Status
Service 52 - Multimedia Messaging Service (MMS)
Service 53 - Extension 8
Service 55 - MMS User Connectivity Parameters
ePDGId: Can't read file -- SW match failed! Expected 9000 and got 6a82.
Done !

View File

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
ADM=DDDDDDDD

View File

@@ -1,55 +0,0 @@
Using PC/SC reader interface
Reading ...
Autodetected card type: sysmosim-gr1
Can't read AIDs from SIM -- SW match failed! Expected 9000 and got 9404.
ICCID: 1122334455667788990
IMSI: 001010000000102
GID1: Can't read file -- SW match failed! Expected 9000 and got 9404.
GID2: Can't read file -- SW match failed! Expected 9000 and got 9404.
SMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff0581005155f5ffffffffffff000000
SPN: Not available
Display HPLMN: False
Display OPLMN: False
PLMNsel: 00f110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
PLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
OPLMNwAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
HPLMNAcT: Can't read file -- SW match failed! Expected 9000 and got 9404.
ACC: 0008
MSISDN: Not available
AD: 000000
SIM Service Table: ff3fff0f0f0000030000
Service 1 - CHV1 disable function
Service 2 - Abbreviated Dialling Numbers (ADN)
Service 3 - Fixed Dialling Numbers (FDN)
Service 4 - Short Message Storage (SMS)
Service 5 - Advice of Charge (AoC)
Service 6 - Capability Configuration Parameters (CCP)
Service 7 - PLMN selector
Service 8 - RFU
Service 9 - MSISDN
Service 10 - Extension1
Service 11 - Extension2
Service 12 - SMS Parameters
Service 13 - Last Number Dialled (LND)
Service 14 - Cell Broadcast Message Identifier
Service 17 - Service Provider Name
Service 18 - Service Dialling Numbers (SDN)
Service 19 - Extension3
Service 20 - RFU
Service 21 - VGCS Group Identifier List (EFVGCS and EFVGCSS)
Service 22 - VBS Group Identifier List (EFVBS and EFVBSS)
Service 23 - enhanced Multi-Level Precedence and Pre-emption Service
Service 24 - Automatic Answer for eMLPP
Service 25 - Data download via SMS-CB
Service 26 - Data download via SMS-PP
Service 27 - Menu selection
Service 28 - Call control
Service 33 - De-personalization Control Keys
Service 34 - Co-operative Network List
Service 35 - Short Message Status Reports
Service 36 - Network's indication of alerting in the MS
Service 57 - Multimedia Messaging Service (MMS)
Service 58 - Extension 8
Done !

View File

@@ -1,13 +0,0 @@
class ScriptFormat():
def parse_process(self, text, stmt_cb, stmt_cb_kwargs={}):
p = self.parse_xform(text)
#print(p.pretty())
for stmt in p.children:
stmt_cb(stmt, **stmt_cb_kwargs)
def parse_process_file(self, fname, stmt_cb, stmt_cb_kwargs={}):
f = open(fname, "r")
text = f.read()
return self.parse_process(text, stmt_cb, stmt_cb_kwargs)

View File

@@ -1,5 +0,0 @@
MCC=001
MNC=01
IMSI=001010000000102
ADM_HEX=0123456789ABCDEF

View File

@@ -1,6 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102

View File

@@ -1,229 +0,0 @@
#!/bin/bash
# Utility to verify the functionality of pysim-prog.py
#
# (C) 2018 by Sysmocom s.f.m.c. GmbH
# All Rights Reserved
#
# Author: Philipp Maier
#
# 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/>.
PYSIM_PROG=../pySim-prog.py
PYSIM_READ=../pySim-read.py
TEMPFILE=temp.tmp
set -e
echo "pysim-test - a test program to test pysim-prog.py"
echo "================================================="
# Generate a list of the cards we expect to see by checking which .ok files
# are present
function gen_card_list {
N_CARDS=0
echo "Expecting to see the following cards:"
for I in *.data ; do
CARD_NAMES[$N_CARDS]=${I%.*}
CARD_SEEN[$N_CARDS]=0
N_CARDS=$((N_CARDS+1))
done
for I in $(seq 0 $((N_CARDS-1))); do
echo ${CARD_NAMES[$I]}
done
}
# Increment counter in card list for a specified card name (type)
function inc_card_list {
CARD_NAME=$1
for I in $(seq 0 $((N_CARDS-1))); do
if [ $CARD_NAME = ${CARD_NAMES[$I]} ]; then
CARD_SEEN[$I]=$((${CARD_NAMES[$I]}+1))
fi
done
}
# Check the card list, each card must be seen exactly one times
function check_card_list {
for I in $(seq 0 $((N_CARDS-1))); do
if [ ${CARD_SEEN[$I]} -ne 1 ]; then
echo "Error: Card ${CARD_NAMES[$I]} seen ${CARD_SEEN[$I]} times!"
exit 1
fi
done
echo "All cards seen -- everything ok!"
}
# Verify the contents of a card by reading them and then diffing against the
# previously created .ok file
function check_card {
TERMINAL=$1
CARD_NAME=$2
echo "Verifying card ..."
stat ./$CARD_NAME.ok > /dev/null
python $PYSIM_READ -p $TERMINAL > $TEMPFILE
set +e
CARD_DIFF=$(diff $TEMPFILE ./$CARD_NAME.ok)
set -e
if [ "$CARD_DIFF" != "" ]; then
echo "Card contents do not match the test data:"
echo "Expected: $CARD_NAME.ok"
echo "------------8<------------"
cat "$CARD_NAME.ok"
echo "------------8<------------"
echo "Got:"
echo "------------8<------------"
cat $TEMPFILE
echo "------------8<------------"
rm *.tmp
exit 1
fi
inc_card_list $CARD_NAME
echo "Card contents match the test data -- success!"
rm $TEMPFILE
}
# Read out the card using pysim-read and store the result as .ok file. This
# data will be used later in order to verify the results of our write tests.
function gen_ok_file {
TERMINAL=$1
CARD_NAME=$2
python $PYSIM_READ -p $TERMINAL > "$CARD_NAME.ok"
echo "Generated file: $CARD_NAME.ok"
echo "------------8<------------"
cat "$CARD_NAME.ok"
echo "------------8<------------"
}
# Find out the type (card name) of the card that is installed in the specified
# reader
function probe_card {
TERMINAL=$1
RESULT=$(timeout 5 $PYSIM_PROG -p $TERMINAL -T | cut -d ":" -f 2 | tail -n 1 | xargs)
echo $RESULT
}
# Read out all cards and store the results as .ok files
function gen_ok_files {
echo "== OK FILE GENERATION =="
for I in $(seq 0 $((N_TERMINALS-1))); do
echo "Probing card in terminal #$I"
CARD_NAME=$(probe_card $I)
if [ -z "$CARD_NAME" ]; then
echo "Error: Unresponsive card!"
exit 1
fi
echo "Card is of type: $CARD_NAME"
gen_ok_file $I $CARD_NAME
done
}
# Execute tests. Each card is programmed and the contents are checked
# afterwards.
function run_test {
for I in $(seq 0 $((N_TERMINALS-1))); do
echo "== EXECUTING TEST =="
echo "Probing card in terminal #$I"
CARD_NAME=$(probe_card $I)
if [ -z "$CARD_NAME" ]; then
echo "Error: Unresponsive card!"
exit 1
fi
echo "Card is of type: $CARD_NAME"
# Make sure some default data is set
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000001
MSISDN=6766266
ADM=00000000
ADM_HEX=""
ADM_OPT="-a"
source "$CARD_NAME.data"
if [ -n "$ADM_HEX" ]; then
ADM_OPT="-A"
ADM=$ADM_HEX
fi
python $PYSIM_PROG -p $I -t $CARD_NAME -o $OPC -k $KI -x $MCC -y $MNC -i $IMSI -s $ICCID --msisdn $MSISDN $ADM_OPT $ADM
check_card $I $CARD_NAME
echo ""
done
}
function usage {
echo "Options:"
echo "-n: number of card terminals"
echo "-o: generate .ok files"
}
# Make sure that the pathes to the python scripts always work, regardless from
# where the script is called.
CURDIR=$PWD
SCRIPTDIR=$(dirname $0)
cd $SCRIPTDIR
PYSIM_PROG=$(realpath $PYSIM_PROG)
PYSIM_READ=$(realpath $PYSIM_READ)
cd $CURDIR
OPT_N_TERMINALS=0
OPT_GEN_OK_FILES=0
while getopts ":hon:" OPT; do
case $OPT in
h)
usage
exit 0
;;
o)
OPT_GEN_OK_FILES=1
;;
n)
OPT_N_TERMINALS=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
N_TERMINALS=$OPT_N_TERMINALS
# Generate a list of available cards, if no explicit reader number is given
# then the number of cards will be used as reader number.
gen_card_list
if [ $N_TERMINALS -eq 0 ]; then
N_TERMINALS=$N_CARDS
fi
echo "Number of card terminals installed: $N_TERMINALS"
echo ""
if [ $OPT_GEN_OK_FILES -eq 1 ]; then
gen_ok_files
exit 0
else
run_test
check_card_list
exit 0
fi

View File

@@ -1,8 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
MSISDN=+77776336143
ADM=12345678

View File

@@ -1,7 +0,0 @@
MCC=001
MNC=01
ICCID=1122334455667788990
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
IMSI=001010000000102
ADM=DDDDDDDD