66 Commits

Author SHA1 Message Date
Alexander Chemeris
5dfda9fdd7 pySim-fairwaves-prog: Read a selected A3/A8 algorithm.
Change-Id: I757ea725bd5616dbd6ef329ea5981063fd780761
2018-01-27 02:09:04 +09:00
Alexander Chemeris
99b5e321e5 Checking in pySim-read-all.py - utility to read all known files on a SIM card.
Change-Id: Iaa301a8a6356d610753c2bba286187c3220833a9
2018-01-27 02:08:25 +09:00
Alexander Chemeris
d21ef12f8d commands: Allow reading binary/record files if they are already selected.
Change-Id: Iad3b240eab8a54a4d0ef2623bc9dd17f7dce5fa5
2018-01-27 02:07:10 +09:00
Alexander Chemeris
1198ad9e15 utils: Add a function to decode SELECT result for EF files.
Change-Id: Ib2af7fe5205654c5277fec01b6b0b10a68e472b9
2018-01-27 02:06:30 +09:00
Alexander Chemeris
cc85a1ee71 utils: Zero-pad MCC and MNC in enc_plmn()
Change-Id: I94607f6a87a416d05bd577a29d1466452921d604
2018-01-27 02:05:20 +09:00
Alexander Chemeris
2322c1f9ff Checking in various utilities to manipulate Fairwaves SIM card DB.
Change-Id: Ibb49b127df68fd210e6b75e4f0b9c667d2c2cc7c
2018-01-26 16:45:59 +09:00
Alexander Chemeris
6a38c4a2f3 Checking in pySim-run-gsm.py - utility to run A3/A8 on a SIM card.
Change-Id: Iea8f61bc86499734d432f668dd0b87af999abf27
2018-01-26 16:24:27 +09:00
Alexander Chemeris
575f64e38a pySim-read: Read SPN and use new EF constants to address other files.
Change-Id: I3207edaa9670d1c76b1ba2142f1042794055e94c
2018-01-26 15:57:19 +09:00
Alexander Chemeris
4d5c0293a2 pySim-fairwaves-prog: Write Ki/OPC/IMSI as read from the input file.
Commenting out generation of random ones.

Change-Id: I36a87f37671cd64d801006d7e2def8f41e95700a
2018-01-26 15:55:58 +09:00
Alexander Chemeris
3a27424ff8 pySim-fairwaves-prog: Add an option to play sound on successful write in the batch mode.
Change-Id: I440a1fd30abfaf95da2daf7b618f6c25c3b092d4
2018-01-26 15:53:26 +09:00
Alexander Chemeris
5705837a1b ts_51_011: Fix a comment language.
Change-Id: I9e92ad180791223fc90a61ea1e68d18dd67cc000
2018-01-26 15:52:29 +09:00
Alexander Chemeris
b5208b5544 cards: Add ability to read/set A3/A8 algorithm for Fairwaves SIM cards.
Change-Id: Ibb68c2a695b1f97ca11e25c14770132cea604cbe
2018-01-26 15:51:19 +09:00
Alexander Chemeris
4dabfda193 cards: Minor formating cleanup
Change-Id: I0d1cf38a422c36b26dc125384d2df70e82e752e1
2018-01-26 15:50:46 +09:00
Alexander Chemeris
6d4a0a1a3e Add a comanda to run GSM auth algorithm.
Change-Id: I55d4cf5ad4d50c473ed4febb171cbc8854d1fa99
2018-01-26 15:49:49 +09:00
Alexander Chemeris
e6d4faa6f5 Checking in pySim-fairwaves-prog.py utility.
This utility is an example utility for programming Fairwaves SIM cards.
The original pySim-prog.py utility is already bloated with features
and is difficult to modify so we decided to create a leaner and easier to
maintain and modify version.

Change-Id: I9f58e1b45d1785d59cef161eab1388332a97936b
2018-01-11 13:27:08 +09:00
Alexander Chemeris
19fffa1db7 Make derive_milenage_opc and calculate_luhn publicly available through utils.py
Change-Id: I2effc85fd55da0981de0ada74dcb28b7e8e56a01
2018-01-11 13:06:43 +09:00
Alexander Chemeris
e0d9d88cd5 cards: Add Fairwaves SIM implementation.
Change-Id: Ia10ac433d3b0482bdf727c31f65a10042152797b
2018-01-10 17:16:31 +09:00
Alexander Chemeris
8ad124a0b8 cards: Implement card type autodetection based on ATR.
Change-Id: I1099a96626c0ce74243b47a8fdfa25b0d76a1ef3
2018-01-10 17:12:10 +09:00
Alexander Chemeris
47c73abd04 pySim-prog: Replace magic numbers with a readable EF file name.
Change-Id: Ibda7d5a4132971e884f6d760baf20cd33025a2af
2018-01-10 17:12:10 +09:00
Alexander Chemeris
a51592e180 pySim-prog: ADM code can be longer 8 digits, it's implementation specific.
E.g. Fairwaves SIM cards have longer ADM codes.

Change-Id: I87d61764eeba4bcf7525ee4778cb8f244930db9b
2018-01-10 17:12:10 +09:00
Alexander Chemeris
eb6807d3cb cards: Extend Card class with access functions for some of the standard EF files.
Change-Id: Icb7227fa7ebc837fccab456cbfad529f6ee81a28
2018-01-10 17:12:10 +09:00
Alexander Chemeris
d2d660a935 Add methods to get ATR for a card or a link.
Implemented for both serial and PCSC readers.

Change-Id: Ic12e4b115d24a8b7e483a5603dd6cec90ad289cc
2018-01-10 17:12:10 +09:00
Alexander Chemeris
dddbf525da utils: Fix documentation. 3+3=6 digits equals 3 bytes, not 6
Change-Id: I2722d788a69976e1c64a9caf6cf3049af27f9a30
2018-01-10 14:04:17 +09:00
Alexander Chemeris
a5f0ea6979 utils: Functions to encode/decode EF SPN.
According to TS 51 011.

Change-Id: Ida184bc5c81cc8c228b8981b703f77d017e53334
2018-01-10 14:04:11 +09:00
Alexander Chemeris
067f69cade ts_51_011: A file with MF/DF/EF constants from TS 51 011
pySim has been using magic numbers to access various files which makes it hard
to read, maintain and extend. With this file in place we can start replacing all
those magic numbers with human readable names lile EF['IMSI'] instead of
['3F00', '7F20', '6F07'].

Change-Id: I38f5d36d16b41b5d516a6a3e2ec1d09637883932
2018-01-10 14:04:08 +09:00
Alexander Chemeris
d17ca3ddd8 Fix comment: Ki -> OPC
Change-Id: I566cf7bc658c730b4381c0f145bfc4f805cca42a
2018-01-10 14:04:06 +09:00
Pau Espin Pedrol
665bd22fc5 utils.py: dec_imsi: Fix ValueError
It should fix the following observed error:
~/pysim$ ./pySim-read.py -p0
Reading ...
ICCID:
Traceback (most recent call last):
  File "./pySim-read.py", line 99, in <module>
    print("IMSI: %s" % (dec_imsi(res),))
  File "/home/lab434/pysim/pySim/utils.py", line 57, in dec_imsi
    l = int(ef[0:2]) * 2            # Length of the IMSI string
ValueError: invalid literal for int() with base 10: 'ff'

Change-Id: I7d3ecbf9edd190d1941816796cee60e3957d5943
2018-01-02 07:20:57 +00:00
Pau Espin Pedrol
287b6ce1b4 pySim-prog.py: Fix trailing whitespace
Change-Id: I735dc7bb774d77d3b60b1712b0f0afcbb81dc726
2017-12-29 23:35:22 +01:00
Pau Espin Pedrol
ac23ad5013 pySim-*.py: Set shebang to use python v2
Nowadays bin/python usually points to python3, and this script is written
in python2, which means if run directly from terminal it will fail with
some print syntax errors.

Change-Id: I6ab4e9edc44a8045915d4828c6de2fa98027fb7e
2017-12-29 23:35:22 +01:00
Daniel Willmann
1d087efc97 Support writing SMSP for sysmoUSIM-SJS1
Closes: OS#1989
Change-Id: I6cbf69be3d410c18a509b98a63cb69bab74a528a
2017-09-01 07:56:13 +00:00
Philipp Maier
e960488338 fix writing of ICCID for sysmo-usim-sjs1
The programming procedure for sysmo-usim-sjs1 lacks
writing the ICCID. This commit adds the missing call
to update_binary()

Change-Id: Ief85aa07c562d8d7b2a6dec302d2f485d0b1e577
2017-04-06 00:38:22 +02:00
Philipp Maier
4146086d2f Fix select control parameter
sysmo-usim-sjs1 requires P2 to be set to 0x0C (request FCI) when
using the USIM application commands. The FCI is not used by pysim
anyway and might even cause problems with other cards.

This commit adds a pair of get/set methods to the SimCardCommands
class in order to set a default for the selection control
parameters (P1, P2). (Similar to the set/get methods for the class
byte)

The SysmoUSIMSJS1 class now calls the setter method for the
selection control parameters inside of its constructuor and sets
the selection control parameter default to "000C". This way we
can be sure that we only change the behaviour for sysmo-usim-sjs1
and do not break support for any other cards.

Change-Id: I1993a267c952bf37d5de1cb4e1107f445614c17b
2017-04-06 00:38:22 +02:00
Neels Hofmeyr
3ce84d9a44 cosmetic: missing newlines on last line of 2 files
It's hard to keep this out of real patches, since normally editors add the
final newline automatically.
2017-03-21 13:07:57 +01:00
Harald Welte
9b7c45d05d README.md: Cosmetic/Formatting fixes 2017-03-17 22:34:08 +01:00
Harald Welte
42b6be8747 Update README with general project information and convert to Markdown 2017-03-17 18:08:09 +01:00
Harald Welte
9a1dcea7b3 Revert "Do not return the FCI information while selecting a file"
This reverts commit 8c1b33c439.
2016-10-27 14:40:02 +02:00
Holger Hans Peter Freyther
4e824686f5 re-program: Instead of specifying the IMSI, read it from the card. 2016-05-22 15:53:28 +02:00
Jan Balke
8c1b33c439 Do not return the FCI information while selecting a file
The sysmoUSIM-SJS1 card does not support returning the FCI
information.
Plus, the FCI information are not used anyway.
2015-08-20 13:32:56 +02:00
Jan Balke
3e84067a2b Add provision support for sysmoUSIM-SJS1 cards
The PIN-ADM has to given on the command line as it is provisioned
different for each card.
Currently only Ki, Op and IMSI are provisioned.
2015-08-20 13:32:56 +02:00
Jan Balke
c3ebd33544 Add PIN-ADM argument to the command line
Allow overwriting the default PIN-ADM set in the card implementation.
2015-08-20 13:32:56 +02:00
Jan Balke
14b350f3a1 Allow changing the class byte for pdu messages 2015-08-20 13:32:03 +02:00
Harald Welte
e9e5ecbe30 Introduce a '--dry-run' option to skip actual card access
This can be used for example to batch convert from CSV input to HLR
output without writing cards.
2015-08-20 13:23:15 +02:00
Harald Welte
c26b82939f read_params_csv: Make sure we don't end up in endless loop
as a side effect, the first line is now specified with '-j 0'
and not '-j 1'
2015-08-20 13:23:15 +02:00
Harald Welte
7f62cecb61 pySim-prog: Add mode where it can re-generate a card from CSV
Rather than just having the capability of writing to CSV, it now
has the capability to (re)write a card based on data from the CSV:

./pySim-prog.py -S csv --read-csv /tmp/sim.csv -i 901701234567890

or in batch mode (from the first line onwards):

./pySim-prog.py -S csv --read-csv /tmp/sim.csv --batch -j 1
2015-08-20 13:23:15 +02:00
Harald Welte
130524b719 split parameter writing for CSV and SQL into separate functions 2015-08-20 13:23:15 +02:00
Sylvain Munaut
2fc205ceb9 cards: sysmocom SysmoSIM-GR2 support added to cards
Written-by: Kevin Redon
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2013-12-23 17:22:56 +01:00
Sylvain Munaut
9f13897408 pySim-read: MISDN is not mandatory
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2013-07-18 10:36:51 +02:00
Alexander Chemeris
7be92ff5d2 Move encoder functions for ICCID, IMSI and PLMN to pySim.utils for consistency. 2013-07-13 08:53:21 +02:00
Alexander Chemeris
6e58914746 Add an utility to read data from a SIM card. 2013-07-13 08:50:55 +02:00
Alexander Chemeris
5e96c3d910 utils: Add functions to decode IMSI and ICCID from EF raw data. 2013-07-13 08:49:03 +02:00
Alexander Chemeris
21885249cf Implement setting of EF.ACC file in GrcardSim and _MagicSimBase
From: Alexander Chemeris <Alexander.Chemeris@gmail.com>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2013-07-02 15:19:09 +02:00
Sylvain Munaut
5da8d4e0d4 cards: Fix spacing according to PEP
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2013-07-02 15:13:24 +02:00
Sylvain Munaut
053c89578c cards: Fix comment position
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2013-07-02 15:12:32 +02:00
Holger Hans Peter Freyther
cca41795a7 usim/opc: Derive OPC from KI and OP 2012-03-22 15:25:31 +01:00
Harald Welte
93b38cd0f5 usim/opc: Add support to write completely random OPC
Allow to set the OPC, write it out to the state, generate it randomly.
2012-03-22 15:25:19 +01:00
Holger Hans Peter Freyther
4d91bf449f sysmoUSIM-GR1: Add basic (hacky) support for the sysmoUSIM-GR1
Right now we are only to program the KI, OPC, ICCID and IMSI. This
is done in a direct way and through the card abstraction.
2012-03-22 14:28:38 +01:00
Sylvain Munaut
8ca49e9ca8 cards: SMSP is left padded not right padded
I confused left & right, silly me ...

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2011-12-10 09:57:50 +01:00
Sylvain Munaut
9977c86e96 pySim-prog: Fix SMSC 'number type' field to 0x81
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2011-12-10 09:57:16 +01:00
Sylvain Munaut
607ce2a029 Fix computation of SMSP from a SMSC number
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2011-12-08 20:16:43 +01:00
Sylvain Munaut
1a914439b8 Whitespace fixes 2011-12-08 20:08:26 +01:00
Harald Welte
e10394bfb6 Add sysmoSIM-GR1 as alias to grcardsim 2011-12-08 19:39:49 +01:00
Harald Welte
2c0ff3a167 correctly compute the ICCID (19 digits, including luhn checksum) 2011-12-08 19:39:49 +01:00
Holger Hans Peter Freyther
5dffefbf0c pySim-prog.py: pyflakes doesn't find Importerror..
Importerror -> ImportError, spotted by pyflakes.
2011-11-22 21:18:06 +01:00
Holger Hans Peter Freyther
50e7c03816 README: Mention the necessary wait_for_card 2011-07-27 08:55:44 +02:00
Harald Welte
3156d9073f cards: Add support for grcard.cn (Green Card) SIM cards
This does not have auto-detection (yet), so you have to explicitly
specify "-t grcardsim" on the command line
2011-03-22 21:48:19 +01:00
Harald Welte
982a3075f9 commands: Check SW for UPDATE BIN / UPDATE REC / VERIFY CHV 2011-03-22 21:47:20 +01:00
17 changed files with 2203 additions and 121 deletions

36
README
View File

@@ -1,36 +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)
# Print IMSI
print sc.read_binary(['3f00', '7f20', '6f07'])
# Run A3/A8
print sc.run_gsm('00112233445566778899aabbccddeeff')

91
README.md Normal file
View File

@@ -0,0 +1,91 @@
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/>
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')

123
fairwaves_db_randomize.py Executable file
View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python
#
# Utility to randomize Ki and other values in a Fairwaves SIM card DB file
#
# Copyright (C) 2017-2018 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/>.
#
from optparse import OptionParser
import os
import sys
import csv
import random
from pySim.utils import derive_milenage_opc
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_sim_db(filename, sim_db):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
for iccid in sorted(sim_db.iterkeys()):
cw.writerow([x for x in sim_db[iccid]])
def process_sim(sim_keys, opts):
# Update IMSI
imsi = sim_keys[1]
imsi = "%03d%02d%s" % (opts.mcc, opts.mnc, imsi[5:])
sim_keys[1] = imsi
# Update Ki
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)]).upper()
sim_keys[8] = ki
# Update OPC
op_opc = derive_milenage_opc(ki, opts.op).upper()
sim_keys[9] = '01' + op_opc
return sim_keys
def process_db(sim_db, opts):
sim_db_new = {}
for iccid, sim_keys in sim_db.items():
sim_db_new[iccid] = process_sim(sim_keys, opts)
return sim_db_new
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to randomize Ki and other values in a Fairwaves SIM card DB file.")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space separated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space separated)",
default=None,
)
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default=001,
)
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default=01,
)
parser.add_option("--op", dest="op",
help="Set OP to derive OPC from OP and KI [default: %default]",
default='00000000000000000000000000000000',
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.out_db_filename is None:
print("Please specify output DB filename")
sys.exit(1)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
sim_db = process_db(sim_db, opts)
print("Writing SIM DB ...")
write_sim_db(opts.out_db_filename, sim_db)

154
fairwaves_db_to_hlr.py Executable file
View File

@@ -0,0 +1,154 @@
#!/usr/bin/env python
#
# Utility to write data from a Fairwaves SIM card DB to Osmocom HLR DB
#
# Copyright (C) 2017-2018 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/>.
#
from optparse import OptionParser
import os
import sys
import csv
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
# reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def _dbi_binary_quote(s):
# Count usage of each char
cnt = {}
for c in s:
cnt[c] = cnt.get(c, 0) + 1
# Find best offset
e = 0
m = len(s)
for i in range(1, 256):
if i == 39:
continue
sum_ = cnt.get(i, 0) + cnt.get((i+1)&0xff, 0) + cnt.get((i+39)&0xff, 0)
if sum_ < m:
m = sum_
e = i
if m == 0: # No overhead ? use this !
break;
# Generate output
out = []
out.append( chr(e) ) # Offset
for c in s:
x = (256 + ord(c) - e) % 256
if x in (0, 1, 39):
out.append('\x01')
out.append(chr(x+1))
else:
out.append(chr(x))
return ''.join(out)
def write_key_hlr(opts, sim_data):
# SQLite3 OpenBSC HLR
import sqlite3
conn = sqlite3.connect(opts.hlr_db_filename)
imsi = sim_data[1]
ki = sim_data[8]
c = conn.execute('SELECT id FROM Subscriber WHERE imsi = ?', (imsi,))
sub_id = c.fetchone()
if sub_id is None:
print("IMSI %s is not found in the HLR" % (imsi,))
return None
sub_id = sub_id[0]
print("IMSI %s has ID %d, writing Ki %s" % (imsi, sub_id, ki))
# c = conn.execute(
# 'INSERT INTO Subscriber ' +
# '(imsi, name, extension, authorized, created, updated) ' +
# 'VALUES ' +
# '(?,?,?,1,datetime(\'now\'),datetime(\'now\'));',
# [
# params['imsi'],
# params['name'],
# '9' + params['iccid'][-5:-1]
# ],
# )
# sub_id = c.lastrowid
# c.close()
c = conn.execute(
'INSERT OR REPLACE INTO AuthKeys ' +
'(subscriber_id, algorithm_id, a3a8_ki)' +
'VALUES ' +
'(?,?,?)',
[ sub_id, 2, sqlite3.Binary(_dbi_binary_quote(h2b(ki))) ],
)
c = conn.execute(
'DELETE FROM AuthLastTuples WHERE subscriber_id = ?',
[ sub_id ],
)
conn.commit()
conn.close()
return True
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to write data from a Fairwaves SIM card DB to Osmocom HLR DB.")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space searated)",
default="sim_db.dat",
)
parser.add_option("-d", "--hlr", dest="hlr_db_filename", type='string', metavar="FILE",
help="filename of a HLR SQLite3 DB to write the keys to",
default="hlr.sqlite3",
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
for iccid, sim in sim_db.items():
write_key_hlr(opts, sim)

82
fairwaves_db_uniq.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
#
# Utility to remove duplicates from a Fairwaves SIM card DB file
#
# Copyright (C) 2017-2018 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/>.
#
from optparse import OptionParser
import os
import sys
import csv
#from pySim.utils import h2b
def h2b(s):
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
# reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_sim_db(filename, sim_db):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
for iccid in sorted(sim_db.iterkeys()):
cw.writerow([x for x in sim_db[iccid]])
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to remove duplicates from a Fairwaves SIM card DB file")
parser.add_option("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space separated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space separated)",
default=None,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.out_db_filename is None:
print("Please specify output DB filename")
sys.exit(1)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
print("Writing SIM DB ...")
write_sim_db(opts.out_db_filename, sim_db)

289
pySim-fairwaves-prog.py Executable file
View File

@@ -0,0 +1,289 @@
#!/usr/bin/env python
#
# Utility to update SPN field of a SIM card
#
# Copyright (C) 2017-2018 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/>.
#
from optparse import OptionParser
import os
import sys
import csv
import random
import subprocess
from pySim.commands import SimCardCommands
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, derive_milenage_opc
from pySim.cards import card_autodetect
def load_sim_db(filename):
sim_db = {}
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=' ')
# Skip the header
reader.next()
for l in reader:
sim_db[l[0]] = l
return sim_db
def write_params_csv(filename, sim_keys):
with open(filename, 'a') as f:
cw = csv.writer(f, delimiter=' ')
cw.writerow([x for x in sim_keys])
def program_sim_card(card, sim_db, opts):
# Program the card
print("Reading SIM card ...")
# EF.ICCID
(iccid, sw) = card.read_iccid()
if sw != '9000':
print("ICCID: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("ICCID: %s" % (iccid))
# Find SIM card keys in the DB
sim_keys = sim_db.get(iccid+'F')
if sim_keys == None:
print("Can't find SIM card in the SIM DB.")
sys.exit(1)
# EF.IMSI
(imsi, sw) = card.read_imsi()
if sw != '9000':
print("IMSI: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("IMSI: %s" % (imsi))
# EF.SPN
((name, hplmn_disp, oplmn_disp), sw) = card.read_spn()
if sw == '9000':
print("Service Provider Name: %s" % name)
print(" display for HPLMN %s" % hplmn_disp)
print(" display for other PLMN %s" % oplmn_disp)
else:
print("Old SPN: Can't read, response code = %s" % (sw,))
print("Entring ADM code...")
# Enter ADM code to get access to proprietary files
sw = card.verify_adm(h2b(sim_keys[6]))
if sw != '9000':
print("Fail to verify ADM code with result = %s" % (sw,))
sys.exit(1)
# Read EF.Ki
(ki, sw) = card.read_ki()
if sw == '9000':
ki = ki.upper()
print("Ki: %s" % ki)
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Read EF.OP/OPC
((op_opc_type, op_opc), sw) = card.read_op_opc()
if sw == '9000':
op_opc = op_opc.upper()
print("%s: %s" % (op_opc_type, op_opc))
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Read EF.A3A8
(a3a8, sw) = card.read_a3a8()
if sw == '9000':
print("A3/A8: %s" % (a3a8,))
else:
print("A3/A8: Can't read, response code = %s" % (sw,))
print("Programming...")
# Update SPN
sw = card.update_spn(opts.name, False, False)
if sw != '9000':
print("SPN: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update Ki
ki = sim_keys[8]
# ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)]).upper()
# sim_keys[8] = ki
sw = card.update_ki(sim_keys[8])
if sw != '9000':
print("Ki: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update OPC
op_opc = sim_keys[9][2:]
# op_opc = derive_milenage_opc(ki, opts.op).upper()
# sim_keys[9] = '01' + op_opc
sw = card.update_opc(sim_keys[9][2:])
if sw != '9000':
print("OPC: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update Home PLMN
sw = card.update_hplmn_act(opts.mcc, opts.mnc)
if sw != '9000':
print("MCC/MNC: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Update IMSI
imsi = sim_keys[1]
# imsi = "%03d%02d%s" % (opts.mcc, opts.mnc, imsi[5:])
# sim_keys[1] = imsi
sw = card.update_imsi(imsi)
if sw != '9000':
print("IMSI: Fail to update with result = %s" % (sw,))
sys.exit(1)
# Verify EF.IMSI
(imsi_new, sw) = card.read_imsi()
if sw != '9000':
print("IMSI: Can't read, response code = %s" % (sw,))
sys.exit(1)
print("IMSI: %s" % (imsi_new))
# Verify EF.SPN
((name, hplmn_disp, oplmn_disp), sw) = card.read_spn()
if sw == '9000':
print("Service Provider Name: %s" % name)
print(" display for HPLMN %s" % hplmn_disp)
print(" display for other PLMN %s" % oplmn_disp)
else:
print("New SPN: Can't read, response code = %s" % (sw,))
# Verify EF.Ki
(ki_new, sw) = card.read_ki()
if sw == '9000':
ki_new = ki_new.upper()
print("Ki: %s (%s)" % (ki_new, "match" if (ki==ki_new) else ("DON'T match %s" % ki)))
else:
print("New Ki: Can't read, response code = %s" % (sw,))
# Verify EF.OP/OPC
((op_opc_type_new, op_opc_new), sw) = card.read_op_opc()
if sw == '9000':
op_opc_new = op_opc_new.upper()
print("%s: %s (%s)" % (op_opc_type_new, op_opc_new, "match" if (op_opc==op_opc_new) else ("DON'T match %s" % op_opc)))
else:
print("Ki: Can't read, response code = %s" % (sw,))
# Done with this card
print "Done !\n"
return sim_keys
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="An example utility to program Fairwaves SIM cards."
" Modify it to your own specific needs.")
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("-s", "--sim-db", dest="sim_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to load keys from (space searated)",
default="sim_db.dat",
)
parser.add_option("-o", "--out-db", dest="out_db_filename", type='string', metavar="FILE",
help="filename of a SIM DB to write keys to (space searated)",
default="out.csv",
)
parser.add_option("--batch", dest="batch",
help="Process SIM cards in batch mode - don't exit after programming and wait for the next SIM card to be inserted.",
default=False, action="store_true",
)
parser.add_option("--sound", dest="sound_file", type='string', metavar="SOUND_FILE",
help="Only in the batch mode. Play the given sound file on successful SIM programming",
)
parser.add_option("-n", "--name", dest="name",
help="Operator name [default: %default]",
default="Fairwaves",
)
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default=001,
)
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default=01,
)
parser.add_option("--op", dest="op",
help="Set OP to derive OPC from OP and KI [default: %default]",
default='00000000000000000000000000000000',
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
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)
# Create command layer
scc = SimCardCommands(transport=sl)
print("Loading SIM DB ...")
sim_db = load_sim_db(opts.sim_db_filename)
if opts.batch:
print("Batch mode enabled! Press Ctrl-C to exit")
# Loop once in non-batch mode and loop forever in batch mode
first_run = True
while first_run or opts.batch:
print("Insert a SIM card to program...")
sl.wait_for_card(newcardonly=not first_run)
first_run = False
card = card_autodetect(scc)
if card is None:
print("Card autodetect failed")
continue
print "Autodetected card type %s" % card.name
sim_keys = program_sim_card(card, sim_db, opts)
write_params_csv(opts.out_db_filename, sim_keys)
if opts.sound_file is not None and opts.sound_file != "":
subprocess.call(["paplay", opts.sound_file])

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
#
# Utility to deal with sim cards and program the 'magic' ones easily
@@ -33,13 +33,13 @@ import sys
try:
import json
except Importerror:
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes
from pySim.utils import h2b
from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn
def parse_options():
@@ -62,11 +62,20 @@ def parse_options():
help="Card type (user -t list to view) [default: %default]",
default="auto",
)
parser.add_option("-a", "--pin-adm", dest="pin_adm",
help="ADM PIN used for provisioning (overwrites default)",
)
parser.add_option("-e", "--erase", dest="erase", action='store_true',
help="Erase beforehand [default: %default]",
default=False,
)
parser.add_option("-S", "--source", dest="source",
help="Data Source[default: %default]",
default="cmdline",
)
# if mode is "cmdline"
parser.add_option("-n", "--name", dest="name",
help="Operator name [default: %default]",
default="Magic",
@@ -83,9 +92,12 @@ def parse_options():
help="Mobile Network Code [default: %default]",
default=55,
)
parser.add_option("-m", "--smsp", dest="smsp",
parser.add_option("-m", "--smsc", dest="smsc",
help="SMSP [default: '00 + country code + 5555']",
)
parser.add_option("-M", "--smsp", dest="smsp",
help="Raw SMSP content in hex [default: auto from SMSC]",
)
parser.add_option("-s", "--iccid", dest="iccid", metavar="ID",
help="Integrated Circuit Card ID",
@@ -96,7 +108,18 @@ def parse_options():
parser.add_option("-k", "--ki", dest="ki",
help="Ki (default is to randomize)",
)
parser.add_option("-o", "--opc", dest="opc",
help="OPC (default is to randomize)",
)
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("-z", "--secret", dest="secret", metavar="STR",
help="Secret used for ICCID/IMSI autogen",
)
@@ -111,12 +134,20 @@ def parse_options():
help="Optional batch state file",
)
# if mode is "csv"
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
help="Read parameters from CSV file rather than command line")
parser.add_option("--write-csv", dest="write_csv", metavar="FILE",
help="Append generated parameters in CSV file",
)
parser.add_option("--write-hlr", dest="write_hlr", metavar="FILE",
help="Append generated parameters to OpenBSC HLR sqlite3",
)
parser.add_option("--dry-run", dest="dry_run",
help="Perform a 'dry run', don't actually program the card",
default=False, action="store_true")
(options, args) = parser.parse_args()
@@ -125,6 +156,20 @@ def parse_options():
print kls.name
sys.exit(0)
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.read_csv is None:
parser.error("CSV mode requires a CSV input file")
elif options.source == 'cmdline':
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
parser.error("If either IMSI or ICCID isn't specified, num is required")
else:
parser.error("Only `cmdline' and `csv' sources supported")
if (options.read_csv is not None) and (options.source != 'csv'):
parser.error("You cannot specify a CSV input file in source != csv")
if (options.batch_mode) and (options.num is None):
options.num = 0
@@ -132,9 +177,6 @@ def parse_options():
if (options.imsi is not None) or (options.iccid is not None):
parser.error("Can't give ICCID/IMSI for batch mode, need to use automatic parameters ! see --num and --secret for more informations")
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
parser.error("If either IMSI or ICCID isn't specified, num is required")
if args:
parser.error("Extraneous arguments")
@@ -155,6 +197,10 @@ def _cc_digits(cc):
def _isnum(s, l=-1):
return s.isdigit() and ((l== -1) or (len(s) == l))
def _ishex(s, l=-1):
hc = '0123456789abcdef'
return all([x in hc for x in s.lower()]) and ((l== -1) or (len(s) == l))
def _dbi_binary_quote(s):
# Count usage of each char
@@ -174,7 +220,7 @@ def _dbi_binary_quote(s):
e = i
if m == 0: # No overhead ? use this !
break;
# Generate output
out = []
out.append( chr(e) ) # Offset
@@ -188,9 +234,8 @@ def _dbi_binary_quote(s):
return ''.join(out)
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
@@ -206,11 +251,11 @@ def gen_parameters(opts):
# Digitize MCC/MNC (5 or 6 digits)
plmn_digits = _mcc_mnc_digits(mcc, mnc)
# ICCID (20 digits)
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
if opts.iccid is not None:
iccid = opts.iccid
if not _isnum(iccid, 20):
raise ValueError('ICCID must be 20 digits !');
if not _isnum(iccid, 19):
raise ValueError('ICCID must be 19 digits !');
else:
if opts.num is None:
@@ -222,7 +267,7 @@ def gen_parameters(opts):
plmn_digits # MCC/MNC on 5/6 digits
)
ml = 20 - len(iccid)
ml = 18 - len(iccid)
if opts.secret is None:
# The raw number
@@ -231,6 +276,9 @@ def gen_parameters(opts):
# Randomized digits
iccid += _digits(opts.secret, 'ccid', ml, opts.num)
# Add checksum digit
iccid += ('%1d' % calculate_luhn(iccid))
# IMSI (15 digits usually)
if opts.imsi is not None:
imsi = opts.imsi
@@ -258,21 +306,68 @@ def gen_parameters(opts):
# SMSP
if opts.smsp is not None:
smsp = opts.smsp
if not _isnum(smsp):
raise ValueError('SMSP must be digits only !')
if not _ishex(smsp):
raise ValueError('SMSP must be hex digits only !')
if len(smsp) < 28*2:
raise ValueError('SMSP must be at least 28 bytes')
else:
smsp = '00%d' % opts.country + '5555' # Hack ...
if opts.smsc is not None:
smsc = opts.smsc
if not _isnum(smsc):
raise ValueError('SMSC must be digits only !')
else:
smsc = '00%d' % opts.country + '5555' # Hack ...
smsc = '%02d' % ((len(smsc) + 3)//2,) + "81" + swap_nibbles(rpad(smsc, 20))
smsp = (
'e1' + # Parameters indicator
'ff' * 12 + # TP-Destination address
smsc + # TP-Service Centre Address
'00' + # TP-Protocol identifier
'00' + # TP-Data coding scheme
'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
if not re.match('^[0-9a-fA-F]{32}$', ki):
raise ValueError('Ki needs to be 128 bits, in hex format')
else:
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
# OPC (random)
if opts.opc is not None:
opc = opts.opc
if not re.match('^[0-9a-fA-F]{32}$', opc):
raise ValueError('OPC needs to be 128 bits, in hex format')
elif opts.op is not None:
opc = derive_milenage_opc(ki, opts.op)
else:
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
if opts.pin_adm is not None:
pin_adm = opts.pin_adm
if not re.match('^([0-9a-fA-F][0-9a-fA-F])+$', pin_adm):
raise ValueError('ADM pin needs to be in hex format (even number of hex digits)')
else:
pin_adm = None
# Return that
return {
'name' : opts.name,
@@ -282,6 +377,9 @@ def gen_parameters(opts):
'imsi' : imsi,
'smsp' : smsp,
'ki' : ki,
'opc' : opc,
'acc' : acc,
'pin_adm' : pin_adm,
}
@@ -294,19 +392,49 @@ def print_parameters(params):
> MCC/MNC : %(mcc)d/%(mnc)d
> IMSI : %(imsi)s
> Ki : %(ki)s
> OPC : %(opc)s
> ACC : %(acc)s
""" % params
def write_parameters(opts, params):
# CSV
def write_params_csv(opts, params):
# csv
if opts.write_csv:
import csv
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki']
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
f = open(opts.write_csv, 'a')
cw = csv.writer(f)
cw.writerow([params[x] for x in row])
f.close()
def _read_params_csv(opts, imsi):
import csv
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
f = open(opts.read_csv, 'r')
cr = csv.DictReader(f, row)
i = 0
for row in cr:
if opts.num is not None and opts.read_imsi is False:
if opts.num == i:
f.close()
return row;
i += 1
if row['imsi'] == imsi:
f.close()
return row;
f.close()
return None
def read_params_csv(opts, imsi):
row = _read_params_csv(opts, imsi)
if row is not None:
row['mcc'] = int(row['mcc'])
row['mnc'] = int(row['mnc'])
return row
def write_params_hlr(opts, params):
# SQLite3 OpenBSC HLR
if opts.write_hlr:
import sqlite3
@@ -320,7 +448,7 @@ def write_parameters(opts, params):
[
params['imsi'],
params['name'],
'9' + params['iccid'][-5:]
'9' + params['iccid'][-5:-1]
],
)
sub_id = c.lastrowid
@@ -337,6 +465,10 @@ def write_parameters(opts, params):
conn.commit()
conn.close()
def write_parameters(opts, params):
write_params_csv(opts, params)
write_params_hlr(opts, params)
BATCH_STATE = [ 'name', 'country', 'mcc', 'mnc', 'smsp', 'secret', 'num' ]
BATCH_INCOMPATIBLE = ['iccid', 'imsi', 'ki']
@@ -432,37 +564,59 @@ if __name__ == '__main__':
done = False
first = True
card = None
while not done:
# Connect transport
print "Insert card now (or CTRL-C to cancel)"
sl.wait_for_card(newcardonly=not first)
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
# Get card
card = card_detect(opts, scc)
if card is None:
if opts.batch_mode:
first = False
continue
else:
sys.exit(-1)
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()
# Erase if requested
if opts.erase:
print "Formatting ..."
card.erase()
card.reset()
# Generate parameters
cp = gen_parameters(opts)
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(EF['IMSI'])
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)
# Program the card
print "Programming ..."
card.program(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)
@@ -477,4 +631,3 @@ if __name__ == '__main__':
if not opts.batch_mode:
done = True

126
pySim-read-all.py Executable file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python2
#
# Utility to display all files from 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
try:
import json
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_select_ef_response
from pySim.ts_51_011 import EF, DF
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,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
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)
# Create command layer
scc = SimCardCommands(transport=sl)
# Wait for SIM card
sl.wait_for_card()
# Program the card
print("Reading ...")
# Read all
for (name, path) in EF.items():
try:
resp = scc.select_file(path)
(length, file_id, file_type, increase_cmd, access_cond,
file_status, data_len, ef_struct, record_len) = dec_select_ef_response(resp[-1])
# print name, resp
print name, (length, file_id, file_type, increase_cmd, access_cond, file_status, data_len, ef_struct, record_len)
if not access_cond[0] == '0' and not access_cond[0] == '1':
print("%s: Requires %s access to read." % (name, access_cond[0],))
continue
if ef_struct == '00':
# transparent
(res, sw) = scc.read_binary_selected(length)
if sw == '9000':
print("%s: %s" % (name, res,))
else:
print("%s: Can't read, response code = %s" % (name, sw,))
elif (ef_struct == '01' or ef_struct == '03') and record_len>0:
for i in range(1,length/record_len+1):
# linear fixed
(res, sw) = scc.read_record_selected(record_len, i)
if sw == '9000':
print("%s[%d]: %s" % (name, i, res,))
else:
print("%s[%d]: Can't read, response code = %s" % (name, i, sw,))
elif ef_struct == '03':
# cyclic
raise RuntimeError("Don't know how to read a cyclic EF")
else:
raise RuntimeError("Unknown EF type")
except RuntimeError as e:
print("%s: Can't read (%s)" % (name,e.message,))
# Done for this card and maybe for everything ?
print "Done !\n"

148
pySim-read.py Executable file
View File

@@ -0,0 +1,148 @@
#!/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
try:
import json
except ImportError:
# Python < 2.5
import simplejson as json
from pySim.commands import SimCardCommands
from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid
from pySim.ts_51_011 import EF, DF
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,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
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)
# Create command layer
scc = SimCardCommands(transport=sl)
# Wait for SIM card
sl.wait_for_card()
# Program the card
print("Reading ...")
# EF.ICCID
(res, sw) = scc.read_binary(['3f00', '2fe2'])
if sw == '9000':
print("ICCID: %s" % (dec_iccid(res),))
else:
print("ICCID: Can't read, response code = %s" % (sw,))
# EF.IMSI
(res, sw) = scc.read_binary(['3f00', '7f20', '6f07'])
if sw == '9000':
print("IMSI: %s" % (dec_imsi(res),))
else:
print("IMSI: Can't read, response code = %s" % (sw,))
# EF.SMSP
(res, sw) = scc.read_record(['3f00', '7f10', '6f42'], 1)
if sw == '9000':
print("SMSP: %s" % (res,))
else:
print("SMSP: Can't read, response code = %s" % (sw,))
# EF.SPN
(res, sw) = scc.read_binary(EF['SPN'])
if sw == '9000':
print("SPN: %s" % (res,))
else:
print("SPN: Can't read, response code = %s" % (sw,))
# EF.HPLMN
(res, sw) = scc.read_binary(EF['PLMNsel'])
if sw == '9000':
print("HPLMN: %s" % (res))
# print("HPLMN: %s" % (dec_hplmn(res),))
else:
print("HPLMN: Can't read, response code = %s" % (sw,))
# FIXME
# EF.ACC
(res, sw) = scc.read_binary(['3f00', '7f20', '6f78'])
if sw == '9000':
print("ACC: %s" % (res,))
else:
print("ACC: Can't read, response code = %s" % (sw,))
# EF.MSISDN
try:
# print(scc.record_size(EF['MSISDN']))
(res, sw) = scc.read_record(EF['MSISDN'], 1)
if sw == '9000':
if res[1] != 'f':
print("MSISDN: %s" % (res,))
else:
print("MSISDN: %s (Not available)" % (res,))
else:
print("MSISDN: Can't read, response code = %s" % (sw,))
except:
print "MSISDN: Can't read. Probably not existing file"
# Done for this card and maybe for everything ?
print "Done !\n"

97
pySim-run-gsm.py Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python2
#
# Utility to run an A3/A8 algorithm on a SIM card
#
# Copyright (C) 2018 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 sys
from optparse import OptionParser
from pySim.commands import SimCardCommands
def parse_options():
parser = OptionParser(usage="usage: %prog [options]",
description="Utility to run an A3/A8 algorithm on a SIM card. "
"Prints generated SRES and Kc for a given RAND number "
"and exits.")
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("-r", "--rand", dest="rand", metavar="RAND",
help="16 bytes of RAND value",
default=None,
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
return options
if __name__ == '__main__':
# Parse options
opts = parse_options()
if opts.rand is None:
print("Please specify RAND value")
sys.exit(1)
if len(opts.rand) != 32:
print("RAND must be 16 bytes long")
sys.exit(1)
# Connect to the card
if opts.pcsc_dev is None:
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
else:
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
# Create command layer
scc = SimCardCommands(transport=sl)
# Wait for SIM card
sl.wait_for_card()
# Program the card
print("Running GSM algorithm with RAND %s" % (opts.rand,))
# Run GSM A3/A8
(res, sw) = scc.run_gsm(opts.rand)
if sw == '9000':
sres, kc = res
print("SRES = %s" % (sres,))
print("Kc = %s" % (kc,))
else:
print("Error %s, result data '%s'" % (sw, res))
# Done for this card and maybe for everything ?
print "Done !\n"

View File

@@ -0,0 +1 @@

View File

@@ -6,6 +6,8 @@
#
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
# Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,31 +23,82 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pySim.utils import b2h, swap_nibbles, rpad, lpad
from pySim.ts_51_011 import EF, DF
from pySim.utils import *
from smartcard.util import toBytes
class Card(object):
def __init__(self, scc):
self._scc = scc
def _e_iccid(self, iccid):
return swap_nibbles(iccid)
def _e_imsi(self, imsi):
"""Converts a string imsi into the value of the EF"""
l = (len(imsi) + 1) // 2 # Required bytes
oe = len(imsi) & 1 # Odd (1) / Even (0)
ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
return ei
def _e_plmn(self, mcc, mnc):
"""Converts integer MCC/MNC into 6 bytes for EF"""
return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))
self._adm_chv_num = 4
def reset(self):
self._scc.reset_card()
def verify_adm(self, key):
'''
Authenticate with ADM key
'''
(res, sw) = self._scc.verify_chv(self._adm_chv_num, key)
return sw
def read_iccid(self):
(res, sw) = self._scc.read_binary(EF['ICCID'])
if sw == '9000':
return (dec_iccid(res), sw)
else:
return (None, sw)
def read_imsi(self):
(res, sw) = self._scc.read_binary(EF['IMSI'])
if sw == '9000':
return (dec_imsi(res), sw)
else:
return (None, sw)
def update_imsi(self, imsi):
data, sw = self._scc.update_binary(EF['IMSI'], enc_imsi(imsi))
return sw
def update_acc(self, acc):
data, sw = self._scc.update_binary(EF['ACC'], lpad(acc, 4))
return sw
def update_hplmn_act(self, mcc, mnc, access_tech='FFFF'):
"""
Update Home PLMN with access technology bit-field
See Section "10.3.37 EFHPLMNwAcT (HPLMN Selector with Access Technology)"
in ETSI TS 151 011 for the details of the access_tech field coding.
Some common values:
access_tech = '0080' # Only GSM is selected
access_tech = 'FFFF' # All technologues selected, even Reserved for Future Use ones
"""
# get size and write EF.HPLMNwAcT
r = self._scc.select_file(EF['HPLMNwAcT'])
size = int(r[-1][4:8], 16)
hplmn = enc_plmn(mcc, mnc)
content = hplmn + access_tech
data, sw = self._scc.update_binary(EF['HPLMNwAcT'], content + 'ffffff0000' * (size/5-1))
return sw
def update_smsp(self, smsp):
data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))
return sw
def read_spn(self):
(spn, sw) = self._scc.read_binary(EF['SPN'])
if sw == '9000':
return (dec_spn(spn), sw)
else:
return (None, sw)
def update_spn(self, name, hplmn_disp=False, oplmn_disp=False):
content = enc_spn(name, hplmn_disp, oplmn_disp)
data, sw = self._scc.update_binary(EF['SPN'], rpad(content, 32))
return sw
class _MagicSimBase(Card):
"""
@@ -102,7 +155,7 @@ class _MagicSimBase(Card):
self._scc.select_file(['3f00', '7f4d'])
# Home PLMN in PLMN_Sel format
hplmn = self._e_plmn(p['mcc'], p['mnc'])
hplmn = enc_plmn(p['mcc'], p['mnc'])
# Operator name ( 3f00/7f4d/8f0c )
self._scc.update_record(self._files['name'][0], 2,
@@ -117,10 +170,10 @@ class _MagicSimBase(Card):
v += p['ki']
# ICCID
v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid'])
v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
# IMSI
v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi'])
v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
# Ki
if self._ki_file:
@@ -129,6 +182,12 @@ class _MagicSimBase(Card):
# PLMN_Sel
v+= '6f30' + '18' + rpad(hplmn, 36)
# ACC
# This doesn't work with "fake" SuperSIM cards,
# but will hopefully work with real SuperSIMs.
if p.get('acc') is not None:
v+= '6f78' + '02' + lpad(p['acc'], 4)
self._scc.update_record(self._files['b_ef'][0], 1,
rpad(v, self._files['b_ef'][1]*2)
)
@@ -140,7 +199,7 @@ class _MagicSimBase(Card):
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = self._e_plmn(p['mcc'], p['mnc'])
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
def erase(self):
@@ -226,7 +285,7 @@ class FakeMagicSim(Card):
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = self._e_plmn(p['mcc'], p['mnc'])
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
# Get total number of entries and entry size
@@ -236,12 +295,10 @@ class FakeMagicSim(Card):
entry = (
'81' + # 1b Status: Valid & Active
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
self._e_iccid(p['iccid']) + # 10b ICCID
self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
enc_iccid(p['iccid']) + # 10b ICCID
enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
p['ki'] + # 16b Ki
24*'f' + 'fd' + 24*'f' + # 25b (unknown ...)
rpad(p['smsp'], 20) + # 10b SMSP (padded with ff if needed)
10*'f' # 5b (unknown ...)
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
)
self._scc.update_record('000c', 1, entry)
@@ -255,5 +312,408 @@ class FakeMagicSim(Card):
self._scc.update_record('000c', 1+i, entry)
class GrcardSim(Card):
"""
Greencard (grcard.cn) HZCOS GSM SIM
These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
"""
name = 'grcardsim'
@classmethod
def autodetect(kls, scc):
return None
def program(self, p):
# We don't really know yet what ADM PIN 4 is about
#self._scc.verify_chv(4, h2b("4444444444444444"))
# Authenticate using ADM PIN 5
if p['pin_adm']:
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
self._scc.verify_chv(5, pin)
# EF.ICCID
r = self._scc.select_file(['3f00', '2fe2'])
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
# EF.IMSI
r = self._scc.select_file(['3f00', '7f20', '6f07'])
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
# EF.ACC
if p.get('acc') is not None:
data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
# EF.SMSP
r = self._scc.select_file(['3f00', '7f10', '6f42'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
# Set the Ki using proprietary command
pdu = '80d4020010' + p['ki']
data, sw = self._scc._tp.send_apdu(pdu)
# EF.HPLMN
r = self._scc.select_file(['3f00', '7f20', '6f30'])
size = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
# EF.SPN (Service Provider Name)
r = self._scc.select_file(['3f00', '7f20', '6f30'])
size = int(r[-1][4:8], 16)
# FIXME
# FIXME: EF.MSISDN
def erase(self):
return
class SysmoSIMgr1(GrcardSim):
"""
sysmocom sysmoSIM-GR1
These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
"""
name = 'sysmosim-gr1'
class SysmoUSIMgr1(Card):
"""
sysmocom sysmoUSIM-GR1
"""
name = 'sysmoUSIM-GR1'
@classmethod
def autodetect(kls, scc):
# TODO: Access the ATR
return None
def program(self, p):
# TODO: check if verify_chv could be used or what it needs
# self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
# Unlock the card..
data, sw = self._scc._tp.send_apdu_checksw("0020000A083332323133323332")
# TODO: move into SimCardCommands
par = ( p['ki'] + # 16b K
p['opc'] + # 32b OPC
enc_iccid(p['iccid']) + # 10b ICCID
enc_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
)
data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
def erase(self):
return
class SysmoSIMgr2(Card):
"""
sysmocom sysmoSIM-GR2
"""
name = 'sysmoSIM-GR2'
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68"):
return kls(scc)
except:
return None
return None
def program(self, p):
# select MF
r = self._scc.select_file(['3f00'])
# authenticate as SUPER ADM using default key
self._scc.verify_chv(0x0b, h2b("3838383838383838"))
# set ADM pin using proprietary command
# INS: D4
# P1: 3A for PIN, 3B for PUK
# P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
# P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
if p['pin_adm']:
pin = p['pin_adm']
else:
pin = h2b("4444444444444444")
pdu = 'A0D43A0508' + b2h(pin)
data, sw = self._scc._tp.send_apdu(pdu)
# authenticate as ADM (enough to write file, and can set PINs)
self._scc.verify_chv(0x05, pin)
# write EF.ICCID
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
# select DF_GSM
r = self._scc.select_file(['7f20'])
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
# write EF.ACC
if p.get('acc') is not None:
data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
# get size and write EF.HPLMN
r = self._scc.select_file(['6f30'])
size = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
# set COMP128 version 0 in proprietary file
data, sw = self._scc.update_binary('0001', '001000')
# set Ki in proprietary file
data, sw = self._scc.update_binary('0001', p['ki'], 3)
# select DF_TELECOM
r = self._scc.select_file(['3f00', '7f10'])
# write EF.SMSP
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
def erase(self):
return
class SysmoUSIMSJS1(Card):
"""
sysmocom sysmoUSIM-SJS1
"""
name = 'sysmoUSIM-SJS1'
def __init__(self, ssc):
super(SysmoUSIMSJS1, self).__init__(ssc)
self._scc.cla_byte = "00"
self._scc.sel_ctrl = "000C"
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5"):
return kls(scc)
except:
return None
return None
def program(self, p):
# authenticate as ADM using default key (written on the card..)
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
# select MF
r = self._scc.select_file(['3f00'])
# write EF.ICCID
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
# select DF_GSM
r = self._scc.select_file(['7f20'])
# set Ki in proprietary file
data, sw = self._scc.update_binary('00FF', p['ki'])
# set Ki in proprietary file
content = "01" + p['opc']
data, sw = self._scc.update_binary('00F7', content)
# write EF.IMSI
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
# EF.SMSP
r = self._scc.select_file(['3f00', '7f10'])
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 104), force_len=True)
def erase(self):
return
class FairwavesSIM(Card):
"""
FairwavesSIM
The SIM card is operating according to the standard.
For Ki/OP/OPC programming the following files are additionally open for writing:
3F00/7F20/FF01 OP/OPC:
byte 1 = 0x01, bytes 2-17: OPC;
byte 1 = 0x00, bytes 2-17: OP;
3F00/7F20/FF02: Ki
3F00/7F20/FF03: 2G/3G auth algorithm
byte 1 = GSM SIM A3/A8 algorithm selection
byte 2 = USIM A3/A8 algorithm selection
Algorithms:
0x01 = Milenage
0x03 = COMP128v1
0x06 = COMP128v2
0x07 = COMP128v3
"""
name = 'Fairwaves SIM'
# Propriatary files
_EF_num = {
'Ki': 'FF02',
'OP/OPC': 'FF01',
'A3A8': 'FF03',
}
_EF = {
'Ki': DF['GSM']+[_EF_num['Ki']],
'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
'A3A8': DF['GSM']+[_EF_num['A3A8']],
}
def __init__(self, ssc):
super(FairwavesSIM, self).__init__(ssc)
self._adm_chv_num = 0x11
self._adm2_chv_num = 0x12
@classmethod
def autodetect(kls, scc):
try:
# Look for ATR
if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 44 22 06 10 00 00 01 A9"):
return kls(scc)
except:
return None
return None
def verify_adm2(self, key):
'''
Authenticate with ADM2 key.
Fairwaves SIM cards support hierarchical key structure and ADM2 key
is a key which has access to proprietary files (Ki and OP/OPC).
That said, ADM key inherits permissions of ADM2 key and thus we rarely
need ADM2 key per se.
'''
(res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
return sw
def read_ki(self):
"""
Read Ki in proprietary file.
Requires ADM1 access level
"""
return self._scc.read_binary(self._EF['Ki'])
def update_ki(self, ki):
"""
Set Ki in proprietary file.
Requires ADM1 access level
"""
data, sw = self._scc.update_binary(self._EF['Ki'], ki)
return sw
def read_op_opc(self):
"""
Read Ki in proprietary file.
Requires ADM1 access level
"""
(ef, sw) = self._scc.read_binary(self._EF['OP/OPC'])
type = 'OP' if ef[0:2] == '00' else 'OPC'
return ((type, ef[2:]), sw)
def update_op(self, op):
"""
Set OP in proprietary file.
Requires ADM1 access level
"""
content = '00' + op
data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
return sw
def update_opc(self, opc):
"""
Set OPC in proprietary file.
Requires ADM1 access level
"""
content = '01' + opc
data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
return sw
def read_a3a8(self):
(ef, sw) = self._scc.read_binary(self._EF['A3A8'])
return (ef, sw)
def update_a3a8(self, content):
(ef, sw) = self._scc.update_binary(self._EF['A3A8'], content)
return (ef, sw)
def program(self, p):
# authenticate as ADM1
if not p['pin_adm']:
raise ValueError("Please provide a PIN-ADM as there is no default one")
sw = self.verify_adm(h2b(p['pin_adm']))
if sw != '9000':
raise RuntimeError('Failed to authenticate with ADM key %s'%(p['pin_adm'],))
# TODO: Set operator name
if p.get('smsp') is not None:
sw = self.update_smsp(p['smsp'])
if sw != '9000':
print("Programming SMSP failed with code %s"%sw)
# This SIM doesn't support changing ICCID
if p.get('mcc') is not None and p.get('mnc') is not None:
sw = self.update_hplmn_act(p['mcc'], p['mnc'])
if sw != '9000':
print("Programming MCC/MNC failed with code %s"%sw)
if p.get('imsi') is not None:
sw = self.update_imsi(p['imsi'])
if sw != '9000':
print("Programming IMSI failed with code %s"%sw)
if p.get('ki') is not None:
sw = self.update_ki(p['ki'])
if sw != '9000':
print("Programming Ki failed with code %s"%sw)
if p.get('opc') is not None:
sw = self.update_opc(p['opc'])
if sw != '9000':
print("Programming OPC failed with code %s"%sw)
if p.get('acc') is not None:
sw = self.update_acc(p['acc'])
if sw != '9000':
print("Programming ACC failed with code %s"%sw)
def erase(self):
return
# In order for autodetection ...
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim ]
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
FairwavesSIM ]
def card_autodetect(scc):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card is not None:
card.reset()
return card
return None

View File

@@ -28,28 +28,54 @@ from pySim.utils import rpad, b2h
class SimCardCommands(object):
def __init__(self, transport):
self._tp = transport;
self._cla_byte = "a0"
self.sel_ctrl = "0000"
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 select_file(self, dir_list):
rv = []
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_selected(self, length, offset=0):
pdu = self.cla_byte + 'b0%04x%02x' % (offset, (min(256, length) & 0xff))
return self._tp.send_apdu(pdu)
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)
return self.read_binary_selected(length, offset)
def update_binary(self, ef, data, offset=0):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
self.select_file(ef)
pdu = 'a0d6%04x%02x' % (offset, len(data)/2) + data
pdu = self.cla_byte + 'd6%04x%02x' % (offset, len(data)/2) + data
return self._tp.send_apdu_checksw(pdu)
def read_record_selected(self, rec_length, rec_no):
pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length)
return self._tp.send_apdu(pdu)
def read_record(self, ef, rec_no):
@@ -57,8 +83,7 @@ class SimCardCommands(object):
ef = [ef]
r = self.select_file(ef)
rec_length = int(r[-1][28:30], 16)
pdu = 'a0b2%02x04%02x' % (rec_no, rec_length)
return self._tp.send_apdu(pdu)
return self.read_record_selected(rec_length, rec_no)
def update_record(self, ef, rec_no, data, force_len=False):
if not hasattr(type(ef), '__iter__'):
@@ -70,8 +95,8 @@ class SimCardCommands(object):
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(pdu)
pdu = (self.cla_byte + 'dc%02x04%02x' % (rec_no, rec_length)) + data
return self._tp.send_apdu_checksw(pdu)
def record_size(self, ef):
r = self.select_file(ef)
@@ -81,15 +106,29 @@ class SimCardCommands(object):
r = self.select_file(ef)
return int(r[-1][4:8], 16) // int(r[-1][28:30], 16)
def run_gsm(self, rand):
def run_gsm_raw(self, rand):
'''
A3/A8 algorithm in the SIM card using the given RAND.
This function returns a raw result tuple.
'''
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 run_gsm(self, rand):
'''
A3/A8 algorithm in the SIM card using the given RAND.
This function returns a parsed ((SRES, Kc), sw) tuple.
'''
(res, sw) = self.run_gsm_raw(rand)
if sw != '9000':
return (res, sw)
return ((res[0:8], res[8:]), sw)
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('a02000' + ('%02x' % chv_no) + '08' + fc)
return self._tp.send_apdu_checksw(self.cla_byte + '2000' + ('%02X' % chv_no) + '08' + fc)

View File

@@ -56,6 +56,9 @@ class PcscSimLink(LinkBase):
except NoCardException:
raise NoCardError()
def get_atr(self):
return self._con.getATR()
def disconnect(self):
self._con.disconnect()

View File

@@ -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,18 +138,24 @@ 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.apend(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.apend(ord(b))
self._dbg_print("Historical = %x" % ord(b))
while True:
x = self._rx_byte()
if not x:
break
self._atr.apend(ord(x))
self._dbg_print("Extra: %x" % ord(x))
return 1

251
pySim/ts_51_011.py Normal file
View File

@@ -0,0 +1,251 @@
#!/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',
# 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']],
'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 named 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']],
}

View File

@@ -34,6 +34,12 @@ 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 not (x == 'f' and y == 'f') ])
def s2h(s):
return b2h(s)
def swap_nibbles(s):
return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
@@ -42,3 +48,87 @@ def rpad(s, l, c='f'):
def lpad(s, l, c='f'):
return c * (l - len(s)) + s
def enc_imsi(imsi):
"""Converts a string imsi into the value of the EF"""
l = (len(imsi) + 1) // 2 # Required bytes
oe = len(imsi) & 1 # Odd (1) / Even (0)
ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
return ei
def dec_imsi(ef):
"""Converts an EF value to the imsi string representation"""
if len(ef) < 4:
return None
l = int(ef[0:2], 16) * 2 # Length of the IMSI string
swapped = swap_nibbles(ef[2:])
oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
if oe:
l = l-1
if l+1 > len(swapped):
return None
imsi = swapped[1:l+2]
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"""
return swap_nibbles(lpad('%03d' % mcc, 3) + lpad('%02d' % mnc, 3))
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 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 dec_select_ef_response(response):
'''
As defined in the TS 151.011 9.2.1 SELECT
'''
length = int(response[4:8], 16)
file_id = response[8:12]
file_type = response[12:14]
increase_cmd = response[14:16]
access_cond = response[16:22]
file_status = response[22:24]
data_len = int(response[24:26], 16)
ef_struct = response[26:28]
if len(response) >= 30:
record_len = int(response[28:30], 16)
else:
record_len = 0
return (length, file_id, file_type, increase_cmd, access_cond, file_status, data_len, ef_struct, record_len)