blob: 6d49572aa9369b7125c963a654492d916a08d9f3 [file] [log] [blame]
Harald Weltef44256c2021-10-14 15:53:39 +02001# coding=utf-8
2"""Utilities / Functions related to sysmocom SJA2 cards
3
4(C) 2021 by Harald Welte <laforge@osmocom.org>
5
6This program is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program. If not, see <http://www.gnu.org/licenses/>.
18"""
19
20from pytlv.TLV import *
21from struct import pack, unpack
22from pySim.utils import *
23from pySim.filesystem import *
24from pySim.ts_102_221 import CardProfileUICC
25from pySim.construct import *
26from construct import *
27import pySim
28
29key_type2str = {
30 0: 'kic',
31 1: 'kid',
32 2: 'kik',
33 3: 'any',
34}
35
36key_algo2str = {
37 0: 'des',
38 1: 'aes'
39}
40
41mac_length = {
42 0: 8,
43 1: 4
44}
45
46class EF_PIN(TransparentEF):
47 def __init__(self, fid, name):
48 super().__init__(fid, name=name, desc='%s PIN file' % name)
49 def _decode_bin(self, raw_bin_data):
50 u = unpack('!BBB8s', raw_bin_data[:11])
51 res = {'enabled': (True, False)[u[0] & 0x01],
52 'initialized': (True, False)[u[0] & 0x02],
53 'disable_able': (False, True)[u[0] & 0x10],
54 'unblock_able': (False, True)[u[0] & 0x20],
55 'change_able': (False, True)[u[0] & 0x40],
56 'valid': (False, True)[u[0] & 0x80],
57 'attempts_remaining': u[1],
58 'maximum_attempts': u[2],
59 'pin': u[3].hex(),
60 }
61 if len(raw_bin_data) == 21:
62 u2 = unpack('!BB8s', raw_bin_data[11:10])
63 res['attempts_remaining_puk'] = u2[0]
64 res['maximum_attempts_puk'] = u2[1]
65 res['puk'] = u2[2].hex()
66 return res
67
68class EF_MILENAGE_CFG(TransparentEF):
69 def __init__(self, fid='6f21', name='EF.MILENAGE_CFG', desc='Milenage connfiguration'):
70 super().__init__(fid, name=name, desc=desc)
71 def _decode_bin(self, raw_bin_data):
72 u = unpack('!BBBBB16s16s16s16s16s', raw_bin_data)
73 return {'r1': u[0], 'r2': u[1], 'r3': u[2], 'r4': u[3], 'r5': u[4],
74 'c1': u[5].hex(),
75 'c2': u[6].hex(),
76 'c3': u[7].hex(),
77 'c4': u[8].hex(),
78 'c5': u[9].hex(),
79 }
80
81class EF_0348_KEY(LinFixedEF):
82 def __init__(self, fid='6f22', name='EF.0348_KEY', desc='TS 03.48 OTA Keys'):
83 super().__init__(fid, name=name, desc=desc, rec_len={27,35})
84 def _decode_record_bin(self, raw_bin_data):
85 u = unpack('!BBB', raw_bin_data[0:3])
86 key_algo = (u[2] >> 6) & 1
87 key_length = ((u[2] >> 3) & 3) * 8
88 return {'sec_domain': u[0],
89 'key_set_version': u[1],
90 'key_type': key_type2str[u[2] & 3],
91 'key_length': key_length,
92 'algorithm': key_algo2str[key_algo],
93 'mac_length': mac_length[(u[2] >> 7)],
94 'key': raw_bin_data[3:key_length].hex()
95 }
96
97class EF_0348_COUNT(LinFixedEF):
98 def __init__(self, fid='6f23', name='EF.0348_COUNT', desc='TS 03.48 OTA Counters'):
99 super().__init__(fid, name=name, desc=desc, rec_len={7,7})
100 def _decode_record_bin(self, raw_bin_data):
101 u = unpack('!BB5s', raw_bin_data)
102 return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2]}
103
104class EF_SIM_AUTH_COUNTER(TransparentEF):
105 def __init__(self, fid='af24', name='EF.SIM_AUTH_COUNTER'):
106 super().__init__(fid, name=name, desc='Number of remaining RUN GSM ALGORITHM executions')
107 self._construct = Struct('num_run_gsm_algo_remain'/Int32ub)
108
109class EF_GP_COUNT(LinFixedEF):
110 def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'):
111 super().__init__(fid, name=name, desc=desc, rec_len={5,5})
112 def _decode_record_bin(self, raw_bin_data):
113 u = unpack('!BBHB', raw_bin_data)
114 return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2], 'rfu': u[3]}
115
116class EF_GP_DIV_DATA(LinFixedEF):
117 def __init__(self, fid='6f27', name='EF.GP_DIV_DATA', desc='GP SCP02 key diversification data'):
118 super().__init__(fid, name=name, desc=desc, rec_len={12,12})
119 def _decode_record_bin(self, raw_bin_data):
120 u = unpack('!BB8s', raw_bin_data)
121 return {'sec_domain': u[0], 'key_set_version': u[1], 'key_div_data': u[2].hex()}
122
123class EF_SIM_AUTH_KEY(TransparentEF):
124 def __init__(self, fid='6f20', name='EF.SIM_AUTH_KEY'):
125 super().__init__(fid, name=name, desc='USIM authentication key')
126 CfgByte = BitStruct(Bit[2],
127 'use_sres_deriv_func_2'/Bit,
128 'use_opc_instead_of_op'/Bit,
129 'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
130 self._construct = Struct('cfg'/CfgByte,
131 'key'/Bytes(16),
132 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
133 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
134 )
135
136class DF_SYSTEM(CardDF):
137 def __init__(self):
138 super().__init__(fid='a515', name='DF.SYSTEM', desc='CardOS specifics')
139 files = [
140 EF_PIN('6f01', 'EF.CHV1'),
141 EF_PIN('6f81', 'EF.CHV2'),
142 EF_PIN('6f0a', 'EF.ADM1'),
143 EF_PIN('6f0b', 'EF.ADM2'),
144 EF_PIN('6f0c', 'EF.ADM3'),
145 EF_PIN('6f0d', 'EF.ADM4'),
146 EF_MILENAGE_CFG(),
147 EF_0348_KEY(),
148 EF_SIM_AUTH_COUNTER(),
149 EF_SIM_AUTH_KEY(),
150 EF_0348_COUNT(),
151 EF_GP_COUNT(),
152 EF_GP_DIV_DATA(),
153 ]
154 self.add_files(files)
155
156 def decode_select_response(self, resp_hex):
Philipp Maier5998a3a2021-11-16 15:16:39 +0100157 return pySim.ts_102_221.CardProfileUICC.decode_select_response(resp_hex)
Harald Weltef44256c2021-10-14 15:53:39 +0200158
159class EF_USIM_SQN(TransparentEF):
160 def __init__(self, fid='af30', name='EF.USIM_SQN'):
161 super().__init__(fid, name=name, desc='SQN parameters for AKA')
162 Flag1 = BitStruct('skip_next_sqn_check'/Bit, 'delta_max_check'/Bit,
163 'age_limit_check'/Bit, 'sqn_check'/Bit,
164 'ind_len'/BitsInteger(4))
165 Flag2 = BitStruct('rfu'/BitsRFU(5), 'dont_clear_amf_for_macs'/Bit,
166 'aus_concealed'/Bit, 'autn_concealed'/Bit)
167 self._construct = Struct('flag1'/Flag1, 'flag2'/Flag2,
168 'delta_max'/BytesInteger(6), 'age_limit'/BytesInteger(6),
Harald Welteeb458382021-10-16 10:44:23 +0200169 'freshness'/GreedyRange(BytesInteger(6)))
Harald Weltef44256c2021-10-14 15:53:39 +0200170
171class EF_USIM_AUTH_KEY(TransparentEF):
172 def __init__(self, fid='af20', name='EF.USIM_AUTH_KEY'):
173 super().__init__(fid, name=name, desc='USIM authentication key')
174 CfgByte = BitStruct(Bit, 'only_4bytes_res_in_3g'/Bit,
175 'use_sres_deriv_func_2_in_3g'/Bit,
176 'use_opc_instead_of_op'/Bit,
177 'algorithm'/Enum(Nibble, milenage=4, sha1_aka=5, xor=15))
178 self._construct = Struct('cfg'/CfgByte,
179 'key'/Bytes(16),
180 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
181 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
182 )
183class EF_USIM_AUTH_KEY_2G(TransparentEF):
184 def __init__(self, fid='af22', name='EF.USIM_AUTH_KEY_2G'):
185 super().__init__(fid, name=name, desc='USIM authentication key in 2G context')
186 CfgByte = BitStruct(Bit, 'only_4bytes_res_in_3g'/Bit,
187 'use_sres_deriv_func_2_in_3g'/Bit,
188 'use_opc_instead_of_op'/Bit,
189 'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3))
190 self._construct = Struct('cfg'/CfgByte,
191 'key'/Bytes(16),
192 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)),
193 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16))
194 )
195class EF_GBA_SK(TransparentEF):
196 def __init__(self, fid='af31', name='EF.GBA_SK'):
197 super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
198 self._construct = GreedyBytes
199
200class EF_GBA_REC_LIST(TransparentEF):
201 def __init__(self, fid='af32', name='EF.GBA_REC_LIST'):
202 super().__init__(fid, name=name, desc='Secret key for GBA key derivation')
203 # integers representing record numbers in EF-GBANL
204 self._construct = GreedyRange(Int8ub)
205
206class EF_GBA_INT_KEY(LinFixedEF):
207 def __init__(self, fid='af33', name='EF.GBA_INT_KEY'):
208 super().__init__(fid, name=name, desc='Secret key for GBA key derivation', rec_len={32,32})
209 self._construct = GreedyBytes
210
211
212
213class SysmocomSJA2(CardModel):
214 _atrs = [ "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9",
215 "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2",
216 "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5" ]
217 @classmethod
218 def add_files(cls, rs:RuntimeState):
219 """Add sysmocom SJA2 specific files to given RuntimeState."""
220 rs.mf.add_file(DF_SYSTEM())
221 # optional USIM application
222 if 'a0000000871002' in rs.mf.applications:
223 usim_adf = rs.mf.applications['a0000000871002']
224 files_adf_usim = [
225 EF_USIM_AUTH_KEY(),
226 EF_USIM_AUTH_KEY_2G(),
227 EF_GBA_SK(),
228 EF_GBA_REC_LIST(),
229 EF_GBA_INT_KEY(),
230 EF_USIM_SQN(),
231 ]
232 usim_adf.add_files(files_adf_usim)
233 # optional ISIM application
234 if 'a0000000871004' in rs.mf.applications:
235 isim_adf = rs.mf.applications['a0000000871004']
236 files_adf_isim = [
237 EF_USIM_AUTH_KEY(name='EF.ISIM_AUTH_KEY'),
238 EF_USIM_AUTH_KEY_2G(name='EF.ISIM_AUTH_KEY_2G'),
239 EF_USIM_SQN(name='EF.ISIM_SQN'),
240 ]
241 isim_adf.add_files(files_adf_isim)