blob: c535bac8e54ee9cce628451d9de2744a6591bfc1 [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
29
Philipp Maiera028c7d2021-11-08 16:12:03 +010030
Harald Weltec91085e2022-02-10 18:05:45 +010031def _mf_select_test(scc: SimCardCommands, cla_byte: str, sel_ctrl: str) -> bool:
32 cla_byte_bak = scc.cla_byte
33 sel_ctrl_bak = scc.sel_ctrl
34 scc.reset_card()
Philipp Maiera028c7d2021-11-08 16:12:03 +010035
Harald Weltec91085e2022-02-10 18:05:45 +010036 scc.cla_byte = cla_byte
37 scc.sel_ctrl = sel_ctrl
38 rc = True
39 try:
40 scc.select_file('3f00')
41 except:
42 rc = False
Philipp Maiera028c7d2021-11-08 16:12:03 +010043
Harald Weltec91085e2022-02-10 18:05:45 +010044 scc.reset_card()
45 scc.cla_byte = cla_byte_bak
46 scc.sel_ctrl = sel_ctrl_bak
47 return rc
Philipp Maiera028c7d2021-11-08 16:12:03 +010048
Harald Weltec91085e2022-02-10 18:05:45 +010049
50def match_uicc(scc: SimCardCommands) -> bool:
51 """ Try to access MF via UICC APDUs (3GPP TS 102.221), if this works, the
52 card is considered a UICC card.
53 """
54 return _mf_select_test(scc, "00", "0004")
55
56
57def match_sim(scc: SimCardCommands) -> bool:
58 """ Try to access MF via 2G APDUs (3GPP TS 11.11), if this works, the card
59 is also a simcard. This will be the case for most UICC cards, but there may
60 also be plain UICC cards without 2G support as well.
61 """
62 return _mf_select_test(scc, "a0", "0000")
63
Philipp Maiera028c7d2021-11-08 16:12:03 +010064
Vadim Yanitskiy04b5d9d2022-07-07 03:05:30 +070065class CardProfile:
Harald Weltec91085e2022-02-10 18:05:45 +010066 """A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of
67 applications as well as profile-specific SW and shell commands. Every card has
68 one card profile, but there may be multiple applications within that profile."""
Philipp Maiera028c7d2021-11-08 16:12:03 +010069
Harald Weltec91085e2022-02-10 18:05:45 +010070 def __init__(self, name, **kw):
71 """
72 Args:
73 desc (str) : Description
74 files_in_mf : List of CardEF instances present in MF
75 applications : List of CardApplications present on card
76 sw : List of status word definitions
77 shell_cmdsets : List of cmd2 shell command sets of profile-specific commands
78 cla : class byte that should be used with cards of this profile
79 sel_ctrl : selection control bytes class byte that should be used with cards of this profile
80 """
81 self.name = name
82 self.desc = kw.get("desc", None)
83 self.files_in_mf = kw.get("files_in_mf", [])
84 self.sw = kw.get("sw", {})
85 self.applications = kw.get("applications", [])
86 self.shell_cmdsets = kw.get("shell_cmdsets", [])
87 self.cla = kw.get("cla", "00")
88 self.sel_ctrl = kw.get("sel_ctrl", "0004")
Philipp Maiera028c7d2021-11-08 16:12:03 +010089
Harald Weltec91085e2022-02-10 18:05:45 +010090 def __str__(self):
91 return self.name
Philipp Maiera028c7d2021-11-08 16:12:03 +010092
Harald Weltec91085e2022-02-10 18:05:45 +010093 def add_application(self, app: CardApplication):
94 """Add an application to a card profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +010095
Harald Weltec91085e2022-02-10 18:05:45 +010096 Args:
97 app : CardApplication instance to be added to profile
98 """
99 self.applications.append(app)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100100
Harald Weltec91085e2022-02-10 18:05:45 +0100101 def interpret_sw(self, sw: str):
102 """Interpret a given status word within the profile.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 Args:
105 sw : Status word as string of 4 hex digits
Philipp Maiera028c7d2021-11-08 16:12:03 +0100106
Harald Weltec91085e2022-02-10 18:05:45 +0100107 Returns:
108 Tuple of two strings
109 """
110 return interpret_sw(self.sw, sw)
Philipp Maiera028c7d2021-11-08 16:12:03 +0100111
Harald Weltec91085e2022-02-10 18:05:45 +0100112 @staticmethod
113 def decode_select_response(data_hex: str) -> object:
114 """Decode the response to a SELECT command.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100115
Harald Weltec91085e2022-02-10 18:05:45 +0100116 This is the fall-back method which doesn't perform any decoding. It mostly
117 exists so specific derived classes can overload it for actual decoding.
118 This method is implemented in the profile and is only used when application
119 specific decoding cannot be performed (no ADF is selected).
Philipp Maiera028c7d2021-11-08 16:12:03 +0100120
Harald Weltec91085e2022-02-10 18:05:45 +0100121 Args:
122 data_hex: Hex string of the select response
123 """
124 return data_hex
Philipp Maiera028c7d2021-11-08 16:12:03 +0100125
Harald Weltec91085e2022-02-10 18:05:45 +0100126 @staticmethod
127 @abc.abstractmethod
128 def match_with_card(scc: SimCardCommands) -> bool:
129 """Check if the specific profile matches the card. This method is a
130 placeholder that is overloaded by specific dirived classes. The method
131 actively probes the card to make sure the profile class matches the
132 physical card. This usually also means that the card is reset during
133 the process, so this method must not be called at random times. It may
134 only be called on startup.
Philipp Maiera028c7d2021-11-08 16:12:03 +0100135
Harald Weltec91085e2022-02-10 18:05:45 +0100136 Args:
137 scc: SimCardCommands class
138 Returns:
139 match = True, no match = False
140 """
141 return False
Philipp Maiera028c7d2021-11-08 16:12:03 +0100142
Harald Weltec91085e2022-02-10 18:05:45 +0100143 @staticmethod
144 def pick(scc: SimCardCommands):
145 profiles = list(all_subclasses(CardProfile))
146 profiles.sort(key=operator.attrgetter('ORDER'))
Philipp Maiera028c7d2021-11-08 16:12:03 +0100147
Harald Weltec91085e2022-02-10 18:05:45 +0100148 for p in profiles:
149 if p.match_with_card(scc):
150 return p()
Philipp Maiera028c7d2021-11-08 16:12:03 +0100151
Harald Weltec91085e2022-02-10 18:05:45 +0100152 return None