Files
pysim-local/pySim/cards.py
Philipp Maier 4146086d2f Fix select control parameter
sysmo-usim-sjs1 requires P2 to be set to 0x0C (request FCI) when
using the USIM application commands. The FCI is not used by pysim
anyway and might even cause problems with other cards.

This commit adds a pair of get/set methods to the SimCardCommands
class in order to set a default for the selection control
parameters (P1, P2). (Similar to the set/get methods for the class
byte)

The SysmoUSIMSJS1 class now calls the setter method for the
selection control parameters inside of its constructuor and sets
the selection control parameter default to "000C". This way we
can be sure that we only change the behaviour for sysmo-usim-sjs1
and do not break support for any other cards.

Change-Id: I1993a267c952bf37d5de1cb4e1107f445614c17b
2017-04-06 00:38:22 +02:00

472 lines
11 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" pySim: Card programmation logic
"""
#
# 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
# 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 pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad, enc_imsi, enc_iccid, enc_plmn
class Card(object):
def __init__(self, scc):
self._scc = scc
def reset(self):
self._scc.reset_card()
class _MagicSimBase(Card):
"""
Theses cards uses several record based EFs to store the provider infos,
each possible provider uses a specific record number in each EF. The
indexes used are ( where N is the number of providers supported ) :
- [2 .. N+1] for the operator name
- [1 .. N] for the programable EFs
* 3f00/7f4d/8f0c : Operator Name
bytes 0-15 : provider name, padded with 0xff
byte 16 : length of the provider name
byte 17 : 01 for valid records, 00 otherwise
* 3f00/7f4d/8f0d : Programmable Binary EFs
* 3f00/7f4d/8f0e : Programmable Record EFs
"""
@classmethod
def autodetect(kls, scc):
try:
for p, l, t in kls._files.values():
if not t:
continue
if scc.record_size(['3f00', '7f4d', p]) != l:
return None
except:
return None
return kls(scc)
def _get_count(self):
"""
Selects the file and returns the total number of entries
and entry size
"""
f = self._files['name']
r = self._scc.select_file(['3f00', '7f4d', f[0]])
rec_len = int(r[-1][28:30], 16)
tlen = int(r[-1][4:8],16)
rec_cnt = (tlen / rec_len) - 1;
if (rec_cnt < 1) or (rec_len != f[1]):
raise RuntimeError('Bad card type')
return rec_cnt
def program(self, p):
# Go to dir
self._scc.select_file(['3f00', '7f4d'])
# Home PLMN in PLMN_Sel format
hplmn = enc_plmn(p['mcc'], p['mnc'])
# Operator name ( 3f00/7f4d/8f0c )
self._scc.update_record(self._files['name'][0], 2,
rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01'
)
# ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
v = ''
# inline Ki
if self._ki_file is None:
v += p['ki']
# ICCID
v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
# IMSI
v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
# Ki
if self._ki_file:
v += self._ki_file + '10' + p['ki']
# 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)
)
# SMSP ( 3f00/7f4d/8f0e )
# FIXME
# Write PLMN_Sel forcefully as well
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
def erase(self):
# Dummy
df = {}
for k, v in self._files.iteritems():
ofs = 1
fv = v[1] * 'ff'
if k == 'name':
ofs = 2
fv = fv[0:-4] + '0000'
df[v[0]] = (fv, ofs)
# Write
for n in range(0,self._get_count()):
for k, (msg, ofs) in df.iteritems():
self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
class SuperSim(_MagicSimBase):
name = 'supersim'
_files = {
'name' : ('8f0c', 18, True),
'b_ef' : ('8f0d', 74, True),
'r_ef' : ('8f0e', 50, True),
}
_ki_file = None
class MagicSim(_MagicSimBase):
name = 'magicsim'
_files = {
'name' : ('8f0c', 18, True),
'b_ef' : ('8f0d', 130, True),
'r_ef' : ('8f0e', 102, False),
}
_ki_file = '6f1b'
class FakeMagicSim(Card):
"""
Theses cards have a record based EF 3f00/000c that contains the provider
informations. See the program method for its format. The records go from
1 to N.
"""
name = 'fakemagicsim'
@classmethod
def autodetect(kls, scc):
try:
if scc.record_size(['3f00', '000c']) != 0x5a:
return None
except:
return None
return kls(scc)
def _get_infos(self):
"""
Selects the file and returns the total number of entries
and entry size
"""
r = self._scc.select_file(['3f00', '000c'])
rec_len = int(r[-1][28:30], 16)
tlen = int(r[-1][4:8],16)
rec_cnt = (tlen / rec_len) - 1;
if (rec_cnt < 1) or (rec_len != 0x5a):
raise RuntimeError('Bad card type')
return rec_cnt, rec_len
def program(self, p):
# Home PLMN
r = self._scc.select_file(['3f00', '7f20', '6f30'])
tl = int(r[-1][4:8], 16)
hplmn = enc_plmn(p['mcc'], p['mnc'])
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
# Get total number of entries and entry size
rec_cnt, rec_len = self._get_infos()
# Set first entry
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
p['ki'] + # 16b Ki
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
)
self._scc.update_record('000c', 1, entry)
def erase(self):
# Get total number of entries and entry size
rec_cnt, rec_len = self._get_infos()
# Erase all entries
entry = 'ff' * rec_len
for i in range(0, rec_cnt):
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"
self._scc.sel_ctrl = "000C"
@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']))
def erase(self):
return
# In order for autodetection ...
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1 ]