mirror of https://gerrit.osmocom.org/pysim
290 lines
8.3 KiB
Python
Executable File
290 lines
8.3 KiB
Python
Executable File
#!/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])
|