blob: 96ad9742de4a2542cd113b70f88198441717af4b [file] [log] [blame]
Sylvain Munaute7c15cd2010-12-07 10:01:55 +01001# -*- coding: utf-8 -*-
2
3""" pySim: PCSC reader transport link base
4"""
5
Harald Welte6e0458d2021-04-03 11:52:37 +02006from typing import Optional
7
Harald Weltee79cc802021-01-21 14:10:43 +01008from pySim.exceptions import *
Harald Welte67d551a2021-01-21 14:50:01 +01009from pySim.utils import sw_match
Harald Weltee79cc802021-01-21 14:10:43 +010010
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010011#
12# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
13#
14# This program is free software: you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation, either version 2 of the License, or
17# (at your option) any later version.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program. If not, see <http://www.gnu.org/licenses/>.
26#
27
Harald Welte7829d8a2021-04-10 11:28:53 +020028class ApduTracer:
29 def trace_command(self, cmd):
30 pass
31
32 def trace_response(self, cmd, sw, resp):
33 pass
34
35
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010036class LinkBase(object):
Harald Welteee3501f2021-04-02 13:00:18 +020037 """Base class for link/transport to card."""
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010038
Harald Welte7829d8a2021-04-10 11:28:53 +020039 def __init__(self, sw_interpreter=None, apdu_tracer=None):
Harald Welteeb05b2f2021-04-10 11:01:56 +020040 self.sw_interpreter = sw_interpreter
Harald Welte7829d8a2021-04-10 11:28:53 +020041 self.apdu_tracer = apdu_tracer
Harald Welte4f2c5462021-04-03 11:48:22 +020042
43 def set_sw_interpreter(self, interp):
44 """Set an (optional) status word interpreter."""
45 self.sw_interpreter = interp
46
Harald Welteee3501f2021-04-02 13:00:18 +020047 def wait_for_card(self, timeout:int=None, newcardonly:bool=False):
48 """Wait for a card and connect to it
Sylvain Munautbdca2522010-12-09 13:31:58 +010049
Harald Welteee3501f2021-04-02 13:00:18 +020050 Args:
51 timeout : Maximum wait time in seconds (None=no timeout)
52 newcardonly : Should we wait for a new card, or an already inserted one ?
Sylvain Munautbdca2522010-12-09 13:31:58 +010053 """
54 pass
55
56 def connect(self):
Harald Welteee3501f2021-04-02 13:00:18 +020057 """Connect to a card immediately
Sylvain Munautbdca2522010-12-09 13:31:58 +010058 """
59 pass
60
61 def disconnect(self):
Harald Welteee3501f2021-04-02 13:00:18 +020062 """Disconnect from card
Sylvain Munautbdca2522010-12-09 13:31:58 +010063 """
64 pass
65
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010066 def reset_card(self):
Harald Welteee3501f2021-04-02 13:00:18 +020067 """Resets the card (power down/up)
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010068 """
69 pass
70
Harald Welteee3501f2021-04-02 13:00:18 +020071 def send_apdu_raw(self, pdu:str):
72 """Sends an APDU with minimal processing
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010073
Harald Welteee3501f2021-04-02 13:00:18 +020074 Args:
75 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
76 Returns:
77 tuple(data, sw), where
78 data : string (in hex) of returned data (ex. "074F4EFFFF")
79 sw : string (in hex) of status word (ex. "9000")
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010080 """
Harald Welte7829d8a2021-04-10 11:28:53 +020081 if self.apdu_tracer:
82 self.apdu_tracer.trace_command(pdu)
83 (data, sw) = self._send_apdu_raw(pdu)
84 if self.apdu_tracer:
85 self.apdu_tracer.trace_response(pdu, sw, data)
86 return (data, sw)
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010087
88 def send_apdu(self, pdu):
Harald Welteee3501f2021-04-02 13:00:18 +020089 """Sends an APDU and auto fetch response data
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010090
Harald Welteee3501f2021-04-02 13:00:18 +020091 Args:
92 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
93 Returns:
94 tuple(data, sw), where
95 data : string (in hex) of returned data (ex. "074F4EFFFF")
96 sw : string (in hex) of status word (ex. "9000")
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010097 """
98 data, sw = self.send_apdu_raw(pdu)
99
Philipp Maier859e0fd2018-06-12 18:40:24 +0200100 # When whe have sent the first APDU, the SW may indicate that there are response bytes
101 # available. There are two SWs commonly used for this 9fxx (sim) and 61xx (usim), where
102 # xx is the number of response bytes available.
103 # See also:
104 # SW1=9F: 3GPP TS 51.011 9.4.1, Responses to commands which are correctly executed
105 # SW1=61: ISO/IEC 7816-4, Table 5 — General meaning of the interindustry values of SW1-SW2
106 if (sw is not None) and ((sw[0:2] == '9f') or (sw[0:2] == '61')):
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100107 pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
108 data, sw = self.send_apdu_raw(pdu_gr)
109
110 return data, sw
111
112 def send_apdu_checksw(self, pdu, sw="9000"):
Harald Welteee3501f2021-04-02 13:00:18 +0200113 """Sends an APDU and check returned SW
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100114
Harald Welteee3501f2021-04-02 13:00:18 +0200115 Args:
116 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
117 sw : string of 4 hexadecimal characters (ex. "9000"). The user may mask out certain
118 digits using a '?' to add some ambiguity if needed.
119 Returns:
120 tuple(data, sw), where
121 data : string (in hex) of returned data (ex. "074F4EFFFF")
122 sw : string (in hex) of status word (ex. "9000")
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100123 """
124 rv = self.send_apdu(pdu)
Philipp Maierd4ebb6f2018-06-12 17:56:07 +0200125
Harald Welte67d551a2021-01-21 14:50:01 +0100126 if not sw_match(rv[1], sw):
Harald Welte4f2c5462021-04-03 11:48:22 +0200127 raise SwMatchError(rv[1], sw.lower(), self.sw_interpreter)
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100128 return rv
Harald Welte6e0458d2021-04-03 11:52:37 +0200129
Harald Welteeb05b2f2021-04-10 11:01:56 +0200130def init_reader(opts, **kwargs) -> Optional[LinkBase]:
Harald Welte6e0458d2021-04-03 11:52:37 +0200131 """
132 Init card reader driver
133 """
134 sl = None # type : :Optional[LinkBase]
135 try:
136 if opts.pcsc_dev is not None:
137 print("Using PC/SC reader interface")
138 from pySim.transport.pcsc import PcscSimLink
Harald Welteeb05b2f2021-04-10 11:01:56 +0200139 sl = PcscSimLink(opts.pcsc_dev, **kwargs)
Harald Welte6e0458d2021-04-03 11:52:37 +0200140 elif opts.osmocon_sock is not None:
141 print("Using Calypso-based (OsmocomBB) reader interface")
142 from pySim.transport.calypso import CalypsoSimLink
Harald Welteeb05b2f2021-04-10 11:01:56 +0200143 sl = CalypsoSimLink(sock_path=opts.osmocon_sock, **kwargs)
Harald Welte6e0458d2021-04-03 11:52:37 +0200144 elif opts.modem_dev is not None:
145 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
146 from pySim.transport.modem_atcmd import ModemATCommandLink
Harald Welteeb05b2f2021-04-10 11:01:56 +0200147 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud, **kwargs)
Harald Welte6e0458d2021-04-03 11:52:37 +0200148 else: # Serial reader is default
149 print("Using serial reader interface")
150 from pySim.transport.serial import SerialSimLink
Harald Welteeb05b2f2021-04-10 11:01:56 +0200151 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate, **kwargs)
Harald Welte6e0458d2021-04-03 11:52:37 +0200152 return sl
153 except Exception as e:
154 print("Card reader initialization failed with exception:\n" + str(e))
155 return None