Tool to (re)configure the sysmoUSIM and sysmoISIM cards
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

499 lines
14 KiB

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Gadgets to modify SYSMO USIM SJS1 parameters
  5. (C) 2017 by Sysmocom s.f.m.c. GmbH
  6. All Rights Reserved
  7. Author: Philipp Maier
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. # Some gadgets to handle functions specific to Sysmo USIM SJS1. The gadgets are
  20. # organized as a loose collection of python functions. Each function serves
  21. # a specific task (e.g. modifiying the auth parameters). For each task two
  22. # functions are implemented sysmo_usim_show_...() to inspect the data that is
  23. # intended to be modified and sysmo_usim_write_...() to perform the actual
  24. # modification task.
  25. # Partial File tree:
  26. # The following tree is incomplete, it just contains the propritary files we
  27. # need to perform the tasks implemented below:
  28. #
  29. # [MF 0x3F00]
  30. # |
  31. # +--[DF_AUTH 0x7FCC]
  32. # | |
  33. # | +--[EF_AUTH 0x6F00]
  34. # | |
  35. # | +--[EF_MLNGC 0x6F01]
  36. # |
  37. # +--[DF_GSM 0x7F20]
  38. # |
  39. # +--[EF_OPC 0x00F7]
  40. # |
  41. # +--[EF_KI 0x00FF]
  42. # |
  43. # +--[EF_IMSI 0x6F07]
  44. import sys
  45. from utils import *
  46. from sysmo_usim import *
  47. # Files (propritary)
  48. SYSMO_USIMSJS1_EF_KI = [0x00, 0xFF]
  49. SYSMO_USIMSJS1_EF_OPC = [0x00, 0xF7]
  50. SYSMO_USIMSJS1_DF_AUTH = [0x7F, 0xCC] #FIXME: Manual does not mention name, just called it "DF_AUTH" might be wrong!
  51. SYSMO_USIMSJS1_EF_AUTH = [0x6F, 0x00]
  52. SYSMO_USIMSJS1_EF_MLNGC = [0x6F, 0x01]
  53. SYSMO_USIMSJS1_EF_SQNC = [0x00, 0xFB] # ADF.USIM
  54. SYSMO_USIMSJS1_EF_SQNA = [0x00, 0xFA] # ADF.USIM
  55. SYSMO_USIMSJS1_EF_EFMLNG = [0x00, 0xFB] # ADF.USIM
  56. SYSMO_USIMSJS1_EF_AC = [0x00, 0xFE] # ADF.USIM
  57. # Authentication algorithms (See sysmousim.pdf cap. 8.5)
  58. SYSMO_USIMSJS1_ALGO_MILENAGE = 0x01
  59. SYSMO_USIMSJS1_ALGO_COMP12V1 = 0x03
  60. SYSMO_USIMSJS1_ALGO_XOR2G = 0x04
  61. SYSMO_USIMSJS1_ALGO_COMP128V2 = 0x06
  62. SYSMO_USIMSJS1_ALGO_COMP128V3 = 0x07
  63. SYSMO_USIMSJS1_ALGO_XOR3G = 0x08
  64. # Application identifier
  65. SYSMO_USIM_AID = [0xa0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02]
  66. # Default content of record No.1 in EF.DIR
  67. SYSMO_USIM_EF_DIR_REC_1_CONTENT = [0x61, 0x19, 0x4f, 0x10] + SYSMO_USIM_AID + \
  68. [0xff, 0xff, 0xff, 0xff, 0x89, 0x07, 0x09, 0x00, 0x00, 0x50, 0x05,
  69. 0x55, 0x53, 0x69, 0x6d, 0x31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  70. 0xff, 0xff, 0xff, 0xff, 0xff]
  71. # Abstraction for the file structure of EF.MLNGC, which holds the
  72. # parameters of the milenage authentication algorithm
  73. class SYSMO_USIMSJS1_FILE_EF_MLNGC:
  74. # Default parameters, see also sysmousim-manual.pdf,
  75. # cap. 8.6 "Milenage Configuration (Ci/Ri)
  76. C1 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  77. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
  78. C2 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  79. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
  80. C3 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  81. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
  82. C4 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  83. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04]
  84. C5 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  85. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08]
  86. R1 = 0x40
  87. R2 = 0x00
  88. R3 = 0x20
  89. R4 = 0x40
  90. R5 = 0x60
  91. def __init__(self, content = None):
  92. if content == None:
  93. return
  94. if len(content) != 85:
  95. return
  96. self.C1 = content[0:16]
  97. self.C2 = content[16:32]
  98. self.C3 = content[32:48]
  99. self.C4 = content[48:64]
  100. self.C5 = content[64:80]
  101. self.R1 = content[80]
  102. self.R2 = content[81]
  103. self.R3 = content[82]
  104. self.R4 = content[83]
  105. self.R5 = content[84]
  106. def __str__(self):
  107. dump = " C1: " + hexdump(self.C1) + "\n"
  108. dump += " C2: " + hexdump(self.C2) + "\n"
  109. dump += " C3: " + hexdump(self.C3) + "\n"
  110. dump += " C4: " + hexdump(self.C4) + "\n"
  111. dump += " C5: " + hexdump(self.C5) + "\n"
  112. dump += " R1: " + str(hex(self.R1)) + "\n"
  113. dump += " R2: " + str(hex(self.R2)) + "\n"
  114. dump += " R3: " + str(hex(self.R3)) + "\n"
  115. dump += " R4: " + str(hex(self.R4)) + "\n"
  116. dump += " R5: " + str(hex(self.R5))
  117. return dump
  118. def encode(self):
  119. out = self.C1 + self.C2 + self.C3 + self.C4 + self.C5
  120. out += [self.R1, self.R2, self.R3, self.R4, self.R5]
  121. return out
  122. class SYSMO_USIMSJS1_FILE_EF_SQNC:
  123. # Default parameters
  124. ind_size_bits = 5
  125. sqn_check_enabled = True
  126. sqn_age_limit_enabled = False
  127. sqn_max_delta_enabled = True
  128. sqnms_offset = 0
  129. max_delta = 2**28 << ind_size_bits
  130. age_limit = 2**28 << ind_size_bits
  131. def __init__(self, content = None):
  132. if content == None:
  133. return
  134. if len(content) != 15:
  135. raise ValueError("unexpected length of %u bytes", len(content))
  136. self.ind_size_bits = content[0] & 0xf
  137. self.sqn_check_enabled = bool(content[0] & 0x10)
  138. self.sqn_age_limit_enabled = bool(content[0] & 0x20)
  139. self.sqn_max_delta_enabled = bool(content[0] & 0x40)
  140. self.sqnms_offset = list_to_int(content[1:3])/6
  141. self.max_delta = list_to_int(content[3:9]) >> self.ind_size_bits
  142. self.age_limit = list_to_int(content[9:15]) >> self.ind_size_bits
  143. def __str__(self):
  144. pfx = " "
  145. dump = ""
  146. dump += "%sIND (bits): %u\n" % (pfx, self.ind_size_bits)
  147. dump += "%sSQN Check enabled: %u\n" % (pfx, self.sqn_check_enabled)
  148. dump += "%sSQN Age Limit enabled: %u\n" % (pfx, self.sqn_age_limit_enabled)
  149. dump += "%sSQN Max Delta enabled: %u\n" % (pfx, self.sqn_max_delta_enabled)
  150. dump += "%sSQNms Offset (into SQN array): %u\n" % (pfx, self.sqnms_offset)
  151. dump += "%sMax Delta: %u\n" % (pfx, self.max_delta)
  152. dump += "%sAge Limit: %u\n" % (pfx, self.age_limit)
  153. return dump
  154. def encode(self):
  155. out = list(range(0, 3))
  156. out[0] = self.ind_size_bits & 0x0f
  157. if self.sqn_check_enabled:
  158. out[0] |= 0x10
  159. if self.sqn_age_limit_enabled:
  160. out[0] |= 0x20
  161. if self.sqn_max_delta_enabled:
  162. out[0] |= 0x40
  163. out[1] = (self.sqnms_offset*6) & 0xff
  164. out[2] = (self.sqnms_offset*6) >> 8
  165. out += int_to_list(self.max_delta, 6)
  166. out += int_to_list(self.age_limit, 6)
  167. return out
  168. class SYSMO_USIMSJS1_FILE_EF_SQNA:
  169. seq_array = []
  170. def __init__(self, content, ind = 5):
  171. if content == None:
  172. for i in range(0, 2**ind):
  173. self.seq_array.append(0)
  174. return
  175. if len(content) != 6*(2**ind):
  176. raise ValueError("unexpected length of %u bytes", len(content))
  177. # read in the SEQ array
  178. for i in range(0, 2**ind):
  179. offset = 6*i;
  180. self.seq_array.append(list_to_int(content[offset:offset+6]))
  181. def __str__(self):
  182. pfx = " "
  183. dump = ""
  184. for i in range(len(self.seq_array)):
  185. dump += "%sSEQ[%03d]: %u\n" % (pfx, i, self.seq_array[i])
  186. return dump
  187. def encode(self):
  188. out = []
  189. for i in self.seq_array:
  190. out += int_to_list(i, 6)
  191. return out
  192. sysmo_usim_algorithms = (
  193. (1, 'MILENAGE'),
  194. (3, 'COMP128v1'),
  195. (4, 'XOR-2G'),
  196. (5, 'GBA'),
  197. (6, 'COMP128v2'),
  198. (7, 'COMP128v3'),
  199. (8, 'XOR-3G'),
  200. (9, 'CIS-B'),
  201. )
  202. sysmo_usim_opcmodes = (
  203. (0, 'OP'),
  204. (1, 'OPc'),
  205. )
  206. class Sysmo_usim_sjs1(Sysmo_usim):
  207. def __init__(self):
  208. Sysmo_usim.__init__(self, "3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5")
  209. # Show the enable status of the USIM application (app is enabled or disabled?)
  210. def show_sim_mode(self):
  211. print("Reading SIM-Mode...")
  212. self._init()
  213. print(" * Reading...")
  214. self.sim.select(GSM_USIM_EF_DIR)
  215. res = self.sim.read_record(0x26, rec_no = 1)
  216. print(" * Current status of Record No. 1 in EF.DIR:")
  217. print(" " + hexdump(res.apdu))
  218. if hexdump(SYSMO_USIM_AID) in hexdump(res.apdu):
  219. print(" ==> USIM application enabled")
  220. else:
  221. print(" ==> USIM application disabled")
  222. print("")
  223. # Show the enable status of the USIM application (app is enabled or disabled?)
  224. def write_sim_mode(self, usim_enabled = True):
  225. print("Programming SIM-Mode...")
  226. self._init()
  227. if usim_enabled:
  228. new_record = SYSMO_USIM_EF_DIR_REC_1_CONTENT
  229. else:
  230. new_record = [0xFF] * len(SYSMO_USIM_EF_DIR_REC_1_CONTENT)
  231. print(" * New status of Record No.1 in EF.DIR:")
  232. print(" " + hexdump(new_record))
  233. if hexdump(SYSMO_USIM_AID) in hexdump(new_record):
  234. print(" ==> USIM application enabled")
  235. else:
  236. print(" ==> USIM application disabled")
  237. print(" * Programming...")
  238. self.sim.select(GSM_USIM_EF_DIR)
  239. self.sim.update_record(new_record, rec_no = 1)
  240. print("")
  241. # Show current athentication parameters
  242. # (Which algorithim is used for which rat?)
  243. def show_auth_params(self):
  244. print("Reading Authentication parameters...")
  245. self._init()
  246. print(" * Reading...")
  247. self.sim.select(SYSMO_USIMSJS1_DF_AUTH)
  248. self.sim.select(SYSMO_USIMSJS1_EF_AUTH)
  249. res = self._read_binary(0x02)
  250. algo_2g, algo_3g = res.apdu[:2]
  251. print(" * Current algorithm setting:")
  252. print(" 2G: %d=%s" % (algo_2g, id_to_str(sysmo_usim_algorithms, algo_2g)))
  253. print(" 3G: %d=%s" % (algo_3g, id_to_str(sysmo_usim_algorithms, algo_3g)))
  254. print("")
  255. # Program new authentication parameters
  256. def write_auth_params(self, algo_2g_str, algo_3g_str):
  257. print("Programming Authentication parameters...")
  258. self._init()
  259. if algo_2g_str.isdigit():
  260. algo_2g = int(algo_2g_str)
  261. else:
  262. algo_2g = str_to_id(sysmo_usim_algorithms, algo_2g_str)
  263. if algo_3g_str.isdigit():
  264. algo_3g = int(algo_3g_str)
  265. else:
  266. algo_3g = str_to_id(sysmo_usim_algorithms, algo_3g_str)
  267. print(" * New algorithm setting:")
  268. print(" 2G: %d=%s" % (algo_2g, id_to_str(sysmo_usim_algorithms, algo_2g)))
  269. print(" 3G: %d=%s" % (algo_3g, id_to_str(sysmo_usim_algorithms, algo_3g)))
  270. print(" * Programming...")
  271. self.sim.select(SYSMO_USIMSJS1_DF_AUTH)
  272. self.sim.select(SYSMO_USIMSJS1_EF_AUTH)
  273. self.sim.update_binary([algo_2g,algo_3g])
  274. print("")
  275. # Show current milenage parameters
  276. def show_milenage_params(self):
  277. print("Reading Milenage parameters...")
  278. self._init()
  279. self.sim.select(SYSMO_USIMSJS1_DF_AUTH)
  280. self.sim.select(SYSMO_USIMSJS1_EF_MLNGC)
  281. print(" * Reading...")
  282. res = self._read_binary(85)
  283. ef_mlngc = SYSMO_USIMSJS1_FILE_EF_MLNGC(res.apdu)
  284. print(" * Current Milenage Parameters in (EF.MLNGC):")
  285. print(str(ef_mlngc))
  286. print("")
  287. # Write new milenage parameters
  288. def write_milenage_params(self, params):
  289. print("Programming Milenage parameters...")
  290. self._init()
  291. print(" * New Milenage Parameters for (EF.MLNGC):")
  292. ef_mlngc = SYSMO_USIMSJS1_FILE_EF_MLNGC(params)
  293. print(str(ef_mlngc))
  294. self.sim.select(SYSMO_USIMSJS1_DF_AUTH)
  295. self.sim.select(SYSMO_USIMSJS1_EF_MLNGC)
  296. print(" * Programming...")
  297. self.sim.update_binary(ef_mlngc.encode())
  298. print("")
  299. def __get_auth_counter(self):
  300. self.sim.select(SYSMO_USIMSJS1_EF_AC)
  301. res = self._read_binary(4, offset=0)
  302. ctr = list_to_int(res.apdu[0:4])
  303. if ctr == 0:
  304. return "LOCKED"
  305. elif ctr == 0xFFFFFFFF:
  306. return "DISABLED"
  307. else:
  308. return ctr
  309. def __set_auth_counter(self, ctr):
  310. if ctr == "LOCKED":
  311. ctr = 0
  312. elif ctr == "DISABLED":
  313. ctr = 0xFFFFFFFF
  314. data = int_to_list(ctr, 4)
  315. self.sim.select(SYSMO_USIMSJS1_EF_AC)
  316. res = self.sim.update_binary(data, offset=0)
  317. if ctr == 0:
  318. return "LOCKED"
  319. elif ctr == 0xFFFFFFFF:
  320. return "DISABLED"
  321. else:
  322. return ctr
  323. # Show current milenage SQN parameters
  324. def show_milenage_sqn_params(self):
  325. print("Reading Milenage Sequence parameters...")
  326. self._init()
  327. self.sim.card.SELECT_ADF_USIM()
  328. self.sim.select(SYSMO_USIMSJS1_EF_SQNC)
  329. res = self._read_binary(15, offset = 0)
  330. ef_sqnc = SYSMO_USIMSJS1_FILE_EF_SQNC(res.apdu)
  331. print(" * Current SQN Configuration:")
  332. print(str(ef_sqnc))
  333. # SQN Array
  334. ind_pow = 2**ef_sqnc.ind_size_bits
  335. self.sim.select(SYSMO_USIMSJS1_EF_SQNA)
  336. res = self._read_binary(ind_pow*6, offset=0)
  337. ef_sqna = SYSMO_USIMSJS1_FILE_EF_SQNA(res.apdu)
  338. print(" * Current SQN Array:")
  339. print(str(ef_sqna))
  340. auth_ctr = self.__get_auth_counter()
  341. print("* Authentication Counter: %s" % auth_ctr)
  342. print("")
  343. # Reset milenage SQN configuration
  344. def reset_milenage_sqn_params(self):
  345. print(" * Resetting SQN Configuration to defaults...")
  346. self._init()
  347. print(" * Resetting...")
  348. self.sim.card.SELECT_ADF_USIM()
  349. ef_sqnc = SYSMO_USIMSJS1_FILE_EF_SQNC(None)
  350. self.sim.select(SYSMO_USIMSJS1_EF_SQNC)
  351. res = self.sim.update_binary(ef_sqnc.encode())
  352. ef_sqna = SYSMO_USIMSJS1_FILE_EF_SQNA(None, ef_sqnc.ind_size_bits)
  353. self.sim.select(SYSMO_USIMSJS1_EF_SQNA)
  354. res = self.sim.update_binary(ef_sqna.encode())
  355. self.__set_auth_counter("DISABLED")
  356. print("")
  357. # Show current OPc value
  358. def show_opc_params(self):
  359. print("Reading OP/c value...")
  360. self._init()
  361. print(" * Reading...")
  362. self.sim.card.SELECT_ADF_USIM()
  363. self.sim.select(SYSMO_USIMSJS1_EF_OPC)
  364. res = self._read_binary(17)
  365. mode_str = id_to_str(sysmo_usim_opcmodes, res.apdu[0])
  366. print(" * Current OP/OPc setting:")
  367. print(" %s: %s" % (mode_str, hexdump(res.apdu[1:])))
  368. print("")
  369. # Program new OPc value
  370. def write_opc_params(self, select, op):
  371. if select:
  372. print("Writing OPc value...")
  373. else:
  374. print("Writing OP value...")
  375. self._init()
  376. print(" * New OPc setting:")
  377. print(" %s: %s" % (id_to_str(sysmo_usim_opcmodes, select), hexdump(op)))
  378. self.sim.select(GSM_SIM_DF_GSM)
  379. self.sim.select(SYSMO_USIMSJS1_EF_OPC)
  380. print(" * Programming...")
  381. self.sim.update_binary([select] + op)
  382. print("")
  383. # Show current KI value
  384. def show_ki_params(self):
  385. print("Reading KI value...")
  386. print(" * Reading...")
  387. self.sim.select(GSM_SIM_DF_GSM)
  388. self.sim.select(SYSMO_USIMSJS1_EF_KI)
  389. res = self._read_binary(16)
  390. print(" * Current KI setting:")
  391. print(" KI: " + hexdump(res.apdu))
  392. print("")
  393. # Program new KI value
  394. def write_ki_params(self, ki):
  395. print("Writing KI value...")
  396. self._init()
  397. print(" * New KI setting:")
  398. print(" KI: " + hexdump(ki))
  399. self.sim.select(GSM_SIM_DF_GSM)
  400. self.sim.select(SYSMO_USIMSJS1_EF_KI)
  401. print(" * Programming...")
  402. self.sim.update_binary(ki)
  403. print("")