15 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
11 changed files with 953 additions and 18 deletions

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])

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"

View File

@@ -37,7 +37,7 @@ except ImportError:
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():
@@ -107,13 +107,20 @@ if __name__ == '__main__':
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(['3f00', '7f20', '6f30'])
# if sw == '9000':
# print("HPLMN: %s" % (res))
(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,))
else:
print("HPLMN: Can't read, response code = %s" % (sw,))
# FIXME
# EF.ACC
@@ -125,13 +132,13 @@ if __name__ == '__main__':
# EF.MSISDN
try:
# print(scc.record_size(['3f00', '7f10', '6f40']))
(res, sw) = scc.read_record(['3f00', '7f10', '6f40'], 1)
# 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: Not available")
print("MSISDN: %s (Not available)" % (res,))
else:
print("MSISDN: Can't read, response code = %s" % (sw,))
except:

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

@@ -553,6 +553,14 @@ class FairwavesSIM(Card):
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'
@@ -560,10 +568,12 @@ class FairwavesSIM(Card):
_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):
@@ -647,6 +657,14 @@ class FairwavesSIM(Card):
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

View File

@@ -55,14 +55,17 @@ class SimCardCommands(object):
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 = self.cla_byte + 'b0%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__'):
@@ -71,13 +74,16 @@ class SimCardCommands(object):
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):
if not hasattr(type(ef), '__iter__'):
ef = [ef]
r = self.select_file(ef)
rec_length = int(r[-1][28:30], 16)
pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length)
return self._tp.send_apdu(pdu)
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__'):
@@ -100,12 +106,26 @@ 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(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()

View File

@@ -229,7 +229,7 @@ EF = {
'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
# 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']],

View File

@@ -78,7 +78,7 @@ def enc_iccid(iccid):
def enc_plmn(mcc, mnc):
"""Converts integer MCC/MNC into 3 bytes for EF"""
return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))
return swap_nibbles(lpad('%03d' % mcc, 3) + lpad('%02d' % mnc, 3))
def dec_spn(ef):
byte1 = int(ef[0:2])
@@ -113,3 +113,22 @@ 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 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)