aboutsummaryrefslogtreecommitdiffstats
path: root/pySim-prog.py
diff options
context:
space:
mode:
Diffstat (limited to 'pySim-prog.py')
-rwxr-xr-xpySim-prog.py304
1 files changed, 204 insertions, 100 deletions
diff --git a/pySim-prog.py b/pySim-prog.py
index d6d7ad5..b070c5e 100755
--- a/pySim-prog.py
+++ b/pySim-prog.py
@@ -30,6 +30,7 @@ import os
import random
import re
import sys
+import traceback
try:
import json
@@ -39,8 +40,10 @@ except ImportError:
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes
-from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn
+from pySim.utils import h2b, swap_nibbles, rpad, derive_milenage_opc, calculate_luhn, dec_iccid
from pySim.ts_51_011 import EF
+from pySim.card_handler import *
+from pySim.utils import *
def parse_options():
@@ -73,6 +76,9 @@ def parse_options():
parser.add_option("-a", "--pin-adm", dest="pin_adm",
help="ADM PIN used for provisioning (overwrites default)",
)
+ parser.add_option("-A", "--pin-adm-hex", dest="pin_adm_hex",
+ help="ADM PIN used for provisioning, as hex string (16 characters long",
+ )
parser.add_option("-e", "--erase", dest="erase", action='store_true',
help="Erase beforehand [default: %default]",
default=False,
@@ -124,10 +130,13 @@ def parse_options():
)
parser.add_option("--acc", dest="acc",
help="Set ACC bits (Access Control Code). not all card types are supported",
- )
+ )
parser.add_option("--read-imsi", dest="read_imsi", action="store_true",
help="Read the IMSI from the CARD", default=False
)
+ parser.add_option("--read-iccid", dest="read_iccid", action="store_true",
+ help="Read the ICCID from the CARD", default=False
+ )
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
help="Secret used for ICCID/IMSI autogen",
)
@@ -157,6 +166,9 @@ def parse_options():
help="Perform a 'dry run', don't actually program the card",
default=False, action="store_true")
+ parser.add_option("--card_handler", dest="card_handler", metavar="FILE",
+ help="Use automatic card handling machine")
+
(options, args) = parser.parse_args()
if options.type == 'list':
@@ -164,12 +176,12 @@ def parse_options():
print kls.name
sys.exit(0)
- if options.probe:
- return options
+ if options.probe:
+ return options
if options.source == 'csv':
- if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False):
- parser.error("CSV mode needs either an IMSI, --read-imsi or batch mode")
+ if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False) and (options.read_iccid is False):
+ parser.error("CSV mode needs either an IMSI, --read-imsi, --read-iccid or batch mode")
if options.read_csv is None:
parser.error("CSV mode requires a CSV input file")
elif options.source == 'cmdline':
@@ -275,7 +287,7 @@ def gen_parameters(opts):
iccid = (
'89' + # Common prefix (telecom)
cc_digits + # Country Code on 2/3 digits
- plmn_digits # MCC/MNC on 5/6 digits
+ plmn_digits # MCC/MNC on 5/6 digits
)
ml = 18 - len(iccid)
@@ -376,17 +388,27 @@ def gen_parameters(opts):
else:
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
+
+ pin_adm = None
+
if opts.pin_adm is not None:
if len(opts.pin_adm) <= 8:
pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
pin_adm = rpad(pin_adm, 16)
- elif len(opts.pin_adm) == 16:
- pin_adm = opts.pin_adm
- else:
- raise ValueError("PIN-ADM needs to be <=8 digits (ascii) or exactly 16 digits (raw hex)")
- else:
- pin_adm = None
+ else:
+ raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
+
+ if opts.pin_adm_hex is not None:
+ if len(opts.pin_adm_hex) == 16:
+ pin_adm = opts.pin_adm_hex
+ # Ensure that it's hex-encoded
+ try:
+ try_encode = h2b(pin_adm)
+ except ValueError:
+ raise ValueError("PIN-ADM needs to be hex encoded using this option")
+ else:
+ raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
# Return that
return {
@@ -405,16 +427,20 @@ def gen_parameters(opts):
def print_parameters(params):
- print """Generated card parameters :
- > Name : %(name)s
- > SMSP : %(smsp)s
- > ICCID : %(iccid)s
- > MCC/MNC : %(mcc)d/%(mnc)d
- > IMSI : %(imsi)s
- > Ki : %(ki)s
- > OPC : %(opc)s
- > ACC : %(acc)s
-""" % params
+ s = ["Generated card parameters :"]
+ if 'name' in params:
+ s.append(" > Name : %(name)s")
+ if 'smsp' in params:
+ s.append(" > SMSP : %(smsp)s")
+ s.append(" > ICCID : %(iccid)s")
+ s.append(" > MCC/MNC : %(mcc)s/%(mnc)s")
+ s.append(" > IMSI : %(imsi)s")
+ s.append(" > Ki : %(ki)s")
+ s.append(" > OPC : %(opc)s")
+ if 'acc' in params:
+ s.append(" > ACC : %(acc)s")
+ s.append(" > ADM1(hex): %(pin_adm)s")
+ print("\n".join(s) % params)
def write_params_csv(opts, params):
@@ -427,18 +453,27 @@ def write_params_csv(opts, params):
cw.writerow([params[x] for x in row])
f.close()
-def _read_params_csv(opts, imsi):
+def _read_params_csv(opts, iccid=None, imsi=None):
import csv
- row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
f = open(opts.read_csv, 'r')
- cr = csv.DictReader(f, row)
+ cr = csv.DictReader(f)
+
+ # Lower-case fieldnames
+ cr.fieldnames = [ field.lower() for field in cr.fieldnames ]
+
i = 0
+ if not 'iccid' in cr.fieldnames:
+ raise Exception("CSV file in wrong format!")
for row in cr:
- if opts.num is not None and opts.read_imsi is False:
+ if opts.num is not None and opts.read_iccid is False and opts.read_imsi is False:
if opts.num == i:
f.close()
return row;
i += 1
+ if row['iccid'] == iccid:
+ f.close()
+ return row;
+
if row['imsi'] == imsi:
f.close()
return row;
@@ -446,11 +481,36 @@ def _read_params_csv(opts, imsi):
f.close()
return None
-def read_params_csv(opts, imsi):
- row = _read_params_csv(opts, imsi)
+def read_params_csv(opts, imsi=None, iccid=None):
+ row = _read_params_csv(opts, iccid=iccid, imsi=imsi)
if row is not None:
- row['mcc'] = int(row['mcc'])
- row['mnc'] = int(row['mnc'])
+ row['mcc'] = row.get('mcc', mcc_from_imsi(row.get('imsi')))
+ row['mnc'] = row.get('mnc', mnc_from_imsi(row.get('imsi')))
+
+ pin_adm = None
+ # We need to escape the pin_adm we get from the csv
+ if 'pin_adm' in row:
+ pin_adm = ''.join(['%02x'%(ord(x)) for x in row['pin_adm']])
+ # Stay compatible to the odoo csv format
+ elif 'adm1' in row:
+ pin_adm = ''.join(['%02x'%(ord(x)) for x in row['adm1']])
+ if pin_adm:
+ row['pin_adm'] = rpad(pin_adm, 16)
+
+ # If the CSV-File defines a pin_adm_hex field use this field to
+ # generate pin_adm from that.
+ pin_adm_hex = row.get('pin_adm_hex')
+ if pin_adm_hex:
+ if len(pin_adm_hex) == 16:
+ row['pin_adm'] = pin_adm_hex
+ # Ensure that it's hex-encoded
+ try:
+ try_encode = h2b(pin_adm)
+ except ValueError:
+ raise ValueError("pin_adm_hex needs to be hex encoded using this option")
+ else:
+ raise ValueError("pin_adm_hex needs to be exactly 16 digits (hex encoded)")
+
return row
@@ -561,6 +621,74 @@ def card_detect(opts, scc):
return card
+def process_card(opts, first, card_handler):
+
+ if opts.dry_run is False:
+ # Connect transport
+ card_handler.get(first)
+
+ if opts.dry_run is False:
+ # Get card
+ card = card_detect(opts, scc)
+ if card is None:
+ print "No card detected!"
+ return -1
+
+ # Probe only
+ if opts.probe:
+ return 0
+
+ # Erase if requested
+ if opts.erase:
+ print "Formatting ..."
+ card.erase()
+ card.reset()
+
+ # Generate parameters
+ if opts.source == 'cmdline':
+ cp = gen_parameters(opts)
+ elif opts.source == 'csv':
+ imsi = None
+ iccid = None
+ if opts.read_iccid:
+ if opts.dry_run:
+ # Connect transport
+ card_handler.get(false)
+ (res,_) = scc.read_binary(['3f00', '2fe2'], length=10)
+ iccid = dec_iccid(res)
+ elif opts.read_imsi:
+ if opts.dry_run:
+ # Connect transport
+ card_handler.get(false)
+ (res,_) = scc.read_binary(EF['IMSI'])
+ imsi = swap_nibbles(res)[3:]
+ else:
+ imsi = opts.imsi
+ cp = read_params_csv(opts, imsi=imsi, iccid=iccid)
+ if cp is None:
+ print "Error reading parameters from CSV file!\n"
+ return 2
+ print_parameters(cp)
+
+ if opts.dry_run is False:
+ # Program the card
+ print "Programming ..."
+ card.program(cp)
+ else:
+ print "Dry Run: NOT PROGRAMMING!"
+
+ # Write parameters permanently
+ write_parameters(opts, cp)
+
+ # Batch mode state update and save
+ if opts.num is not None:
+ opts.num += 1
+ save_batch(opts)
+
+ card_handler.done()
+ return 0
+
+
if __name__ == '__main__':
# Parse options
@@ -568,93 +696,69 @@ if __name__ == '__main__':
# Init card reader driver
if opts.pcsc_dev is not None:
+ print("Using PC/SC reader (dev=%d) interface"
+ % opts.pcsc_dev)
from pySim.transport.pcsc import PcscSimLink
sl = PcscSimLink(opts.pcsc_dev)
elif opts.osmocon_sock is not None:
+ print("Using Calypso-based (OsmocomBB, sock=%s) reader interface"
+ % opts.osmocon_sock)
from pySim.transport.calypso import CalypsoSimLink
sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
else: # Serial reader is default
+ print("Using serial reader (port=%s, baudrate=%d) interface"
+ % (opts.device, opts.baudrate))
from pySim.transport.serial import SerialSimLink
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
# Create command layer
scc = SimCardCommands(transport=sl)
+ # If we use a CSV file as data input, check if the CSV file exists.
+ if opts.source == 'csv':
+ print "Using CSV file as data input: " + str(opts.read_csv)
+ if not os.path.isfile(opts.read_csv):
+ print "CSV file not found!"
+ sys.exit(1)
+
# Batch mode init
init_batch(opts)
+ if opts.card_handler:
+ card_handler = card_handler_auto(sl, opts.card_handler)
+ else:
+ card_handler = card_handler(sl)
+
# Iterate
- done = False
first = True
card = None
- while not done:
-
- if opts.dry_run is False:
- # Connect transport
- print "Insert card now (or CTRL-C to cancel)"
- sl.wait_for_card(newcardonly=not first)
+ while 1:
+ try:
+ rc = process_card(opts, first, card_handler)
+ except (KeyboardInterrupt):
+ print ""
+ print "Terminated by user!"
+ sys.exit(0)
+ except (SystemExit):
+ raise
+ except:
+ print ""
+ print "Card programming failed with an execption:"
+ print "---------------------8<---------------------"
+ traceback.print_exc()
+ print "---------------------8<---------------------"
+ print ""
+ rc = -1
+
+ # Something did not work as well as expected, however, lets
+ # make sure the card is pulled from the reader.
+ if rc != 0:
+ card_handler.error()
+
+ # If we are not in batch mode we are done in any case, so lets
+ # exit here.
+ if not opts.batch_mode:
+ sys.exit(rc)
- # Not the first anymore !
first = False
-
- if opts.dry_run is False:
- # Get card
- card = card_detect(opts, scc)
- if card is None:
- if opts.batch_mode:
- first = False
- continue
- else:
- sys.exit(-1)
-
- # Probe only
- if opts.probe:
- break;
-
- # Erase if requested
- if opts.erase:
- print "Formatting ..."
- card.erase()
- card.reset()
-
- # Generate parameters
- if opts.source == 'cmdline':
- cp = gen_parameters(opts)
- elif opts.source == 'csv':
- if opts.read_imsi:
- if opts.dry_run:
- # Connect transport
- print "Insert card now (or CTRL-C to cancel)"
- sl.wait_for_card(newcardonly=not first)
- (res,_) = scc.read_binary(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)
-
- if opts.dry_run is False:
- # Program the card
- print "Programming ..."
- if opts.dry_run is not True:
- card.program(cp)
- else:
- print "Dry Run: NOT PROGRAMMING!"
-
- # Write parameters permanently
- write_parameters(opts, cp)
-
- # Batch mode state update and save
- if opts.num is not None:
- opts.num += 1
- save_batch(opts)
-
- # Done for this card and maybe for everything ?
- print "Done !\n"
-
- if not opts.batch_mode:
- done = True