blob: e464e1f9b98168f111ae6a9a106ac38085d96d16 [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
91 """
92 self.name = name
93 self.desc = kw.get("desc", None)
94 self.files_in_mf = kw.get("files_in_mf", [])
95 self.sw = kw.get("sw", {})
96 self.applications = kw.get("applications", [])
97 self.shell_cmdsets = kw.get("shell_cmdsets", [])
98 self.cla = kw.get("cla", "00")
99 self.sel_ctrl = kw.get("sel_ctrl", "0004")
Philipp Maiera028c7d2021-11-08 16:12:03 +0100100
Harald Weltec91085e2022-02-10 18:05:45 +0100101 def __str__(self):
102 return self.name
Philipp Maiera028c7d2021-11-08 16:12:03 +0100103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 def add_application(self, app: CardApplication):
105 """Add an application to a card profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100106
Harald Weltec91085e2022-02-10 18:05:45 +0100107 Args:
108 app : CardApplication instance to be added to profile
109 """
110 self.applications.append(app)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100111
Harald Weltec91085e2022-02-10 18:05:45 +0100112 def interpret_sw(self, sw: str):
113 """Interpret a given status word within the profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100114
Harald Weltec91085e2022-02-10 18:05:45 +0100115 Args:
116 sw : Status word as string of 4 hex digits
Philipp Maiera028c7d2021-11-08 16:12:03 +0100117
Harald Weltec91085e2022-02-10 18:05:45 +0100118 Returns:
119 Tuple of two strings
120 """
121 return interpret_sw(self.sw, sw)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100122
Harald Weltec91085e2022-02-10 18:05:45 +0100123 @staticmethod
124 def decode_select_response(data_hex: str) -> object:
125 """Decode the response to a SELECT command.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100126
Harald Weltec91085e2022-02-10 18:05:45 +0100127 This is the fall-back method which doesn't perform any decoding. It mostly
128 exists so specific derived classes can overload it for actual decoding.
129 This method is implemented in the profile and is only used when application
130 specific decoding cannot be performed (no ADF is selected).
Philipp Maiera028c7d2021-11-08 16:12:03 +0100131
Harald Weltec91085e2022-02-10 18:05:45 +0100132 Args:
133 data_hex: Hex string of the select response
134 """
135 return data_hex
Philipp Maiera028c7d2021-11-08 16:12:03 +0100136
Harald Weltec91085e2022-02-10 18:05:45 +0100137 @staticmethod
138 @abc.abstractmethod
139 def match_with_card(scc: SimCardCommands) -> bool:
140 """Check if the specific profile matches the card. This method is a
141 placeholder that is overloaded by specific dirived classes. The method
142 actively probes the card to make sure the profile class matches the
143 physical card. This usually also means that the card is reset during
144 the process, so this method must not be called at random times. It may
145 only be called on startup.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100146
Harald Weltec91085e2022-02-10 18:05:45 +0100147 Args:
148 scc: SimCardCommands class
149 Returns:
150 match = True, no match = False
151 """
152 return False
Philipp Maiera028c7d2021-11-08 16:12:03 +0100153
Harald Weltec91085e2022-02-10 18:05:45 +0100154 @staticmethod
155 def pick(scc: SimCardCommands):
156 profiles = list(all_subclasses(CardProfile))
157 profiles.sort(key=operator.attrgetter('ORDER'))
Philipp Maiera028c7d2021-11-08 16:12:03 +0100158
Harald Weltec91085e2022-02-10 18:05:45 +0100159 for p in profiles:
160 if p.match_with_card(scc):
161 return p()
Philipp Maiera028c7d2021-11-08 16:12:03 +0100162
Harald Weltec91085e2022-02-10 18:05:45 +0100163 return None