blob: 0d09e81b32c5edebd6af87b58d8a67b7af56f908 [file] [log] [blame]
Philipp Maiera028c7d2021-11-08 16:12:03 +01001# -*- coding: utf-8 -*-
2
3""" pySim: tell old 2G SIMs apart from UICC
4"""
5
6#
7# (C) 2021 by Sysmocom s.f.m.c. GmbH
8# All Rights Reserved
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
24from pySim.commands import SimCardCommands
25from pySim.filesystem import CardApplication, interpret_sw
26from pySim.utils import all_subclasses
Philipp Maiera028c7d2021-11-08 16:12:03 +010027import abc
28import operator
Vadim Yanitskiy87dd0202023-04-22 20:45:29 +070029from typing import List
Philipp Maiera028c7d2021-11-08 16:12:03 +010030
Philipp Maiera028c7d2021-11-08 16:12:03 +010031
Vadim Yanitskiy87dd0202023-04-22 20:45:29 +070032def _mf_select_test(scc: SimCardCommands,
33 cla_byte: str, sel_ctrl: str,
34 fids: List[str]) -> bool:
Harald Weltec91085e2022-02-10 18:05:45 +010035 cla_byte_bak = scc.cla_byte
36 sel_ctrl_bak = scc.sel_ctrl
37 scc.reset_card()
Philipp Maiera028c7d2021-11-08 16:12:03 +010038
Harald Weltec91085e2022-02-10 18:05:45 +010039 scc.cla_byte = cla_byte
40 scc.sel_ctrl = sel_ctrl
41 rc = True
42 try:
Vadim Yanitskiy87dd0202023-04-22 20:45:29 +070043 for fid in fids:
44 scc.select_file(fid)
Harald Weltec91085e2022-02-10 18:05:45 +010045 except:
46 rc = False
Philipp Maiera028c7d2021-11-08 16:12:03 +010047
Harald Weltec91085e2022-02-10 18:05:45 +010048 scc.reset_card()
49 scc.cla_byte = cla_byte_bak
50 scc.sel_ctrl = sel_ctrl_bak
51 return rc
Philipp Maiera028c7d2021-11-08 16:12:03 +010052
Harald Weltec91085e2022-02-10 18:05:45 +010053
54def match_uicc(scc: SimCardCommands) -> bool:
55 """ Try to access MF via UICC APDUs (3GPP TS 102.221), if this works, the
56 card is considered a UICC card.
57 """
Vadim Yanitskiy87dd0202023-04-22 20:45:29 +070058 return _mf_select_test(scc, "00", "0004", ["3f00"])
Harald Weltec91085e2022-02-10 18:05:45 +010059
60
61def match_sim(scc: SimCardCommands) -> bool:
62 """ Try to access MF via 2G APDUs (3GPP TS 11.11), if this works, the card
63 is also a simcard. This will be the case for most UICC cards, but there may
64 also be plain UICC cards without 2G support as well.
65 """
Vadim Yanitskiy87dd0202023-04-22 20:45:29 +070066 return _mf_select_test(scc, "a0", "0000", ["3f00"])
67
68
69def match_ruim(scc: SimCardCommands) -> bool:
70 """ Try to access MF/DF.CDMA via 2G APDUs (3GPP TS 11.11), if this works,
71 the card is considered an R-UIM card for CDMA.
72 """
73 return _mf_select_test(scc, "a0", "0000", ["3f00", "7f25"])
Harald Weltec91085e2022-02-10 18:05:45 +010074
Philipp Maiera028c7d2021-11-08 16:12:03 +010075
Vadim Yanitskiy04b5d9d2022-07-07 03:05:30 +070076class CardProfile:
Harald Weltec91085e2022-02-10 18:05:45 +010077 """A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of
78 applications as well as profile-specific SW and shell commands. Every card has
79 one card profile, but there may be multiple applications within that profile."""
Philipp Maiera028c7d2021-11-08 16:12:03 +010080
Harald Weltec91085e2022-02-10 18:05:45 +010081 def __init__(self, name, **kw):
82 """
83 Args:
84 desc (str) : Description
85 files_in_mf : List of CardEF instances present in MF
86 applications : List of CardApplications present on card
87 sw : List of status word definitions
88 shell_cmdsets : List of cmd2 shell command sets of profile-specific commands
89 cla : class byte that should be used with cards of this profile
90 sel_ctrl : selection control bytes class byte that should be used with cards of this profile
Harald Welte323a3502023-07-11 17:26:39 +020091 addons: List of optional CardAddons that a card of this profile might have
Harald Weltec91085e2022-02-10 18:05:45 +010092 """
93 self.name = name
94 self.desc = kw.get("desc", None)
95 self.files_in_mf = kw.get("files_in_mf", [])
96 self.sw = kw.get("sw", {})
97 self.applications = kw.get("applications", [])
98 self.shell_cmdsets = kw.get("shell_cmdsets", [])
99 self.cla = kw.get("cla", "00")
100 self.sel_ctrl = kw.get("sel_ctrl", "0004")
Harald Welte323a3502023-07-11 17:26:39 +0200101 # list of optional addons that a card of this profile might have
102 self.addons = kw.get("addons", [])
Philipp Maiera028c7d2021-11-08 16:12:03 +0100103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 def __str__(self):
105 return self.name
Philipp Maiera028c7d2021-11-08 16:12:03 +0100106
Harald Weltec91085e2022-02-10 18:05:45 +0100107 def add_application(self, app: CardApplication):
108 """Add an application to a card profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100109
Harald Weltec91085e2022-02-10 18:05:45 +0100110 Args:
111 app : CardApplication instance to be added to profile
112 """
113 self.applications.append(app)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100114
Harald Weltec91085e2022-02-10 18:05:45 +0100115 def interpret_sw(self, sw: str):
116 """Interpret a given status word within the profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100117
Harald Weltec91085e2022-02-10 18:05:45 +0100118 Args:
119 sw : Status word as string of 4 hex digits
Philipp Maiera028c7d2021-11-08 16:12:03 +0100120
Harald Weltec91085e2022-02-10 18:05:45 +0100121 Returns:
122 Tuple of two strings
123 """
124 return interpret_sw(self.sw, sw)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100125
Harald Weltec91085e2022-02-10 18:05:45 +0100126 @staticmethod
127 def decode_select_response(data_hex: str) -> object:
128 """Decode the response to a SELECT command.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100129
Harald Weltec91085e2022-02-10 18:05:45 +0100130 This is the fall-back method which doesn't perform any decoding. It mostly
131 exists so specific derived classes can overload it for actual decoding.
132 This method is implemented in the profile and is only used when application
133 specific decoding cannot be performed (no ADF is selected).
Philipp Maiera028c7d2021-11-08 16:12:03 +0100134
Harald Weltec91085e2022-02-10 18:05:45 +0100135 Args:
136 data_hex: Hex string of the select response
137 """
138 return data_hex
Philipp Maiera028c7d2021-11-08 16:12:03 +0100139
Harald Weltec91085e2022-02-10 18:05:45 +0100140 @staticmethod
141 @abc.abstractmethod
142 def match_with_card(scc: SimCardCommands) -> bool:
143 """Check if the specific profile matches the card. This method is a
144 placeholder that is overloaded by specific dirived classes. The method
145 actively probes the card to make sure the profile class matches the
146 physical card. This usually also means that the card is reset during
147 the process, so this method must not be called at random times. It may
148 only be called on startup.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100149
Harald Weltec91085e2022-02-10 18:05:45 +0100150 Args:
151 scc: SimCardCommands class
152 Returns:
153 match = True, no match = False
154 """
155 return False
Philipp Maiera028c7d2021-11-08 16:12:03 +0100156
Harald Weltec91085e2022-02-10 18:05:45 +0100157 @staticmethod
158 def pick(scc: SimCardCommands):
159 profiles = list(all_subclasses(CardProfile))
160 profiles.sort(key=operator.attrgetter('ORDER'))
Philipp Maiera028c7d2021-11-08 16:12:03 +0100161
Harald Weltec91085e2022-02-10 18:05:45 +0100162 for p in profiles:
163 if p.match_with_card(scc):
164 return p()
Philipp Maiera028c7d2021-11-08 16:12:03 +0100165
Harald Weltec91085e2022-02-10 18:05:45 +0100166 return None
Harald Welte323a3502023-07-11 17:26:39 +0200167
168 def add_addon(self, addon: 'CardProfileAddon'):
169 assert(addon not in self.addons)
170 # we don't install any additional files, as that is happening in the RuntimeState.
171 self.addons.append(addon)
172
173class CardProfileAddon(abc.ABC):
174 """A Card Profile Add-on is something that is not a card application or a full stand-alone
175 card profile, but an add-on to an existing profile. Think of GSM-R specific files existing
176 on what is otherwise a SIM or USIM+SIM card."""
177
178 def __init__(self, name: str, **kw):
179 """
180 Args:
181 desc (str) : Description
182 files_in_mf : List of CardEF instances present in MF
183 shell_cmdsets : List of cmd2 shell command sets of profile-specific commands
184 """
185 self.name = name
186 self.desc = kw.get("desc", None)
187 self.files_in_mf = kw.get("files_in_mf", [])
188 self.shell_cmdsets = kw.get("shell_cmdsets", [])
189 pass
190
191 def __str__(self):
192 return self.name
193
194 @abc.abstractmethod
195 def probe(self, card: 'CardBase') -> bool:
196 """Probe a given card to determine whether or not this add-on is present/supported."""
197 pass