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.

207 lines
5.7 KiB

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Simcard IO Class
  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. from card.USIM import USIM
  20. from card.SIM import SIM
  21. from card.utils import *
  22. from utils import *
  23. # Files
  24. GSM_SIM_MF = [0x3F, 0x00]
  25. GSM_SIM_DF_TELECOM = [0x7F, 0x10]
  26. GSM_SIM_DF_GSM = [0x7F, 0x20]
  27. GSM_SIM_EF_ADN = [0x6f,0x3A]
  28. GSM_SIM_EF_IMSI = [0x6F, 0x07]
  29. GSM_SIM_EF_AD = [0x6f, 0xAD]
  30. GSM_SIM_EF_ICCID = [0x2F, 0xE2]
  31. GSM_USIM_EF_DIR = [0x2F, 0x00] # See also: 3GPP TS 31.102 Table 105
  32. # Card types
  33. GSM_SIM = 0
  34. GSM_USIM = 1
  35. # CHV Types
  36. GSM_CHV1 = 0x01
  37. GSM_CHV2 = 0x02
  38. # Record oriented read modes
  39. GSM_SIM_INS_READ_RECORD_NEXT = 0x02
  40. GSM_SIM_INS_READ_RECORD_PREV = 0x03
  41. GSM_SIM_INS_READ_RECORD_ABS = 0x04
  42. # Record oriented write modes
  43. GSM_SIM_INS_UPDATE_RECORD_NEXT = 0x02
  44. GSM_SIM_INS_UPDATE_RECORD_PREV = 0x03
  45. GSM_SIM_INS_UPDATE_RECORD_ABS = 0x04
  46. class Card_res_apdu():
  47. apdu = None
  48. sw = None
  49. # convert Benoit Michau style result to sysmocom style result
  50. def from_mich(self, mich):
  51. self.apdu = mich[3]
  52. self.sw = [ mich[2][0], mich[2][1] ]
  53. def __str__(self):
  54. dump = ""
  55. if len(self.apdu) > 0:
  56. dump = "APDU: " + hexdump(self.apdu)
  57. else:
  58. dump = "APDU: (no data)"
  59. dump += ", SW: " + hexdump(self.sw)
  60. return dump
  61. # A class to abstract a simcard.
  62. class Simcard():
  63. card = None
  64. filelen = 0 #length of the currently selected file
  65. has_isim = False
  66. has_usim = False
  67. # Constructor: Create a new simcard object
  68. def __init__(self, cardtype = GSM_USIM, atr = None):
  69. if cardtype == GSM_USIM:
  70. self.card = USIM(atr)
  71. self.usim = True
  72. # Detect ISIM / USIM applications
  73. self.card.get_AID()
  74. AID = self.card.AID
  75. for a in AID:
  76. if a[0:7] == [0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04]:
  77. self.has_isim = True
  78. elif a[0:7] == [0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02]:
  79. self.has_usim = True
  80. else:
  81. self.card = SIM(atr)
  82. self.usim = False
  83. # Find the right class byte, depending on the simcard type
  84. def __get_cla(self, usim):
  85. return self.card.CLA
  86. # Get file size from FCP
  87. def __get_len_from_tlv(self, fcp):
  88. # Note: This has been taken from http://git.osmocom.org/pysim/tree/pySim/commands.py,
  89. # but pySim uses ascii-hex strings for its internal data representation. We use
  90. # regular lists with integers, so we must convert to an ascii-hex string first:
  91. fcp = ''.join('{:02x}'.format(x) for x in fcp)
  92. # see also: ETSI TS 102 221, chapter 11.1.1.3.1 Response for MF,
  93. # DF or ADF
  94. from pytlv.TLV import TLV
  95. tlvparser = TLV(['82', '83', '84', 'a5', '8a', '8b', '8c', '80', 'ab', 'c6', '81', '88'])
  96. # pytlv is case sensitive!
  97. fcp = fcp.lower()
  98. if fcp[0:2] != '62':
  99. raise ValueError('Tag of the FCP template does not match, expected 62 but got %s'%fcp[0:2])
  100. # Unfortunately the spec is not very clear if the FCP length is
  101. # coded as one or two byte vale, so we have to try it out by
  102. # checking if the length of the remaining TLV string matches
  103. # what we get in the length field.
  104. # See also ETSI TS 102 221, chapter 11.1.1.3.0 Base coding.
  105. exp_tlv_len = int(fcp[2:4], 16)
  106. if len(fcp[4:])/2 == exp_tlv_len:
  107. skip = 4
  108. else:
  109. exp_tlv_len = int(fcp[2:6], 16)
  110. if len(fcp[4:])/2 == exp_tlv_len:
  111. skip = 6
  112. # Skip FCP tag and length
  113. tlv = fcp[skip:]
  114. tlv_parsed = tlvparser.parse(tlv)
  115. if '80' in tlv_parsed:
  116. return int(tlv_parsed['80'], 16)
  117. else:
  118. return 0
  119. # Get the file length from a response (select)
  120. def __len(self, res, p2):
  121. if p2 == 0x04:
  122. return self.__get_len_from_tlv(res)
  123. else:
  124. return int(res[-1][4:8], 16)
  125. # Select a file and retrieve its length
  126. def select(self, fid):
  127. self.filelen = 0
  128. p2 = 0x04
  129. res = Card_res_apdu()
  130. res.from_mich(self.card.SELECT_FILE(P2 = p2, Data = fid))
  131. # Stop here, on failure
  132. if res.sw[0] != 0x61:
  133. return res
  134. res.from_mich(self.card.GET_RESPONSE(res.sw[1]))
  135. self.filelen = self.__len(res.apdu, p2)
  136. return res
  137. # Perform card holder verification
  138. def verify_chv(self, chv, chv_no):
  139. res = Card_res_apdu()
  140. res.from_mich(self.card.VERIFY(P2 = chv_no, Data = chv))
  141. return res
  142. # Read CHV retry counter
  143. def chv_retrys(self, chv_no):
  144. res = self.card.VERIFY(P2 = chv_no)
  145. return res[2][1] & 0x0F
  146. # Perform file operation (Write)
  147. def update_binary(self, data, offset = 0):
  148. offs_high = (offset >> 8) & 0xFF
  149. offs_low = offset & 0xFF
  150. res = Card_res_apdu()
  151. res.from_mich(self.card.UPDATE_BINARY(offs_high, offs_low, data))
  152. return res
  153. # Perform file operation (Read, byte oriented)
  154. def read_binary(self, length, offset = 0):
  155. offs_high = (offset >> 8) & 0xFF
  156. offs_low = offset & 0xFF
  157. res = Card_res_apdu()
  158. res.from_mich(self.card.READ_BINARY(offs_high, offs_low, length))
  159. return res
  160. # Perform file operation (Read, record oriented)
  161. def read_record(self, length, rec_no = 0):
  162. res = Card_res_apdu()
  163. res.from_mich(self.card.READ_RECORD(rec_no, GSM_SIM_INS_READ_RECORD_ABS, length))
  164. return res
  165. # Perform file operation (Read, record oriented)
  166. def update_record(self, data, rec_no = 0):
  167. res = Card_res_apdu()
  168. res.from_mich(self.card.UPDATE_RECORD(rec_no, GSM_SIM_INS_UPDATE_RECORD_ABS, data))
  169. return res