mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-19 03:48:34 +03:00
Compare commits
26 Commits
27c3
...
zecke/tmp2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d56eb30eea | ||
|
|
533a0dce3a | ||
|
|
29474b0d5b | ||
|
|
800c9eb097 | ||
|
|
73b686f7ee | ||
|
|
2fc205ceb9 | ||
|
|
9f13897408 | ||
|
|
7be92ff5d2 | ||
|
|
6e58914746 | ||
|
|
5e96c3d910 | ||
|
|
21885249cf | ||
|
|
5da8d4e0d4 | ||
|
|
053c89578c | ||
|
|
cca41795a7 | ||
|
|
93b38cd0f5 | ||
|
|
4d91bf449f | ||
|
|
8ca49e9ca8 | ||
|
|
9977c86e96 | ||
|
|
607ce2a029 | ||
|
|
1a914439b8 | ||
|
|
e10394bfb6 | ||
|
|
2c0ff3a167 | ||
|
|
5dffefbf0c | ||
|
|
50e7c03816 | ||
|
|
3156d9073f | ||
|
|
982a3075f9 |
2
README
2
README
@@ -29,6 +29,8 @@ from pySim.commands import SimCardCommands
|
|||||||
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
|
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
|
||||||
sc = SimCardCommands(sl)
|
sc = SimCardCommands(sl)
|
||||||
|
|
||||||
|
sl.wait_for_card()
|
||||||
|
|
||||||
# Print IMSI
|
# Print IMSI
|
||||||
print sc.read_binary(['3f00', '7f20', '6f07'])
|
print sc.read_binary(['3f00', '7f20', '6f07'])
|
||||||
|
|
||||||
|
|||||||
123
pySim-prog.py
123
pySim-prog.py
@@ -33,13 +33,13 @@ import sys
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
except Importerror:
|
except ImportError:
|
||||||
# Python < 2.5
|
# Python < 2.5
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
from pySim.commands import SimCardCommands
|
from pySim.commands import SimCardCommands
|
||||||
from pySim.cards import _cards_classes
|
from pySim.cards import _cards_classes
|
||||||
from pySim.utils import h2b
|
from pySim.utils import h2b, swap_nibbles, rpad
|
||||||
|
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
@@ -62,6 +62,9 @@ def parse_options():
|
|||||||
help="Card type (user -t list to view) [default: %default]",
|
help="Card type (user -t list to view) [default: %default]",
|
||||||
default="auto",
|
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',
|
parser.add_option("-e", "--erase", dest="erase", action='store_true',
|
||||||
help="Erase beforehand [default: %default]",
|
help="Erase beforehand [default: %default]",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -83,9 +86,12 @@ def parse_options():
|
|||||||
help="Mobile Network Code [default: %default]",
|
help="Mobile Network Code [default: %default]",
|
||||||
default=55,
|
default=55,
|
||||||
)
|
)
|
||||||
parser.add_option("-m", "--smsp", dest="smsp",
|
parser.add_option("-m", "--smsc", dest="smsc",
|
||||||
help="SMSP [default: '00 + country code + 5555']",
|
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",
|
parser.add_option("-s", "--iccid", dest="iccid", metavar="ID",
|
||||||
help="Integrated Circuit Card ID",
|
help="Integrated Circuit Card ID",
|
||||||
@@ -96,6 +102,16 @@ def parse_options():
|
|||||||
parser.add_option("-k", "--ki", dest="ki",
|
parser.add_option("-k", "--ki", dest="ki",
|
||||||
help="Ki (default is to randomize)",
|
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("-z", "--secret", dest="secret", metavar="STR",
|
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
|
||||||
help="Secret used for ICCID/IMSI autogen",
|
help="Secret used for ICCID/IMSI autogen",
|
||||||
@@ -155,6 +171,10 @@ def _cc_digits(cc):
|
|||||||
def _isnum(s, l=-1):
|
def _isnum(s, l=-1):
|
||||||
return s.isdigit() and ((l== -1) or (len(s) == l))
|
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):
|
def _dbi_binary_quote(s):
|
||||||
# Count usage of each char
|
# Count usage of each char
|
||||||
@@ -174,7 +194,7 @@ def _dbi_binary_quote(s):
|
|||||||
e = i
|
e = i
|
||||||
if m == 0: # No overhead ? use this !
|
if m == 0: # No overhead ? use this !
|
||||||
break;
|
break;
|
||||||
|
|
||||||
# Generate output
|
# Generate output
|
||||||
out = []
|
out = []
|
||||||
out.append( chr(e) ) # Offset
|
out.append( chr(e) ) # Offset
|
||||||
@@ -188,9 +208,26 @@ def _dbi_binary_quote(s):
|
|||||||
|
|
||||||
return ''.join(out)
|
return ''.join(out)
|
||||||
|
|
||||||
|
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 derive_milenage_opc(ki_hex, op_hex):
|
||||||
|
"""
|
||||||
|
Run the milenage algorithm.
|
||||||
|
"""
|
||||||
|
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 gen_parameters(opts):
|
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"""
|
options given by the user"""
|
||||||
|
|
||||||
# MCC/MNC
|
# MCC/MNC
|
||||||
@@ -206,11 +243,11 @@ def gen_parameters(opts):
|
|||||||
# Digitize MCC/MNC (5 or 6 digits)
|
# Digitize MCC/MNC (5 or 6 digits)
|
||||||
plmn_digits = _mcc_mnc_digits(mcc, mnc)
|
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:
|
if opts.iccid is not None:
|
||||||
iccid = opts.iccid
|
iccid = opts.iccid
|
||||||
if not _isnum(iccid, 20):
|
if not _isnum(iccid, 19):
|
||||||
raise ValueError('ICCID must be 20 digits !');
|
raise ValueError('ICCID must be 19 digits !');
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if opts.num is None:
|
if opts.num is None:
|
||||||
@@ -222,7 +259,7 @@ def gen_parameters(opts):
|
|||||||
plmn_digits # MCC/MNC on 5/6 digits
|
plmn_digits # MCC/MNC on 5/6 digits
|
||||||
)
|
)
|
||||||
|
|
||||||
ml = 20 - len(iccid)
|
ml = 18 - len(iccid)
|
||||||
|
|
||||||
if opts.secret is None:
|
if opts.secret is None:
|
||||||
# The raw number
|
# The raw number
|
||||||
@@ -231,6 +268,9 @@ def gen_parameters(opts):
|
|||||||
# Randomized digits
|
# Randomized digits
|
||||||
iccid += _digits(opts.secret, 'ccid', ml, opts.num)
|
iccid += _digits(opts.secret, 'ccid', ml, opts.num)
|
||||||
|
|
||||||
|
# Add checksum digit
|
||||||
|
iccid += ('%1d' % calculate_luhn(iccid))
|
||||||
|
|
||||||
# IMSI (15 digits usually)
|
# IMSI (15 digits usually)
|
||||||
if opts.imsi is not None:
|
if opts.imsi is not None:
|
||||||
imsi = opts.imsi
|
imsi = opts.imsi
|
||||||
@@ -258,21 +298,69 @@ def gen_parameters(opts):
|
|||||||
# SMSP
|
# SMSP
|
||||||
if opts.smsp is not None:
|
if opts.smsp is not None:
|
||||||
smsp = opts.smsp
|
smsp = opts.smsp
|
||||||
if not _isnum(smsp):
|
if not _ishex(smsp):
|
||||||
raise ValueError('SMSP must be digits only !')
|
raise ValueError('SMSP must be hex digits only !')
|
||||||
|
if len(smsp) < 28*2:
|
||||||
|
raise ValueError('SMSP must be at least 28 bytes')
|
||||||
|
|
||||||
else:
|
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)
|
# Ki (random)
|
||||||
if opts.ki is not None:
|
if opts.ki is not None:
|
||||||
ki = opts.ki
|
ki = opts.ki
|
||||||
if not re.match('^[0-9a-fA-F]{32}$', ki):
|
if not re.match('^[0-9a-fA-F]{32}$', ki):
|
||||||
raise ValueError('Ki needs to be 128 bits, in hex format')
|
raise ValueError('Ki needs to be 128 bits, in hex format')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||||
|
|
||||||
|
# Ki (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:
|
||||||
|
if len(opts.pin_adm) > 8:
|
||||||
|
raise ValueError("PIN-ADM needs to be <=8 digits")
|
||||||
|
pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
|
||||||
|
pin_adm = rpad(pin_adm, 16)
|
||||||
|
else:
|
||||||
|
pin_adm = None
|
||||||
|
|
||||||
|
|
||||||
# Return that
|
# Return that
|
||||||
return {
|
return {
|
||||||
'name' : opts.name,
|
'name' : opts.name,
|
||||||
@@ -282,6 +370,9 @@ def gen_parameters(opts):
|
|||||||
'imsi' : imsi,
|
'imsi' : imsi,
|
||||||
'smsp' : smsp,
|
'smsp' : smsp,
|
||||||
'ki' : ki,
|
'ki' : ki,
|
||||||
|
'opc' : opc,
|
||||||
|
'acc' : acc,
|
||||||
|
'pin_adm' : pin_adm,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -294,6 +385,8 @@ def print_parameters(params):
|
|||||||
> MCC/MNC : %(mcc)d/%(mnc)d
|
> MCC/MNC : %(mcc)d/%(mnc)d
|
||||||
> IMSI : %(imsi)s
|
> IMSI : %(imsi)s
|
||||||
> Ki : %(ki)s
|
> Ki : %(ki)s
|
||||||
|
> OPC : %(opc)s
|
||||||
|
> ACC : %(acc)s
|
||||||
""" % params
|
""" % params
|
||||||
|
|
||||||
|
|
||||||
@@ -301,7 +394,7 @@ def write_parameters(opts, params):
|
|||||||
# CSV
|
# CSV
|
||||||
if opts.write_csv:
|
if opts.write_csv:
|
||||||
import 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')
|
f = open(opts.write_csv, 'a')
|
||||||
cw = csv.writer(f)
|
cw = csv.writer(f)
|
||||||
cw.writerow([params[x] for x in row])
|
cw.writerow([params[x] for x in row])
|
||||||
@@ -432,7 +525,7 @@ if __name__ == '__main__':
|
|||||||
done = False
|
done = False
|
||||||
first = True
|
first = True
|
||||||
card = None
|
card = None
|
||||||
|
|
||||||
while not done:
|
while not done:
|
||||||
# Connect transport
|
# Connect transport
|
||||||
print "Insert card now (or CTRL-C to cancel)"
|
print "Insert card now (or CTRL-C to cancel)"
|
||||||
|
|||||||
141
pySim-read.py
Executable file
141
pySim-read.py
Executable file
@@ -0,0 +1,141 @@
|
|||||||
|
#!/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"
|
||||||
267
pySim/cards.py
267
pySim/cards.py
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||||
|
# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
from pySim.utils import b2h, swap_nibbles, rpad, lpad
|
from pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad, enc_imsi, enc_iccid, enc_plmn
|
||||||
|
|
||||||
|
|
||||||
class Card(object):
|
class Card(object):
|
||||||
@@ -29,20 +30,6 @@ class Card(object):
|
|||||||
def __init__(self, scc):
|
def __init__(self, scc):
|
||||||
self._scc = 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))
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._scc.reset_card()
|
self._scc.reset_card()
|
||||||
|
|
||||||
@@ -102,7 +89,7 @@ class _MagicSimBase(Card):
|
|||||||
self._scc.select_file(['3f00', '7f4d'])
|
self._scc.select_file(['3f00', '7f4d'])
|
||||||
|
|
||||||
# Home PLMN in PLMN_Sel format
|
# 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 )
|
# Operator name ( 3f00/7f4d/8f0c )
|
||||||
self._scc.update_record(self._files['name'][0], 2,
|
self._scc.update_record(self._files['name'][0], 2,
|
||||||
@@ -117,10 +104,10 @@ class _MagicSimBase(Card):
|
|||||||
v += p['ki']
|
v += p['ki']
|
||||||
|
|
||||||
# ICCID
|
# ICCID
|
||||||
v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid'])
|
v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
|
||||||
|
|
||||||
# IMSI
|
# IMSI
|
||||||
v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi'])
|
v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
|
||||||
|
|
||||||
# Ki
|
# Ki
|
||||||
if self._ki_file:
|
if self._ki_file:
|
||||||
@@ -129,6 +116,12 @@ class _MagicSimBase(Card):
|
|||||||
# PLMN_Sel
|
# PLMN_Sel
|
||||||
v+= '6f30' + '18' + rpad(hplmn, 36)
|
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,
|
self._scc.update_record(self._files['b_ef'][0], 1,
|
||||||
rpad(v, self._files['b_ef'][1]*2)
|
rpad(v, self._files['b_ef'][1]*2)
|
||||||
)
|
)
|
||||||
@@ -140,7 +133,7 @@ class _MagicSimBase(Card):
|
|||||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||||
tl = int(r[-1][4:8], 16)
|
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))
|
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
|
||||||
|
|
||||||
def erase(self):
|
def erase(self):
|
||||||
@@ -226,7 +219,7 @@ class FakeMagicSim(Card):
|
|||||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||||
tl = int(r[-1][4:8], 16)
|
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))
|
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
|
||||||
|
|
||||||
# Get total number of entries and entry size
|
# Get total number of entries and entry size
|
||||||
@@ -236,12 +229,10 @@ class FakeMagicSim(Card):
|
|||||||
entry = (
|
entry = (
|
||||||
'81' + # 1b Status: Valid & Active
|
'81' + # 1b Status: Valid & Active
|
||||||
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
|
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
|
||||||
self._e_iccid(p['iccid']) + # 10b ICCID
|
enc_iccid(p['iccid']) + # 10b ICCID
|
||||||
self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
|
enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
|
||||||
p['ki'] + # 16b Ki
|
p['ki'] + # 16b Ki
|
||||||
24*'f' + 'fd' + 24*'f' + # 25b (unknown ...)
|
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
|
||||||
rpad(p['smsp'], 20) + # 10b SMSP (padded with ff if needed)
|
|
||||||
10*'f' # 5b (unknown ...)
|
|
||||||
)
|
)
|
||||||
self._scc.update_record('000c', 1, entry)
|
self._scc.update_record('000c', 1, entry)
|
||||||
|
|
||||||
@@ -255,5 +246,229 @@ class FakeMagicSim(Card):
|
|||||||
self._scc.update_record('000c', 1+i, entry)
|
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):
|
||||||
|
# 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)
|
||||||
|
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"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def autodetect(kls, scc):
|
||||||
|
# TODO: look for ATR 3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5
|
||||||
|
return None
|
||||||
|
|
||||||
|
def program(self, p):
|
||||||
|
|
||||||
|
|
||||||
|
# select MF
|
||||||
|
r = self._scc.select_file(['3f00'])
|
||||||
|
|
||||||
|
# select DF_GSM
|
||||||
|
r = self._scc.select_file(['7f20'])
|
||||||
|
|
||||||
|
# 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']))
|
||||||
|
|
||||||
|
|
||||||
|
# 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']))
|
||||||
|
|
||||||
|
# write EF.AUTH
|
||||||
|
content = "0101"
|
||||||
|
r = self._scc.select_file(['7FCC', '6f00'])
|
||||||
|
data, sw = self._scc.update_binary('6f00', content)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def erase(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# In order for autodetection ...
|
# In order for autodetection ...
|
||||||
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim ]
|
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
|
||||||
|
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1 ]
|
||||||
|
|||||||
@@ -28,11 +28,20 @@ from pySim.utils import rpad, b2h
|
|||||||
class SimCardCommands(object):
|
class SimCardCommands(object):
|
||||||
def __init__(self, transport):
|
def __init__(self, transport):
|
||||||
self._tp = transport;
|
self._tp = transport;
|
||||||
|
self._cla_byte = "a0"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cla_byte(self):
|
||||||
|
return self._cla_byte
|
||||||
|
@cla_byte.setter
|
||||||
|
def cla_byte(self, value):
|
||||||
|
self._cla_byte = value
|
||||||
|
|
||||||
|
|
||||||
def select_file(self, dir_list):
|
def select_file(self, dir_list):
|
||||||
rv = []
|
rv = []
|
||||||
for i in dir_list:
|
for i in dir_list:
|
||||||
data, sw = self._tp.send_apdu_checksw("a0a4000002" + i)
|
data, sw = self._tp.send_apdu_checksw(self.cla_byte + "a4000C02" + i)
|
||||||
rv.append(data)
|
rv.append(data)
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
@@ -42,22 +51,22 @@ class SimCardCommands(object):
|
|||||||
r = self.select_file(ef)
|
r = self.select_file(ef)
|
||||||
if length is None:
|
if length is None:
|
||||||
length = int(r[-1][4:8], 16) - offset
|
length = int(r[-1][4:8], 16) - offset
|
||||||
pdu = 'a0b0%04x%02x' % (offset, (min(256, length) & 0xff))
|
pdu = self.cla_byte + 'b0%04x%02x' % (offset, (min(256, length) & 0xff))
|
||||||
return self._tp.send_apdu(pdu)
|
return self._tp.send_apdu(pdu)
|
||||||
|
|
||||||
def update_binary(self, ef, data, offset=0):
|
def update_binary(self, ef, data, offset=0):
|
||||||
if not hasattr(type(ef), '__iter__'):
|
if not hasattr(type(ef), '__iter__'):
|
||||||
ef = [ef]
|
ef = [ef]
|
||||||
self.select_file(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(pdu)
|
return self._tp.send_apdu_checksw(pdu)
|
||||||
|
|
||||||
def read_record(self, ef, rec_no):
|
def read_record(self, ef, rec_no):
|
||||||
if not hasattr(type(ef), '__iter__'):
|
if not hasattr(type(ef), '__iter__'):
|
||||||
ef = [ef]
|
ef = [ef]
|
||||||
r = self.select_file(ef)
|
r = self.select_file(ef)
|
||||||
rec_length = int(r[-1][28:30], 16)
|
rec_length = int(r[-1][28:30], 16)
|
||||||
pdu = 'a0b2%02x04%02x' % (rec_no, rec_length)
|
pdu = self.cla_byte + 'b2%02x04%02x' % (rec_no, rec_length)
|
||||||
return self._tp.send_apdu(pdu)
|
return self._tp.send_apdu(pdu)
|
||||||
|
|
||||||
def update_record(self, ef, rec_no, data, force_len=False):
|
def update_record(self, ef, rec_no, data, force_len=False):
|
||||||
@@ -70,8 +79,8 @@ class SimCardCommands(object):
|
|||||||
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data)/2))
|
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data)/2))
|
||||||
else:
|
else:
|
||||||
rec_length = len(data)/2
|
rec_length = len(data)/2
|
||||||
pdu = ('a0dc%02x04%02x' % (rec_no, rec_length)) + data
|
pdu = (self.cla_byte + 'dc%02x04%02x' % (rec_no, rec_length)) + data
|
||||||
return self._tp.send_apdu(pdu)
|
return self._tp.send_apdu_checksw(pdu)
|
||||||
|
|
||||||
def record_size(self, ef):
|
def record_size(self, ef):
|
||||||
r = self.select_file(ef)
|
r = self.select_file(ef)
|
||||||
@@ -85,11 +94,11 @@ class SimCardCommands(object):
|
|||||||
if len(rand) != 32:
|
if len(rand) != 32:
|
||||||
raise ValueError('Invalid rand')
|
raise ValueError('Invalid rand')
|
||||||
self.select_file(['3f00', '7f20'])
|
self.select_file(['3f00', '7f20'])
|
||||||
return self._tp.send_apdu('a088000010' + rand)
|
return self._tp.send_apdu(self.cla_byte + '88000010' + rand)
|
||||||
|
|
||||||
def reset_card(self):
|
def reset_card(self):
|
||||||
return self._tp.reset_card()
|
return self._tp.reset_card()
|
||||||
|
|
||||||
def verify_chv(self, chv_no, code):
|
def verify_chv(self, chv_no, code):
|
||||||
fc = rpad(b2h(code), 16)
|
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)
|
||||||
@@ -42,3 +42,34 @@ def rpad(s, l, c='f'):
|
|||||||
|
|
||||||
def lpad(s, l, c='f'):
|
def lpad(s, l, c='f'):
|
||||||
return c * (l - len(s)) + s
|
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))
|
||||||
|
|||||||
Reference in New Issue
Block a user