Compare commits
185 Commits
zecke/hack
...
sysmocom/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46bc6d25d6 | ||
|
|
1e42420e57 | ||
|
|
ca6739458e | ||
|
|
3c0bd7a41e | ||
|
|
b1634db0b3 | ||
|
|
d6a5ec51a8 | ||
|
|
e61170c0eb | ||
|
|
0e9f93fdd6 | ||
|
|
0dc8f69217 | ||
|
|
29ca8049d6 | ||
|
|
eb06b45d0e | ||
|
|
d58c632277 | ||
|
|
5c2cc66de5 | ||
|
|
cd3d6268a6 | ||
|
|
30eb8ca6aa | ||
|
|
32daaf5470 | ||
|
|
e8536c04bc | ||
|
|
7f9f64ac2a | ||
|
|
7f1d3c496f | ||
|
|
ff84c23839 | ||
|
|
b689754b49 | ||
|
|
fc83e43637 | ||
|
|
d572edef1e | ||
|
|
7d77d2d5d0 | ||
|
|
9837055f4f | ||
|
|
32f0d415af | ||
|
|
ee15c77185 | ||
|
|
df33037e42 | ||
|
|
8b72c27270 | ||
|
|
9641299f9a | ||
|
|
69e5d27e3c | ||
|
|
d3b13d0c85 | ||
|
|
0c4d82d84a | ||
|
|
3e6f16d8f6 | ||
|
|
f9f3e5e0c1 | ||
|
|
3bf43639ce | ||
|
|
475dcaa4a1 | ||
|
|
e26331ef72 | ||
|
|
441c4a768f | ||
|
|
bf5d602588 | ||
|
|
52ef675c31 | ||
|
|
6d66af653f | ||
|
|
d1fb6fc359 | ||
|
|
a850a47981 | ||
|
|
1757b263bf | ||
|
|
140844052a | ||
|
|
9efd8ef812 | ||
|
|
846cefb0cd | ||
|
|
ebe6dbaef5 | ||
|
|
e573ccb53c | ||
|
|
ad10d66baf | ||
|
|
d21349a610 | ||
|
|
c7f2f7413b | ||
|
|
98a6927b27 | ||
|
|
f9762dc98f | ||
|
|
3566b8eb55 | ||
|
|
4c306ab200 | ||
|
|
e4e98316a8 | ||
|
|
0e90e6c3c6 | ||
|
|
ab46d625fc | ||
|
|
d950786525 | ||
|
|
b3e11ea196 | ||
|
|
9664b2e7fc | ||
|
|
edf873d04a | ||
|
|
fa617ac20d | ||
|
|
41c22176e4 | ||
|
|
7ba2428de5 | ||
|
|
99affe1529 | ||
|
|
4133080ced | ||
|
|
5a541016fb | ||
|
|
4b1c763395 | ||
|
|
d24f163513 | ||
|
|
f442fb4d69 | ||
|
|
c8796a3184 | ||
|
|
2d7859759e | ||
|
|
9ca41c1f26 | ||
|
|
840a9e2a76 | ||
|
|
6af4c21cdf | ||
|
|
a3bb334981 | ||
|
|
6727f0c02c | ||
|
|
fa1dc34152 | ||
|
|
e51643e87e | ||
|
|
7947d92bc1 | ||
|
|
f829945117 | ||
|
|
0ad5bcfbc1 | ||
|
|
07cd481954 | ||
|
|
cdfdd41293 | ||
|
|
4e724391e0 | ||
|
|
76db7d7295 | ||
|
|
5a8763154e | ||
|
|
8902bcde07 | ||
|
|
84d2cb3cb3 | ||
|
|
7592eeea59 | ||
|
|
be069e26ae | ||
|
|
196b08cdfd | ||
|
|
120a000e86 | ||
|
|
1de89bf889 | ||
|
|
c5b422e2ca | ||
|
|
e053da5a14 | ||
|
|
164b963dd2 | ||
|
|
f432b2ba96 | ||
|
|
c46a4eba43 | ||
|
|
7d38d74716 | ||
|
|
9f9c60937e | ||
|
|
23888dab85 | ||
|
|
67acdbc817 | ||
|
|
35a96edb0d | ||
|
|
5491c48e71 | ||
|
|
89cfded971 | ||
|
|
851e9c0f4c | ||
|
|
91d4ec7e7b | ||
|
|
6e507a7786 | ||
|
|
45daa925bd | ||
|
|
ee908ae195 | ||
|
|
2d15ea015e | ||
|
|
91f26d7f0a | ||
|
|
9f9f5a6157 | ||
|
|
588f3aca3c | ||
|
|
1381ff15af | ||
|
|
4fa8f1c49b | ||
|
|
0ec147513c | ||
|
|
c555e18ebb | ||
|
|
c8ce82a11f | ||
|
|
11a2e3ca10 | ||
|
|
a3de5a331e | ||
|
|
087feff7cb | ||
|
|
52f59aca78 | ||
|
|
fb98dd6d6e | ||
|
|
53b6dc216f | ||
|
|
73a0f047a1 | ||
|
|
4731232aae | ||
|
|
7f340851fc | ||
|
|
af9ae8bd6e | ||
|
|
1be35bfa09 | ||
|
|
a2650496ce | ||
|
|
ea6bdf0b99 | ||
|
|
589c1a4ff5 | ||
|
|
5bf42600d4 | ||
|
|
ac9dde683f | ||
|
|
0e3fcaa1bb | ||
|
|
859e0fd1ce | ||
|
|
f779231da2 | ||
|
|
d4ebb6fdf4 | ||
|
|
d9824887c9 | ||
|
|
9eeadfc46b | ||
|
|
6cbecaaf5c | ||
|
|
d9d2b941eb | ||
|
|
19fffa1db7 | ||
|
|
e0d9d88cd5 | ||
|
|
8ad124a0b8 | ||
|
|
47c73abd04 | ||
|
|
a51592e180 | ||
|
|
eb6807d3cb | ||
|
|
d2d660a935 | ||
|
|
dddbf525da | ||
|
|
a5f0ea6979 | ||
|
|
067f69cade | ||
|
|
d17ca3ddd8 | ||
|
|
665bd22fc5 | ||
|
|
287b6ce1b4 | ||
|
|
ac23ad5013 | ||
|
|
1d087efc97 | ||
|
|
e960488338 | ||
|
|
4146086d2f | ||
|
|
3ce84d9a44 | ||
|
|
9b7c45d05d | ||
|
|
42b6be8747 | ||
|
|
9a1dcea7b3 | ||
|
|
4e824686f5 | ||
|
|
8c1b33c439 | ||
|
|
3e84067a2b | ||
|
|
c3ebd33544 | ||
|
|
14b350f3a1 | ||
|
|
e9e5ecbe30 | ||
|
|
c26b82939f | ||
|
|
7f62cecb61 | ||
|
|
130524b719 | ||
|
|
2fc205ceb9 | ||
|
|
9f13897408 | ||
|
|
7be92ff5d2 | ||
|
|
6e58914746 | ||
|
|
5e96c3d910 | ||
|
|
21885249cf | ||
|
|
5da8d4e0d4 | ||
|
|
053c89578c |
3
.gitreview
Normal file
3
.gitreview
Normal file
@@ -0,0 +1,3 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=pysim
|
||||
38
README
38
README
@@ -1,38 +0,0 @@
|
||||
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
Normal file
107
README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
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'))
|
||||
19
contrib/jenkins.sh
Executable file
19
contrib/jenkins.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/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
|
||||
|
||||
16
csv-format
Normal file
16
csv-format
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
141
execute_ipr.py
Executable file
141
execute_ipr.py
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/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})
|
||||
55
format_ipr.py
Normal file
55
format_ipr.py
Normal file
@@ -0,0 +1,55 @@
|
||||
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
|
||||
74
format_ldr.py
Normal file
74
format_ldr.py
Normal file
@@ -0,0 +1,74 @@
|
||||
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
|
||||
84
ldr_to_ipr.py
Executable file
84
ldr_to_ipr.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/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)
|
||||
417
pySim-prog.py
417
pySim-prog.py
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Utility to deal with sim cards and program the 'magic' ones easily
|
||||
@@ -30,6 +30,7 @@ import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import json
|
||||
@@ -38,9 +39,11 @@ except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
from pySim.commands import SimCardCommands
|
||||
from pySim.cards import _cards_classes
|
||||
from pySim.utils import h2b, swap_nibbles, rpad
|
||||
|
||||
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 *
|
||||
|
||||
def parse_options():
|
||||
|
||||
@@ -58,10 +61,32 @@ 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,
|
||||
@@ -81,16 +106,21 @@ def parse_options():
|
||||
help="Country code [default: %default]",
|
||||
default=1,
|
||||
)
|
||||
parser.add_option("-x", "--mcc", dest="mcc", type="int",
|
||||
parser.add_option("-x", "--mcc", dest="mcc", type="string",
|
||||
help="Mobile Country Code [default: %default]",
|
||||
default=901,
|
||||
default="901",
|
||||
)
|
||||
parser.add_option("-y", "--mnc", dest="mnc", type="int",
|
||||
parser.add_option("-y", "--mnc", dest="mnc", type="string",
|
||||
help="Mobile Network Code [default: %default]",
|
||||
default=55,
|
||||
default="55",
|
||||
)
|
||||
parser.add_option("--mnclen", dest="mnclen", type="choice",
|
||||
help="Length of Mobile Network Code [default: %default]",
|
||||
default=2,
|
||||
choices=[2, 3],
|
||||
)
|
||||
parser.add_option("-m", "--smsc", dest="smsc",
|
||||
help="SMSP [default: '00 + country code + 5555']",
|
||||
help="SMSC number (Start with + for international no.) [default: '00 + country code + 5555']",
|
||||
)
|
||||
parser.add_option("-M", "--smsp", dest="smsp",
|
||||
help="Raw SMSP content in hex [default: auto from SMSC]",
|
||||
@@ -102,6 +132,9 @@ 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)",
|
||||
)
|
||||
@@ -111,11 +144,15 @@ def parse_options():
|
||||
parser.add_option("--op", dest="op",
|
||||
help="Set OP to derive OPC from OP and KI",
|
||||
)
|
||||
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",
|
||||
)
|
||||
@@ -144,17 +181,22 @@ def parse_options():
|
||||
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):
|
||||
parser.error("CSV mode needs either an IMSI, --read-imsi or batch mode")
|
||||
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':
|
||||
@@ -185,7 +227,7 @@ def _digits(secret, usage, len, num):
|
||||
return d[0:len]
|
||||
|
||||
def _mcc_mnc_digits(mcc, mnc):
|
||||
return ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
|
||||
return '%s%s' % (mcc, mnc)
|
||||
|
||||
def _cc_digits(cc):
|
||||
return ('%03d' if cc > 100 else '%02d') % cc
|
||||
@@ -230,34 +272,25 @@ 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 from the
|
||||
"""Generates Name, ICCID, MCC, MNC, IMSI, SMSP, Ki, PIN-ADM from the
|
||||
options given by the user"""
|
||||
|
||||
# MCC/MNC
|
||||
mcc = opts.mcc
|
||||
mnc = opts.mnc
|
||||
|
||||
if not ((0 < mcc < 999) and (0 < mnc < 999)):
|
||||
raise ValueError('mcc & mnc must be between 0 and 999')
|
||||
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")
|
||||
|
||||
# Digitize country code (2 or 3 digits)
|
||||
cc_digits = _cc_digits(opts.country)
|
||||
@@ -265,11 +298,26 @@ 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):
|
||||
raise ValueError('ICCID must be 19 digits !');
|
||||
if not _isnum(iccid, 19) and not _isnum(iccid, 20):
|
||||
raise ValueError('ICCID must be 19 or 20 digits !');
|
||||
|
||||
else:
|
||||
if opts.num is None:
|
||||
@@ -278,7 +326,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)
|
||||
@@ -326,14 +374,19 @@ 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 !')
|
||||
raise ValueError('SMSC must be digits only!\n \
|
||||
Start with \'+\' for international numbers')
|
||||
else:
|
||||
smsc = '00%d' % opts.country + '5555' # Hack ...
|
||||
|
||||
smsc = '%02d' % ((len(smsc) + 3)//2,) + "81" + swap_nibbles(rpad(smsc, 20))
|
||||
smsc = '%02d' % ((len(smsc) + 3)//2,) + ton + swap_nibbles(rpad(smsc, 20))
|
||||
|
||||
smsp = (
|
||||
'e1' + # Parameters indicator
|
||||
@@ -344,6 +397,17 @@ def gen_parameters(opts):
|
||||
'00' # TP-Validity period
|
||||
)
|
||||
|
||||
# ACC
|
||||
if opts.acc is not None:
|
||||
acc = opts.acc
|
||||
if not _ishex(acc):
|
||||
raise ValueError('ACC must be hex digits only !')
|
||||
if len(acc) != 2*2:
|
||||
raise ValueError('ACC must be exactly 2 bytes')
|
||||
|
||||
else:
|
||||
acc = None
|
||||
|
||||
# Ki (random)
|
||||
if opts.ki is not None:
|
||||
ki = opts.ki
|
||||
@@ -352,7 +416,7 @@ def gen_parameters(opts):
|
||||
else:
|
||||
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||
|
||||
# Ki (random)
|
||||
# OPC (random)
|
||||
if opts.opc is not None:
|
||||
opc = opts.opc
|
||||
if not re.match('^[0-9a-fA-F]{32}$', opc):
|
||||
@@ -363,6 +427,7 @@ def gen_parameters(opts):
|
||||
else:
|
||||
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||
|
||||
pin_adm = sanitize_pin_adm(opts)
|
||||
|
||||
# Return that
|
||||
return {
|
||||
@@ -374,20 +439,28 @@ def gen_parameters(opts):
|
||||
'smsp' : smsp,
|
||||
'ki' : ki,
|
||||
'opc' : opc,
|
||||
'acc' : acc,
|
||||
'pin_adm' : pin_adm,
|
||||
'msisdn' : opts.msisdn,
|
||||
}
|
||||
|
||||
|
||||
def print_parameters(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
|
||||
""" % 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)
|
||||
|
||||
|
||||
def write_params_csv(opts, params):
|
||||
@@ -400,18 +473,27 @@ def write_params_csv(opts, params):
|
||||
cw.writerow([params[x] for x in row])
|
||||
f.close()
|
||||
|
||||
def _read_params_csv(opts, imsi):
|
||||
def _read_params_csv(opts, iccid=None, imsi=None):
|
||||
import csv
|
||||
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
|
||||
f = open(opts.read_csv, 'r')
|
||||
cr = csv.DictReader(f, row)
|
||||
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_imsi is False:
|
||||
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;
|
||||
@@ -419,11 +501,36 @@ def _read_params_csv(opts, imsi):
|
||||
f.close()
|
||||
return None
|
||||
|
||||
def read_params_csv(opts, imsi):
|
||||
row = _read_params_csv(opts, imsi)
|
||||
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'] = int(row['mcc'])
|
||||
row['mnc'] = int(row['mnc'])
|
||||
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
|
||||
|
||||
|
||||
@@ -473,7 +580,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 ...
|
||||
@@ -481,7 +588,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
|
||||
@@ -504,34 +611,72 @@ def save_batch(opts):
|
||||
fh.close()
|
||||
|
||||
|
||||
def card_detect(opts, scc):
|
||||
def process_card(opts, first, card_handler):
|
||||
|
||||
# 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:
|
||||
# Connect transport
|
||||
card_handler.get(first)
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Get card
|
||||
card = card_detect(opts.type, scc)
|
||||
if card is None:
|
||||
print "Autodetection failed"
|
||||
return
|
||||
print("No card detected!")
|
||||
return -1
|
||||
|
||||
if opts.type == "auto_once":
|
||||
opts.type = card.name
|
||||
# Probe only
|
||||
if opts.probe:
|
||||
return 0
|
||||
|
||||
elif opts.type in ctypes:
|
||||
card = ctypes[opts.type](scc)
|
||||
# Erase if requested
|
||||
if opts.erase:
|
||||
print("Formatting ...")
|
||||
card.erase()
|
||||
card.reset()
|
||||
|
||||
# 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:
|
||||
raise ValueError("Unknown card type %s" % opts.type)
|
||||
print("Dry Run: NOT PROGRAMMING!")
|
||||
|
||||
return card
|
||||
# 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
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -539,89 +684,57 @@ if __name__ == '__main__':
|
||||
# Parse options
|
||||
opts = parse_options()
|
||||
|
||||
# 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)
|
||||
# Init card reader driver
|
||||
sl = init_reader(opts)
|
||||
|
||||
# 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 not done:
|
||||
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
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
|
||||
# Not the first anymore !
|
||||
first = False
|
||||
|
||||
if opts.dry_run is 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
|
||||
if opts.source == 'cmdline':
|
||||
cp = gen_parameters(opts)
|
||||
elif opts.source == 'csv':
|
||||
if opts.read_imsi:
|
||||
if opts.dry_run:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
(res,_) = scc.read_binary(['3f00', '7f20', '6f07'])
|
||||
imsi = swap_nibbles(res)[3:]
|
||||
else:
|
||||
imsi = opts.imsi
|
||||
cp = read_params_csv(opts, imsi)
|
||||
if cp is None:
|
||||
print "Error reading parameters\n"
|
||||
sys.exit(2)
|
||||
print_parameters(cp)
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Program the card
|
||||
print "Programming ..."
|
||||
if opts.dry_run is not True:
|
||||
card.program(cp)
|
||||
else:
|
||||
print "Dry Run: NOT PROGRAMMING!"
|
||||
|
||||
# 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"
|
||||
# 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:
|
||||
done = True
|
||||
sys.exit(rc)
|
||||
|
||||
first = False
|
||||
|
||||
284
pySim-read.py
Executable file
284
pySim-read.py
Executable file
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Utility to display some informations about a SIM card
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
|
||||
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
|
||||
# Copyright (C) 2013 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/>.
|
||||
#
|
||||
|
||||
import hashlib
|
||||
from optparse import OptionParser
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def parse_options():
|
||||
|
||||
parser = OptionParser(usage="usage: %prog [options]")
|
||||
|
||||
parser.add_option("-d", "--device", dest="device", metavar="DEV",
|
||||
help="Serial Device for SIM access [default: %default]",
|
||||
default="/dev/ttyUSB0",
|
||||
)
|
||||
parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",
|
||||
help="Baudrate used for SIM access [default: %default]",
|
||||
default=9600,
|
||||
)
|
||||
parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",
|
||||
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()
|
||||
|
||||
if args:
|
||||
parser.error("Extraneous arguments")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Parse options
|
||||
opts = parse_options()
|
||||
|
||||
# Init card reader driver
|
||||
sl = init_reader(opts)
|
||||
|
||||
# Create command layer
|
||||
scc = SimCardCommands(transport=sl)
|
||||
|
||||
# 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()
|
||||
if sw == '9000':
|
||||
print("ICCID: %s" % (res,))
|
||||
else:
|
||||
print("ICCID: Can't read, response code = %s" % (sw,))
|
||||
|
||||
# EF.IMSI
|
||||
(res, sw) = card.read_imsi()
|
||||
if sw == '9000':
|
||||
print("IMSI: %s" % (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)
|
||||
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.ACC
|
||||
(res, sw) = card.read_binary('ACC')
|
||||
if sw == '9000':
|
||||
print("ACC: %s" % (res,))
|
||||
else:
|
||||
print("ACC: Can't read, response code = %s" % (sw,))
|
||||
|
||||
# EF.MSISDN
|
||||
try:
|
||||
(res, sw) = card.read_msisdn()
|
||||
if sw == '9000':
|
||||
# (npi, ton, msisdn) = res
|
||||
if res is not None:
|
||||
print("MSISDN (NPI=%d ToN=%d): %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,))
|
||||
|
||||
# Done for this card and maybe for everything ?
|
||||
print("Done !\n")
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
108
pySim/card_handler.py
Normal file
108
pySim/card_handler.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/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("")
|
||||
969
pySim/cards.py
969
pySim/cards.py
File diff suppressed because it is too large
Load Diff
@@ -24,72 +24,183 @@
|
||||
|
||||
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):
|
||||
return self._cla_byte
|
||||
@cla_byte.setter
|
||||
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("a0a4000002" + i)
|
||||
data, sw = self._tp.send_apdu_checksw(self.cla_byte + "a4" + self.sel_ctrl + "02" + i)
|
||||
rv.append(data)
|
||||
return rv
|
||||
|
||||
def read_binary(self, ef, length=None, offset=0):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
r = self.select_file(ef)
|
||||
if length is None:
|
||||
length = int(r[-1][4:8], 16) - offset
|
||||
pdu = 'a0b0%04x%02x' % (offset, (min(256, length) & 0xff))
|
||||
return self._tp.send_apdu(pdu)
|
||||
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 update_binary(self, ef, data, offset=0):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
def read_binary(self, ef, length=None, offset=0):
|
||||
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
|
||||
|
||||
def update_binary(self, ef, data, offset=0, verify=False):
|
||||
self.select_file(ef)
|
||||
pdu = 'a0d6%04x%02x' % (offset, len(data)/2) + data
|
||||
return self._tp.send_apdu_checksw(pdu)
|
||||
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()))
|
||||
|
||||
def read_record(self, ef, rec_no):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
r = self.select_file(ef)
|
||||
rec_length = int(r[-1][28:30], 16)
|
||||
pdu = 'a0b2%02x04%02x' % (rec_no, rec_length)
|
||||
rec_length = self.__record_len(r)
|
||||
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):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
def update_record(self, ef, rec_no, data, force_len=False, verify=False):
|
||||
r = self.select_file(ef)
|
||||
if not force_len:
|
||||
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))
|
||||
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))
|
||||
else:
|
||||
rec_length = len(data)/2
|
||||
pdu = ('a0dc%02x04%02x' % (rec_no, rec_length)) + data
|
||||
return self._tp.send_apdu_checksw(pdu)
|
||||
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()))
|
||||
|
||||
def record_size(self, ef):
|
||||
r = self.select_file(ef)
|
||||
return int(r[-1][28:30], 16)
|
||||
return self.__record_len(r)
|
||||
|
||||
def record_count(self, ef):
|
||||
r = self.select_file(ef)
|
||||
return int(r[-1][4:8], 16) // int(r[-1][28:30], 16)
|
||||
return self.__len(r) // self.__record_len(r)
|
||||
|
||||
def binary_size(self, ef):
|
||||
r = self.select_file(ef)
|
||||
return self.__len(r)
|
||||
|
||||
def run_gsm(self, rand):
|
||||
if len(rand) != 32:
|
||||
raise ValueError('Invalid rand')
|
||||
self.select_file(['3f00', '7f20'])
|
||||
return self._tp.send_apdu('a088000010' + rand)
|
||||
return self._tp.send_apdu(self.cla_byte + '88000010' + rand)
|
||||
|
||||
def reset_card(self):
|
||||
return self._tp.reset_card()
|
||||
|
||||
def verify_chv(self, chv_no, code):
|
||||
fc = rpad(b2h(code), 16)
|
||||
return self._tp.send_apdu_checksw('a02000' + ('%02x' % chv_no) + '08' + fc)
|
||||
return self._tp.send_apdu_checksw(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)
|
||||
|
||||
@@ -23,11 +23,17 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import exceptions
|
||||
|
||||
|
||||
class NoCardError(exceptions.Exception):
|
||||
try:
|
||||
# This is for compatibility with python 2 and 3
|
||||
from exceptions import Exception
|
||||
except:
|
||||
pass
|
||||
|
||||
class ProtocolError(exceptions.Exception):
|
||||
class NoCardError(Exception):
|
||||
pass
|
||||
|
||||
class ProtocolError(Exception):
|
||||
pass
|
||||
|
||||
class ReaderError(Exception):
|
||||
pass
|
||||
|
||||
@@ -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,12 +62,18 @@ 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)
|
||||
|
||||
if (sw is not None) and (sw[0:2] == '9f'):
|
||||
# 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')):
|
||||
pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
|
||||
data, sw = self.send_apdu_raw(pdu_gr)
|
||||
|
||||
@@ -77,12 +83,23 @@ 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")
|
||||
sw : string of 4 hexadecimal characters (ex. "9000"). The
|
||||
user may mask out certain digits using a '?' to add some
|
||||
ambiguity if needed.
|
||||
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)
|
||||
if sw.lower() != rv[1]:
|
||||
raise RuntimeError("SW match failed ! Expected %s and got %s." % (sw.lower(), rv[1]))
|
||||
|
||||
# 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]))
|
||||
return rv
|
||||
|
||||
157
pySim/transport/calypso.py
Normal file
157
pySim/transport/calypso.py
Normal file
@@ -0,0 +1,157 @@
|
||||
#!/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)
|
||||
126
pySim/transport/modem_atcmd.py
Normal file
126
pySim/transport/modem_atcmd.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/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
|
||||
@@ -22,6 +22,7 @@
|
||||
# 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
|
||||
@@ -52,19 +53,22 @@ class PcscSimLink(LinkBase):
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self._con.connect()
|
||||
# Explicitly select T=0 communication protocol
|
||||
self._con.connect(CardConnection.T0_protocol)
|
||||
except CardConnectionException:
|
||||
raise ProtocolError()
|
||||
except NoCardException:
|
||||
raise NoCardError()
|
||||
|
||||
def get_atr(self):
|
||||
return self._con.getATR()
|
||||
|
||||
def disconnect(self):
|
||||
self._con.disconnect()
|
||||
|
||||
def reset_card(self):
|
||||
self._con.disconnect()
|
||||
try:
|
||||
self._con.connect()
|
||||
except NoCardException:
|
||||
raise NoCardError()
|
||||
self.disconnect()
|
||||
self.connect()
|
||||
return 1
|
||||
|
||||
def send_apdu_raw(self, pdu):
|
||||
|
||||
@@ -46,6 +46,7 @@ class SerialSimLink(LinkBase):
|
||||
)
|
||||
self._rst_pin = rst
|
||||
self._debug = debug
|
||||
self._atr = None
|
||||
|
||||
def __del__(self):
|
||||
self._sl.close()
|
||||
@@ -91,6 +92,9 @@ class SerialSimLink(LinkBase):
|
||||
def connect(self):
|
||||
self.reset_card()
|
||||
|
||||
def get_atr(self):
|
||||
return self._atr
|
||||
|
||||
def disconnect(self):
|
||||
pass # Nothing to do really ...
|
||||
|
||||
@@ -102,6 +106,7 @@ class SerialSimLink(LinkBase):
|
||||
raise ProtocolError()
|
||||
|
||||
def _reset_card(self):
|
||||
self._atr = None
|
||||
rst_meth_map = {
|
||||
'rts': self._sl.setRTS,
|
||||
'dtr': self._sl.setDTR,
|
||||
@@ -133,25 +138,31 @@ 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):
|
||||
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(self._rx_byte())))
|
||||
b = self._rx_byte()
|
||||
self._atr.append(ord(b))
|
||||
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
|
||||
|
||||
for i in range(0, t0 & 0xf):
|
||||
self._dbg_print("Historical = %x" % ord(self._rx_byte()))
|
||||
b = self._rx_byte()
|
||||
self._atr.append(ord(b))
|
||||
self._dbg_print("Historical = %x" % ord(b))
|
||||
|
||||
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)
|
||||
@@ -202,7 +213,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 = ''
|
||||
|
||||
248
pySim/ts_31_102.py
Normal file
248
pySim/ts_31_102.py
Normal file
@@ -0,0 +1,248 @@
|
||||
#!/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',
|
||||
}
|
||||
46
pySim/ts_31_103.py
Normal file
46
pySim/ts_31_103.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/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'
|
||||
}
|
||||
316
pySim/ts_51_011.py
Normal file
316
pySim/ts_51_011.py
Normal file
@@ -0,0 +1,316 @@
|
||||
#!/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',
|
||||
}
|
||||
527
pySim/utils.py
527
pySim/utils.py
@@ -34,6 +34,17 @@ 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])])
|
||||
|
||||
@@ -42,3 +53,519 @@ 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
|
||||
oe = len(imsi) & 1 # Odd (1) / Even (0)
|
||||
ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
|
||||
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
|
||||
oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
|
||||
if not oe:
|
||||
# if even, only half of last byte was used
|
||||
l = l-1
|
||||
if l != len(swapped) - 1:
|
||||
return None
|
||||
imsi = swapped[1:]
|
||||
return imsi
|
||||
|
||||
def dec_iccid(ef):
|
||||
return swap_nibbles(ef).strip('f')
|
||||
|
||||
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
|
||||
|
||||
76
pySim/utils_test.py
Normal file
76
pySim/utils_test.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/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()
|
||||
5
pysim-testdata/Fairwaves-SIM.data
Normal file
5
pysim-testdata/Fairwaves-SIM.data
Normal file
@@ -0,0 +1,5 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
IMSI=001010000000111
|
||||
ADM_HEX=CAE743DB9C5B5A58
|
||||
|
||||
119
pysim-testdata/Fairwaves-SIM.ok
Normal file
119
pysim-testdata/Fairwaves-SIM.ok
Normal file
@@ -0,0 +1,119 @@
|
||||
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 !
|
||||
|
||||
5
pysim-testdata/Wavemobile-SIM.data
Normal file
5
pysim-testdata/Wavemobile-SIM.data
Normal file
@@ -0,0 +1,5 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
IMSI=001010000000102
|
||||
ADM_HEX=15E31383624FDC8A
|
||||
|
||||
135
pysim-testdata/Wavemobile-SIM.ok
Normal file
135
pysim-testdata/Wavemobile-SIM.ok
Normal file
@@ -0,0 +1,135 @@
|
||||
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 !
|
||||
|
||||
6
pysim-testdata/fakemagicsim.data
Normal file
6
pysim-testdata/fakemagicsim.data
Normal file
@@ -0,0 +1,6 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
57
pysim-testdata/fakemagicsim.ok
Normal file
57
pysim-testdata/fakemagicsim.ok
Normal file
@@ -0,0 +1,57 @@
|
||||
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 !
|
||||
|
||||
7
pysim-testdata/sysmoISIM-SJA2.data
Normal file
7
pysim-testdata/sysmoISIM-SJA2.data
Normal file
@@ -0,0 +1,7 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=AABBCCDDEEFFAABBCCDDEEFFAABBCCDD
|
||||
OPC=12345678901234567890123456789012
|
||||
IMSI=001010000000102
|
||||
ADM=72273953
|
||||
169
pysim-testdata/sysmoISIM-SJA2.ok
Normal file
169
pysim-testdata/sysmoISIM-SJA2.ok
Normal file
@@ -0,0 +1,169 @@
|
||||
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 !
|
||||
|
||||
8
pysim-testdata/sysmoUSIM-SJS1.data
Normal file
8
pysim-testdata/sysmoUSIM-SJS1.data
Normal file
@@ -0,0 +1,8 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
MSISDN=+77776336143
|
||||
ADM=55538407
|
||||
143
pysim-testdata/sysmoUSIM-SJS1.ok
Normal file
143
pysim-testdata/sysmoUSIM-SJS1.ok
Normal file
@@ -0,0 +1,143 @@
|
||||
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 !
|
||||
|
||||
7
pysim-testdata/sysmosim-gr1.data
Normal file
7
pysim-testdata/sysmosim-gr1.data
Normal file
@@ -0,0 +1,7 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
ADM=DDDDDDDD
|
||||
55
pysim-testdata/sysmosim-gr1.ok
Normal file
55
pysim-testdata/sysmosim-gr1.ok
Normal file
@@ -0,0 +1,55 @@
|
||||
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 !
|
||||
|
||||
13
script_format.py
Normal file
13
script_format.py
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
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)
|
||||
5
tests/Wavemobile-SIM.data.example
Normal file
5
tests/Wavemobile-SIM.data.example
Normal file
@@ -0,0 +1,5 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
IMSI=001010000000102
|
||||
ADM_HEX=0123456789ABCDEF
|
||||
|
||||
6
tests/fakemagicsim.data.example
Normal file
6
tests/fakemagicsim.data.example
Normal file
@@ -0,0 +1,6 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
229
tests/pysim-test.sh
Executable file
229
tests/pysim-test.sh
Executable file
@@ -0,0 +1,229 @@
|
||||
#!/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
|
||||
8
tests/sysmoUSIM-SJS1.data.example
Normal file
8
tests/sysmoUSIM-SJS1.data.example
Normal file
@@ -0,0 +1,8 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
MSISDN=+77776336143
|
||||
ADM=12345678
|
||||
7
tests/sysmosim-gr1.data.example
Normal file
7
tests/sysmosim-gr1.data.example
Normal file
@@ -0,0 +1,7 @@
|
||||
MCC=001
|
||||
MNC=01
|
||||
ICCID=1122334455667788990
|
||||
KI=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
OPC=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
IMSI=001010000000102
|
||||
ADM=DDDDDDDD
|
||||
Reference in New Issue
Block a user