Sylvain Munaut | 76504e0 | 2010-12-07 00:24:32 +0100 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | """ pySim: Card programmation logic |
| 4 | """ |
| 5 | |
| 6 | # |
| 7 | # Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com> |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 8 | # Copyright (C) 2011-2023 Harald Welte <laforge@gnumonks.org> |
Alexander Chemeris | eb6807d | 2017-07-18 17:04:38 +0300 | [diff] [blame] | 9 | # Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com> |
Sylvain Munaut | 76504e0 | 2010-12-07 00:24:32 +0100 | [diff] [blame] | 10 | # |
| 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 Yanitskiy | 03c67f7 | 2021-05-02 02:10:39 +0200 | [diff] [blame] | 25 | from typing import Optional, Dict, Tuple |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 26 | from pySim.ts_102_221 import EF_DIR, EF_ICCID |
| 27 | from pySim.ts_51_011 import DF_GSM |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 28 | from pySim.transport import LinkBase |
Vadim Yanitskiy | 85302d6 | 2021-05-02 02:18:42 +0200 | [diff] [blame] | 29 | import abc |
Vadim Yanitskiy | 03c67f7 | 2021-05-02 02:10:39 +0200 | [diff] [blame] | 30 | |
Alexander Chemeris | eb6807d | 2017-07-18 17:04:38 +0300 | [diff] [blame] | 31 | from pySim.utils import * |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 32 | from pySim.commands import Path |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 33 | |
| 34 | class CardBase: |
| 35 | """General base class for some kind of telecommunications card.""" |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 36 | def __init__(self, scc: LinkBase): |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 37 | self._scc = scc |
| 38 | self._aids = [] |
| 39 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 40 | def reset(self) -> Optional[Hexstr]: |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 41 | rc = self._scc.reset_card() |
| 42 | if rc == 1: |
| 43 | return self._scc.get_atr() |
| 44 | else: |
| 45 | return None |
| 46 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 47 | def set_apdu_parameter(self, cla: Hexstr, sel_ctrl: Hexstr) -> None: |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 48 | """Set apdu parameters (class byte and selection control bytes)""" |
| 49 | self._scc.cla_byte = cla |
| 50 | self._scc.sel_ctrl = sel_ctrl |
| 51 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 52 | def get_apdu_parameter(self) -> Tuple[Hexstr, Hexstr]: |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 53 | """Get apdu parameters (class byte and selection control bytes)""" |
| 54 | return (self._scc.cla_byte, self._scc.sel_ctrl) |
| 55 | |
| 56 | def erase(self): |
| 57 | print("warning: erasing is not supported for specified card type!") |
| 58 | return |
| 59 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 60 | def file_exists(self, fid: Path) -> bool: |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 61 | res_arr = self._scc.try_select_path(fid) |
| 62 | for res in res_arr: |
| 63 | if res[1] != '9000': |
| 64 | return False |
| 65 | return True |
| 66 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 67 | def read_aids(self) -> List[Hexstr]: |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 68 | # a non-UICC doesn't have any applications. Convenience helper to avoid |
| 69 | # callers having to do hasattr('read_aids') ahead of every call. |
| 70 | return [] |
| 71 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 72 | def read_iccid(self) -> Tuple[Optional[Hexstr], SwHexstr]: |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 73 | ef_iccid = EF_ICCID() |
| 74 | (res, sw) = self._scc.read_binary(ef_iccid.fid) |
| 75 | if sw == '9000': |
| 76 | return (dec_iccid(res), sw) |
| 77 | else: |
| 78 | return (None, sw) |
| 79 | |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 80 | |
| 81 | class SimCardBase(CardBase): |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 82 | """Here we only add methods for commands specified in TS 51.011, without |
| 83 | any higher-layer processing.""" |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 84 | name = 'SIM' |
| 85 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 86 | def probe(self) -> bool: |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 87 | df_gsm = DF_GSM() |
| 88 | return self.file_exists(df_gsm.fid) |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 89 | |
| 90 | |
| 91 | class UiccCardBase(SimCardBase): |
| 92 | name = 'UICC' |
| 93 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 94 | def __init__(self, ssc: LinkBase): |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 95 | super(UiccCardBase, self).__init__(ssc) |
| 96 | # See also: ETSI TS 102 221, Table 9.3 |
| 97 | self._adm_chv_num = 0xA0 |
| 98 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 99 | def probe(self) -> bool: |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 100 | # EF.DIR is a mandatory EF on all ICCIDs; however it *may* also exist on a TS 51.011 SIM |
| 101 | ef_dir = EF_DIR() |
| 102 | return self.file_exists(ef_dir.fid) |
Harald Welte | 263fb08 | 2023-07-09 17:15:36 +0200 | [diff] [blame] | 103 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 104 | def read_aids(self) -> List[Hexstr]: |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 105 | """Fetch all the AIDs present on UICC""" |
| 106 | self._aids = [] |
| 107 | try: |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 108 | ef_dir = EF_DIR() |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 109 | # Find out how many records the EF.DIR has |
| 110 | # and store all the AIDs in the UICC |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 111 | rec_cnt = self._scc.record_count(ef_dir.fid) |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 112 | for i in range(0, rec_cnt): |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 113 | rec = self._scc.read_record(ef_dir.fid, i + 1) |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 114 | if (rec[0][0:2], rec[0][4:6]) == ('61', '4f') and len(rec[0]) > 12 \ |
| 115 | and rec[0][8:8 + int(rec[0][6:8], 16) * 2] not in self._aids: |
| 116 | self._aids.append(rec[0][8:8 + int(rec[0][6:8], 16) * 2]) |
| 117 | except Exception as e: |
| 118 | print("Can't read AIDs from SIM -- %s" % (str(e),)) |
| 119 | self._aids = [] |
| 120 | return self._aids |
Supreeth Herle | e4e9831 | 2020-03-18 11:33:14 +0100 | [diff] [blame] | 121 | |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 122 | @staticmethod |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 123 | def _get_aid(adf="usim") -> Optional[Hexstr]: |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 124 | aid_map = {} |
| 125 | # First (known) halves of the U/ISIM AID |
| 126 | aid_map["usim"] = "a0000000871002" |
| 127 | aid_map["isim"] = "a0000000871004" |
| 128 | adf = adf.lower() |
| 129 | if adf in aid_map: |
| 130 | return aid_map[adf] |
| 131 | return None |
Philipp Maier | 46c6154 | 2021-11-16 16:36:50 +0100 | [diff] [blame] | 132 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 133 | def _complete_aid(self, aid: Hexstr) -> Optional[Hexstr]: |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 134 | """find the complete version of an ADF.U/ISIM AID""" |
| 135 | # Find full AID by partial AID: |
| 136 | if is_hex(aid): |
| 137 | for aid_known in self._aids: |
| 138 | if len(aid_known) >= len(aid) and aid == aid_known[0:len(aid)]: |
| 139 | return aid_known |
| 140 | return None |
Philipp Maier | 46c6154 | 2021-11-16 16:36:50 +0100 | [diff] [blame] | 141 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 142 | def adf_present(self, adf: str = "usim") -> bool: |
Philipp Maier | 8490240 | 2023-02-10 18:23:36 +0100 | [diff] [blame] | 143 | """Check if the AID of the specified ADF is present in EF.DIR (call read_aids before use)""" |
| 144 | aid = self._get_aid(adf) |
| 145 | if aid: |
| 146 | aid_full = self._complete_aid(aid) |
| 147 | if aid_full: |
| 148 | return True |
| 149 | return False |
| 150 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 151 | def select_adf_by_aid(self, adf: str = "usim") -> Tuple[Optional[Hexstr], Optional[SwHexstr]]: |
Harald Welte | c91085e | 2022-02-10 18:05:45 +0100 | [diff] [blame] | 152 | """Select ADF.U/ISIM in the Card using its full AID""" |
| 153 | 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: |
| 160 | return self._scc.select_adf(aid_full) |
| 161 | else: |
| 162 | # If we cannot get the full AID, try with short AID |
| 163 | return self._scc.select_adf(aid) |
| 164 | return (None, None) |
Supreeth Herle | f9f3e5e | 2020-03-22 08:04:59 +0100 | [diff] [blame] | 165 | |
Harald Welte | a396129 | 2023-07-09 21:44:52 +0200 | [diff] [blame] | 166 | def card_detect(scc: LinkBase) -> Optional[CardBase]: |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 167 | # 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 Maier | bda5283 | 2022-06-14 16:18:12 +0200 | [diff] [blame] | 171 | |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 172 | # 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 Welte | ca67394 | 2020-06-03 15:19:40 +0200 | [diff] [blame] | 176 | |
Harald Welte | f8d2e2b | 2023-07-09 17:58:38 +0200 | [diff] [blame] | 177 | return None |