5 Commits

Author SHA1 Message Date
Holger Hans Peter Freyther
44e4636755 re-program: Instead of specifying the IMSI, read it from the card. 2012-08-15 21:09:01 +02:00
Harald Welte
93315bd466 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.
2012-08-15 15:26:30 +02:00
Harald Welte
69c2ce2525 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'
2012-08-15 15:25:51 +02:00
Harald Welte
8b59a55488 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
2012-08-13 20:19:09 +02:00
Harald Welte
1d5968cfcf split parameter writing for CSV and SQL into separate functions 2012-08-13 16:50:28 +02:00
8 changed files with 139 additions and 1118 deletions

View File

@@ -1,153 +0,0 @@
#!/usr/bin/env python
#
# Utility to write the cards
#
#
# Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
#
# 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
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes
def card_detect(opts, scc):
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if opts.type in ("auto", "auto_once"):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card:
print "Autodetected card type %s" % card.name
card.reset()
break
if card is None:
print "Autodetection failed"
return
if opts.type == "auto_once":
opts.type = card.name
elif opts.type in ctypes:
card = ctypes[opts.type](scc)
else:
raise ValueError("Unknown card type %s" % opts.type)
return card
#
# Main
#
def parse_options():
parser = OptionParser(usage="usage: %prog [options]")
# Card interface
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("-t", "--type", dest="type",
help="Card type (user -t list to view) [default: %default]",
default="auto",
)
(options, args) = parser.parse_args()
if options.type == 'list':
for kls in _cards_classes:
print kls.name
sys.exit(0)
if args:
parser.error("Extraneous arguments")
return options
def 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)
# Iterate
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)
# 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)
# Check type
if card.name != 'fakemagicsim':
print "Can't fix this type of card ..."
continue
# Fix record
data, sw = scc.read_record(['000c'], 1)
data_new = data[0:100] + 'fffffffffffffffffffffffffdffffffffffffffffffffffff0791947106004034ffffffffffffff'
scc.update_record(['000c'], 1, data_new)
# Done for this card and maybe for everything ?
print "Card should be fixed now !\n"
if __name__ == '__main__':
main()

View File

@@ -1,254 +0,0 @@
#!/usr/bin/env python
#
# Utility to generate the HLR
#
#
# Copyright (C) 2010 Sylvain Munaut <tnt@246tNt.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
from ccc import StateManager, CardParametersGenerator, isnum
from pySim.utils import h2b, swap_nibbles, rpad
#
# OpenBSC HLR Writing
#
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 hlr_write_cards(filename, network, cards):
import sqlite3
conn = sqlite3.connect(filename)
for card in cards:
c = conn.execute(
'INSERT INTO Subscriber ' +
'(imsi, name, extension, authorized, created, updated) ' +
'VALUES ' +
'(?,?,?,1,datetime(\'now\'),datetime(\'now\'));',
[
card.imsi,
'%s #%d' % (network.name, card.num),
'9%05d' % card.num,
],
)
sub_id = c.lastrowid
c.close()
c = conn.execute(
'INSERT INTO AuthKeys ' +
'(subscriber_id, algorithm_id, a3a8_ki)' +
'VALUES ' +
'(?,?,?)',
[ sub_id, 2, sqlite3.Binary(_dbi_binary_quote(h2b(card.ki))) ],
)
c.close()
conn.commit()
conn.close()
#
# CSV Writing
#
def csv_write_cards(filename, network, cards):
import csv
fh = open(filename, 'a')
cw = csv.writer(fh)
cw.writerows(cards)
fh.close()
#
# Main stuff
#
def parse_options():
parser = OptionParser(usage="usage: %prog [options]")
# Network parameters
parser.add_option("-n", "--name", dest="name",
help="Operator name [default: %default]",
default="CCC Event",
)
parser.add_option("-c", "--country", dest="country", type="int", metavar="CC",
help="Country code [default: %default]",
default=49,
)
parser.add_option("-x", "--mcc", dest="mcc", type="int",
help="Mobile Country Code [default: %default]",
default=262,
)
parser.add_option("-y", "--mnc", dest="mnc", type="int",
help="Mobile Network Code [default: %default]",
default=42,
)
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]",
)
# Autogen
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
help="Secret used for ICCID/IMSI autogen",
)
parser.add_option("-k", "--count", dest="count", type="int", metavar="CNT",
help="Number of entried to generate [default: %default]",
default=1000,
)
# Output
parser.add_option("--state", dest="state_file", metavar="FILE",
help="Use this state file",
)
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",
)
(options, args) = parser.parse_args()
if args:
parser.error("Extraneous arguments")
# Check everything
if 1 < len(options.name) > 16:
parser.error("Name must be between 1 and 16 characters")
if 0 < options.country > 999:
parser.error("Invalid country code")
if 0 < options.mcc > 999:
parser.error("Invalid Mobile Country Code (MCC)")
if 0 < options.mnc > 999:
parser.error("Invalid Mobile Network Code (MNC)")
# SMSP
if options.smsp is not None:
smsp = options.smsp
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:
if options.smsc is not None:
smsc = options.smsc
if not _isnum(smsc):
raise ValueError('SMSC must be digits only !')
else:
smsc = '00%d' % options.country + '5555' # Hack ...
smsc = '%02d' % ((len(smsc) + 3)//2,) + "81" + swap_nibbles(rpad(smsc, 20))
options.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
)
return options
def main():
# Parse options
opts = parse_options()
# Load state
sm = StateManager(opts.state_file, opts)
sm.load()
# Instanciate generator
np = sm.network
cpg = CardParametersGenerator(np.cc, np.mcc, np.mnc, sm.get_secret())
# Generate cards
imsis = set()
cards = []
while len(cards) < opts.count:
# Next number
i = sm.next_gen_num()
# Generate card number
cp = cpg.generate(i)
# Check for dupes
if cp.imsi in imsis:
continue
imsis.add(cp.imsi)
# Collect
cards.append(cp)
# Save cards
if opts.write_hlr:
hlr_write_cards(opts.write_hlr, np, cards)
if opts.write_csv:
csv_write_cards(opts.write_csv, np, cards)
# Save state
sm.save()
if __name__ == '__main__':
main()

View File

@@ -1,213 +0,0 @@
#!/usr/bin/env python
#
# Utility to write the cards
#
#
# Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
#
# 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
from pySim.commands import SimCardCommands
from pySim.cards import _cards_classes
from ccc import StateManager, CardParameters
def csv_load_cards(filename):
import csv
fh = open(filename, 'r')
cr = csv.reader(fh)
cards = dict([(int(x[0]), CardParameters(int(x[0]), x[1], x[2], x[3])) for x in cr])
fh.close()
return cards
def card_detect(opts, scc):
# Detect type if needed
card = None
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
if opts.type in ("auto", "auto_once"):
for kls in _cards_classes:
card = kls.autodetect(scc)
if card:
print "Autodetected card type %s" % card.name
card.reset()
break
if card is None:
print "Autodetection failed"
return
if opts.type == "auto_once":
opts.type = card.name
elif opts.type in ctypes:
card = ctypes[opts.type](scc)
else:
raise ValueError("Unknown card type %s" % opts.type)
return card
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
""" % params
#
# Main
#
def parse_options():
parser = OptionParser(usage="usage: %prog [options]")
# Card interface
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("-t", "--type", dest="type",
help="Card type (user -t list to view) [default: %default]",
default="auto",
)
parser.add_option("-e", "--erase", dest="erase", action='store_true',
help="Erase beforehand [default: %default]",
default=False,
)
# Data source
parser.add_option("--state", dest="state_file", metavar="FILE",
help="Use this state file",
)
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
help="Read parameters from CSV file",
)
(options, args) = parser.parse_args()
if options.type == 'list':
for kls in _cards_classes:
print kls.name
sys.exit(0)
if args:
parser.error("Extraneous arguments")
return options
def 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)
# Load state
sm = StateManager(opts.state_file)
sm.load()
np = sm.network
# Load cards
cards = csv_load_cards(opts.read_csv)
# Iterate
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)
# 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)
# Erase if requested
if opts.erase:
print "Formatting ..."
card.erase()
card.reset()
# Get parameters
cp = cards[sm.next_write_num()]
cpp = {
'name': np.name,
'smsp': np.smsp,
'iccid': cp.iccid,
'mcc': np.mcc,
'mnc': np.mnc,
'imsi': cp.imsi,
'ki': cp.ki,
}
print_parameters(cpp)
# Program the card
print "Programming ..."
card.program(cpp)
# Update state
sm.save()
# Done for this card and maybe for everything ?
print "Card written !\n"
if __name__ == '__main__':
main()

193
ccc.py
View File

@@ -1,193 +0,0 @@
#!/usr/bin/env python
#
# CCC Event HLR management common stuff
#
#
# Copyright (C) 2010 Sylvain Munaut <tnt@246tNt.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
import os
import random
from collections import namedtuple
try:
import json
except Importerror:
# Python < 2.5
import simplejson as json
#
# Various helpers
#
def isnum(s, l=-1):
return s.isdigit() and ((l== -1) or (len(s) == l))
#
# Storage tuples
#
CardParameters = namedtuple("CardParameters", "num iccid imsi ki")
NetworkParameters = namedtuple("NetworkParameters", "name cc mcc mnc smsp")
#
# State management
#
class StateManager(object):
def __init__(self, filename=None, options=None):
# Filename for state storage
self._filename = filename
# Params from options
self._net_name = options.name if options else None
self._net_cc = options.country if options else None
self._net_mcc = options.mcc if options else None
self._net_mnc = options.mnc if options else None
self._net_smsp = options.smsp if options else None
self._secret = options.secret if options else None
# Default
self._num_gen = 0
self._num_write = 0
def load(self):
# Skip if no state file
if self._filename is None:
return
# Skip if doesn't exist yet
if not os.path.isfile(self._filename):
return
# Read
fh = open(self._filename, 'r')
data = fh.read()
fh.close()
# Decode json and merge
dd = json.loads(data)
self._net_name = dd['name']
self._net_cc = dd['cc']
self._net_mcc = dd['mcc']
self._net_mnc = dd['mnc']
self._net_smsp = dd['smsp']
self._secret = dd['secret']
self._num_gen = dd['num_gen']
self._num_write = dd['num_write']
def save(self):
# Skip if no state file
if self._filename is None:
return
# Serialize
data = json.dumps({
'name': self._net_name,
'cc': self._net_cc,
'mcc': self._net_mcc,
'mnc': self._net_mnc,
'smsp': self._net_smsp,
'secret': self._secret,
'num_gen': self._num_gen,
'num_write': self._num_write,
})
# Save in json
fh = open(self._filename, 'w')
fh.write(data)
fh.close()
@property
def network(self):
return NetworkParameters(
self._net_name,
self._net_cc,
self._net_mcc,
self._net_mnc,
self._net_smsp,
)
def get_secret(self):
return self._secret
def next_gen_num(self):
n = self._num_gen
self._num_gen += 1
return n
def next_write_num(self):
n = self._num_write
self._num_write += 1
return n
#
# Card parameters generation
#
class CardParametersGenerator(object):
def __init__(self, cc, mcc, mnc, secret):
# Digitize country code (2 or 3 digits)
self._cc_digits = ('%03d' if cc > 100 else '%02d') % cc
# Digitize MCC/MNC (5 or 6 digits)
self._plmn_digits = ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
# Store secret
self._secret = secret
def _digits(self, usage, len_, num):
s = hashlib.sha1(self._secret + usage + '%d' % num)
d = ''.join(['%02d'%ord(x) for x in s.digest()])
return d[0:len_]
def _gen_iccid(self, num):
iccid = (
'89' + # Common prefix (telecom)
self._cc_digits + # Country Code on 2/3 digits
self._plmn_digits # MCC/MNC on 5/6 digits
)
ml = 20 - len(iccid)
iccid += self._digits('ccid', ml, num)
return iccid
def _gen_imsi(self, num):
ml = 15 - len(self._plmn_digits)
msin = self._digits('imsi', ml, num)
return (
self._plmn_digits + # MCC/MNC on 5/6 digits
msin # MSIN
)
def _gen_ki(self):
return ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
def generate(self, num):
return CardParameters(
num,
self._gen_iccid(num),
self._gen_imsi(num),
self._gen_ki(),
)

View File

@@ -67,6 +67,12 @@ def parse_options():
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",
@@ -105,8 +111,8 @@ def parse_options():
parser.add_option("--op", dest="op",
help="Set OP to derive OPC from OP and KI",
)
parser.add_option("--acc", dest="acc",
help="Set ACC bits (Access Control Code). not all card types are supported",
parser.add_option("--read-imsi", dest="read_imsi", action="store_true",
help="Read the IMSI from the CARD", default=False
)
@@ -124,12 +130,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()
@@ -138,6 +152,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
@@ -145,9 +173,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")
@@ -319,17 +344,6 @@ def gen_parameters(opts):
'00' # TP-Validity period
)
# ACC
if opts.acc is not None:
acc = opts.acc
if not _ishex(acc):
raise ValueError('ACC must be hex digits only !')
if len(acc) != 2*2:
raise ValueError('ACC must be exactly 2 bytes')
else:
acc = None
# Ki (random)
if opts.ki is not None:
ki = opts.ki
@@ -360,7 +374,6 @@ def gen_parameters(opts):
'smsp' : smsp,
'ki' : ki,
'opc' : opc,
'acc' : acc,
}
@@ -374,12 +387,11 @@ def print_parameters(params):
> 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', 'opc']
@@ -388,6 +400,34 @@ def write_parameters(opts, params):
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
@@ -401,7 +441,7 @@ def write_parameters(opts, params):
[
params['imsi'],
params['name'],
'9' + params['iccid'][-5:]
'9' + params['iccid'][-5:-1]
],
)
sub_id = c.lastrowid
@@ -418,6 +458,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']
@@ -515,35 +559,57 @@ if __name__ == '__main__':
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(['3f00', '7f20', '6f07'])
imsi = swap_nibbles(res)[3:]
else:
imsi = opts.imsi
cp = read_params_csv(opts, imsi)
if cp is None:
print "Error reading parameters\n"
sys.exit(2)
print_parameters(cp)
# 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)

View File

@@ -1,141 +0,0 @@
#!/usr/bin/env python
#
# 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
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.HPLMN
# (res, sw) = scc.read_binary(['3f00', '7f20', '6f30'])
# 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(['3f00', '7f10', '6f40']))
(res, sw) = scc.read_record(['3f00', '7f10', '6f40'], 1)
if sw == '9000':
if res[1] != 'f':
print("MSISDN: %s" % (res,))
else:
print("MSISDN: Not available")
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"

View File

@@ -22,7 +22,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad, enc_imsi, enc_iccid, enc_plmn
from pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad
class Card(object):
@@ -30,6 +30,20 @@ class Card(object):
def __init__(self, scc):
self._scc = scc
def _e_iccid(self, iccid):
return swap_nibbles(rpad(iccid, 20))
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))
def reset(self):
self._scc.reset_card()
@@ -89,7 +103,7 @@ class _MagicSimBase(Card):
self._scc.select_file(['3f00', '7f4d'])
# Home PLMN in PLMN_Sel format
hplmn = enc_plmn(p['mcc'], p['mnc'])
hplmn = self._e_plmn(p['mcc'], p['mnc'])
# Operator name ( 3f00/7f4d/8f0c )
self._scc.update_record(self._files['name'][0], 2,
@@ -104,10 +118,10 @@ class _MagicSimBase(Card):
v += p['ki']
# ICCID
v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid'])
# IMSI
v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi'])
# Ki
if self._ki_file:
@@ -116,12 +130,6 @@ 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)
)
@@ -133,7 +141,7 @@ class _MagicSimBase(Card):
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
hplmn = self._e_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
def erase(self):
@@ -219,7 +227,7 @@ class FakeMagicSim(Card):
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
hplmn = self._e_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
# Get total number of entries and entry size
@@ -229,8 +237,8 @@ class FakeMagicSim(Card):
entry = (
'81' + # 1b Status: Valid & Active
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
enc_iccid(p['iccid']) + # 10b ICCID
enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
self._e_iccid(p['iccid']) + # 10b ICCID
self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
p['ki'] + # 16b Ki
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
)
@@ -245,7 +253,6 @@ class FakeMagicSim(Card):
for i in range(0, rec_cnt):
self._scc.update_record('000c', 1+i, entry)
class GrcardSim(Card):
"""
Greencard (grcard.cn) HZCOS GSM SIM
@@ -268,15 +275,15 @@ class GrcardSim(Card):
# EF.ICCID
r = self._scc.select_file(['3f00', '2fe2'])
data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
data, sw = self._scc.update_binary('2fe2', self._e_iccid(p['iccid']))
# EF.IMSI
r = self._scc.select_file(['3f00', '7f20', '6f07'])
data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
data, sw = self._scc.update_binary('6f07', self._e_imsi(p['imsi']))
# EF.ACC
if p.get('acc') is not None:
data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
#r = self._scc.select_file(['3f00', '7f20', '6f78'])
#self._scc.update_binary('6f78', self._e_imsi(p['imsi'])
# EF.SMSP
r = self._scc.select_file(['3f00', '7f10', '6f42'])
@@ -289,7 +296,7 @@ class GrcardSim(Card):
# EF.HPLMN
r = self._scc.select_file(['3f00', '7f20', '6f30'])
size = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
hplmn = self._e_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
# EF.SPN (Service Provider Name)
@@ -310,6 +317,7 @@ class SysmoSIMgr1(GrcardSim):
"""
name = 'sysmosim-gr1'
# In order for autodetection ...
class SysmoUSIMgr1(Card):
"""
@@ -330,82 +338,14 @@ class SysmoUSIMgr1(Card):
# 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
p['opc'] + # 32b OPC
self._e_iccid(p['iccid']) + # 10b ICCID
self._e_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):
# TODO: look for ATR 3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68
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)
pdu = 'A0D43A0508' + "4444444444444444"
data, sw = self._scc._tp.send_apdu(pdu)
# authenticate as ADM (enough to write file, and can set PINs)
self._scc.verify_chv(0x05, h2b("4444444444444444"))
# 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
# In order for autodetection ...
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1 ]
SysmoSIMgr1, SysmoUSIMgr1 ]

View File

@@ -42,34 +42,3 @@ 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]) * 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 6 bytes for EF"""
return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))