blob: 457361622cab59d1525e8efb2b86d48740c5b6b3 [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
3""" pySim: Card programmation logic
4"""
5
6#
7# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
Harald Welte263fb082023-07-09 17:15:36 +02008# Copyright (C) 2011-2023 Harald Welte <laforge@gnumonks.org>
Alexander Chemeriseb6807d2017-07-18 17:04:38 +03009# Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com>
Sylvain Munaut76504e02010-12-07 00:24:32 +010010#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation, either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program. If not, see <http://www.gnu.org/licenses/>.
23#
24
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +020025from typing import Optional, Dict, Tuple
Philipp Maiera42ee6f2023-08-16 10:44:57 +020026from pySim.ts_102_221 import EF_DIR
Harald Weltef8d2e2b2023-07-09 17:58:38 +020027from pySim.ts_51_011 import DF_GSM
Vadim Yanitskiy85302d62021-05-02 02:18:42 +020028import abc
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +020029
Alexander Chemeriseb6807d2017-07-18 17:04:38 +030030from pySim.utils import *
Harald Welte79972522023-10-21 20:19:32 +020031from pySim.commands import Path, SimCardCommands
Harald Welte263fb082023-07-09 17:15:36 +020032
33class CardBase:
34 """General base class for some kind of telecommunications card."""
Harald Welte79972522023-10-21 20:19:32 +020035 def __init__(self, scc: SimCardCommands):
Harald Welte263fb082023-07-09 17:15:36 +020036 self._scc = scc
37 self._aids = []
38
Harald Weltea3961292023-07-09 21:44:52 +020039 def reset(self) -> Optional[Hexstr]:
Harald Welte263fb082023-07-09 17:15:36 +020040 rc = self._scc.reset_card()
41 if rc == 1:
42 return self._scc.get_atr()
43 else:
44 return None
45
Harald Weltea3961292023-07-09 21:44:52 +020046 def set_apdu_parameter(self, cla: Hexstr, sel_ctrl: Hexstr) -> None:
Harald Welte263fb082023-07-09 17:15:36 +020047 """Set apdu parameters (class byte and selection control bytes)"""
48 self._scc.cla_byte = cla
49 self._scc.sel_ctrl = sel_ctrl
50
Harald Weltea3961292023-07-09 21:44:52 +020051 def get_apdu_parameter(self) -> Tuple[Hexstr, Hexstr]:
Harald Welte263fb082023-07-09 17:15:36 +020052 """Get apdu parameters (class byte and selection control bytes)"""
53 return (self._scc.cla_byte, self._scc.sel_ctrl)
54
55 def erase(self):
56 print("warning: erasing is not supported for specified card type!")
57 return
58
Harald Weltea3961292023-07-09 21:44:52 +020059 def file_exists(self, fid: Path) -> bool:
Harald Welte263fb082023-07-09 17:15:36 +020060 res_arr = self._scc.try_select_path(fid)
61 for res in res_arr:
62 if res[1] != '9000':
63 return False
64 return True
65
Harald Weltea3961292023-07-09 21:44:52 +020066 def read_aids(self) -> List[Hexstr]:
Harald Welte263fb082023-07-09 17:15:36 +020067 # a non-UICC doesn't have any applications. Convenience helper to avoid
68 # callers having to do hasattr('read_aids') ahead of every call.
69 return []
70
71
72class SimCardBase(CardBase):
Harald Welte263fb082023-07-09 17:15:36 +020073 """Here we only add methods for commands specified in TS 51.011, without
74 any higher-layer processing."""
Harald Welte263fb082023-07-09 17:15:36 +020075 name = 'SIM'
76
Harald Welte79972522023-10-21 20:19:32 +020077 def __init__(self, scc: SimCardCommands):
Harald Welte172c28e2023-07-10 22:20:40 +020078 super(SimCardBase, self).__init__(scc)
79 self._scc.cla_byte = "A0"
Harald Welte659d7c12023-07-10 22:25:58 +020080 self._scc.sel_ctrl = "0000"
Harald Welte172c28e2023-07-10 22:20:40 +020081
Harald Weltea3961292023-07-09 21:44:52 +020082 def probe(self) -> bool:
Harald Weltef8d2e2b2023-07-09 17:58:38 +020083 df_gsm = DF_GSM()
84 return self.file_exists(df_gsm.fid)
Harald Welte263fb082023-07-09 17:15:36 +020085
86
87class UiccCardBase(SimCardBase):
88 name = 'UICC'
89
Harald Welte79972522023-10-21 20:19:32 +020090 def __init__(self, scc: SimCardCommands):
Harald Welte775ab012023-07-10 22:23:07 +020091 super(UiccCardBase, self).__init__(scc)
Harald Welte172c28e2023-07-10 22:20:40 +020092 self._scc.cla_byte = "00"
Harald Welte659d7c12023-07-10 22:25:58 +020093 self._scc.sel_ctrl = "0004" # request an FCP
Harald Welte172c28e2023-07-10 22:20:40 +020094 # See also: ETSI TS 102 221, Table 9.3
Philipp Maier3175d612023-07-20 17:28:10 +020095 self._adm_chv_num = 0x0A
Harald Welte263fb082023-07-09 17:15:36 +020096
Harald Weltea3961292023-07-09 21:44:52 +020097 def probe(self) -> bool:
Harald Weltef8d2e2b2023-07-09 17:58:38 +020098 # EF.DIR is a mandatory EF on all ICCIDs; however it *may* also exist on a TS 51.011 SIM
99 ef_dir = EF_DIR()
100 return self.file_exists(ef_dir.fid)
Harald Welte263fb082023-07-09 17:15:36 +0200101
Harald Weltea3961292023-07-09 21:44:52 +0200102 def read_aids(self) -> List[Hexstr]:
Harald Weltec91085e2022-02-10 18:05:45 +0100103 """Fetch all the AIDs present on UICC"""
104 self._aids = []
105 try:
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200106 ef_dir = EF_DIR()
Harald Weltec91085e2022-02-10 18:05:45 +0100107 # Find out how many records the EF.DIR has
108 # and store all the AIDs in the UICC
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200109 rec_cnt = self._scc.record_count(ef_dir.fid)
Harald Weltec91085e2022-02-10 18:05:45 +0100110 for i in range(0, rec_cnt):
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200111 rec = self._scc.read_record(ef_dir.fid, i + 1)
Harald Weltec91085e2022-02-10 18:05:45 +0100112 if (rec[0][0:2], rec[0][4:6]) == ('61', '4f') and len(rec[0]) > 12 \
113 and rec[0][8:8 + int(rec[0][6:8], 16) * 2] not in self._aids:
114 self._aids.append(rec[0][8:8 + int(rec[0][6:8], 16) * 2])
115 except Exception as e:
116 print("Can't read AIDs from SIM -- %s" % (str(e),))
117 self._aids = []
118 return self._aids
Supreeth Herlee4e98312020-03-18 11:33:14 +0100119
Harald Weltec91085e2022-02-10 18:05:45 +0100120 @staticmethod
Harald Weltea3961292023-07-09 21:44:52 +0200121 def _get_aid(adf="usim") -> Optional[Hexstr]:
Harald Weltec91085e2022-02-10 18:05:45 +0100122 aid_map = {}
123 # First (known) halves of the U/ISIM AID
124 aid_map["usim"] = "a0000000871002"
125 aid_map["isim"] = "a0000000871004"
126 adf = adf.lower()
127 if adf in aid_map:
128 return aid_map[adf]
129 return None
Philipp Maier46c61542021-11-16 16:36:50 +0100130
Harald Weltea3961292023-07-09 21:44:52 +0200131 def _complete_aid(self, aid: Hexstr) -> Optional[Hexstr]:
Harald Weltec91085e2022-02-10 18:05:45 +0100132 """find the complete version of an ADF.U/ISIM AID"""
133 # Find full AID by partial AID:
134 if is_hex(aid):
135 for aid_known in self._aids:
136 if len(aid_known) >= len(aid) and aid == aid_known[0:len(aid)]:
137 return aid_known
138 return None
Philipp Maier46c61542021-11-16 16:36:50 +0100139
Harald Weltea3961292023-07-09 21:44:52 +0200140 def adf_present(self, adf: str = "usim") -> bool:
Philipp Maier84902402023-02-10 18:23:36 +0100141 """Check if the AID of the specified ADF is present in EF.DIR (call read_aids before use)"""
142 aid = self._get_aid(adf)
143 if aid:
144 aid_full = self._complete_aid(aid)
145 if aid_full:
146 return True
147 return False
148
Harald Welte6dd6f3e2023-10-22 10:28:18 +0200149 def select_adf_by_aid(self, adf: str = "usim", scc: Optional[SimCardCommands] = None) -> Tuple[Optional[Hexstr], Optional[SwHexstr]]:
Harald Weltec91085e2022-02-10 18:05:45 +0100150 """Select ADF.U/ISIM in the Card using its full AID"""
Harald Welte6dd6f3e2023-10-22 10:28:18 +0200151 # caller may pass a custom scc; we fall back to default
152 scc = scc or self._scc
Harald Weltec91085e2022-02-10 18:05:45 +0100153 if is_hex(adf):
154 aid = adf
155 else:
156 aid = self._get_aid(adf)
157 if aid:
158 aid_full = self._complete_aid(aid)
159 if aid_full:
Harald Welte6dd6f3e2023-10-22 10:28:18 +0200160 return scc.select_adf(aid_full)
Harald Weltec91085e2022-02-10 18:05:45 +0100161 else:
162 # If we cannot get the full AID, try with short AID
Harald Welte6dd6f3e2023-10-22 10:28:18 +0200163 return scc.select_adf(aid)
Harald Weltec91085e2022-02-10 18:05:45 +0100164 return (None, None)
Supreeth Herlef9f3e5e2020-03-22 08:04:59 +0100165
Harald Welte79972522023-10-21 20:19:32 +0200166def card_detect(scc: SimCardCommands) -> Optional[CardBase]:
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200167 # UICC always has higher preference, as a UICC might also contain a SIM application
168 uicc = UiccCardBase(scc)
169 if uicc.probe():
170 return uicc
Philipp Maierbda52832022-06-14 16:18:12 +0200171
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200172 # this is for detecting a real, classic TS 11.11 SIM card without any UICC support
173 sim = SimCardBase(scc)
174 if sim.probe():
175 return sim
Harald Welteca673942020-06-03 15:19:40 +0200176
Harald Weltef8d2e2b2023-07-09 17:58:38 +0200177 return None