blob: 269ec9cf69471ff396836896bf8c9fc653de6de0 [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
Sylvain Munaut76504e02010-12-07 00:24:32 +01003# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
Sylvain Munaut76504e02010-12-07 00:24:32 +010019import serial
20import time
Philipp Maier92bdd5e2021-02-22 16:14:47 +010021import os.path
Harald Weltef9f8d7a2023-07-09 17:06:16 +020022from typing import Optional
Sylvain Munaut76504e02010-12-07 00:24:32 +010023
24from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010025from pySim.transport import LinkBase
Harald Weltef9f8d7a2023-07-09 17:06:16 +020026from pySim.utils import h2b, b2h, Hexstr, ResTuple
Sylvain Munaut76504e02010-12-07 00:24:32 +010027
28
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010029class SerialSimLink(LinkBase):
Harald Weltec91085e2022-02-10 18:05:45 +010030 """ pySim: Transport Link for serial (RS232) based readers included with simcard"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010031
Harald Weltec91085e2022-02-10 18:05:45 +010032 def __init__(self, device: str = '/dev/ttyUSB0', baudrate: int = 9600, rst: str = '-rts',
33 debug: bool = False, **kwargs):
34 super().__init__(**kwargs)
Philipp Maier30773432023-10-23 10:18:04 +020035 print("Using serial reader interface")
Harald Weltec91085e2022-02-10 18:05:45 +010036 if not os.path.exists(device):
37 raise ValueError("device file %s does not exist -- abort" % device)
38 self._sl = serial.Serial(
39 port=device,
40 parity=serial.PARITY_EVEN,
41 bytesize=serial.EIGHTBITS,
42 stopbits=serial.STOPBITS_TWO,
43 timeout=1,
44 xonxoff=0,
45 rtscts=0,
46 baudrate=baudrate,
47 )
48 self._rst_pin = rst
49 self._debug = debug
50 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010051
Harald Weltec91085e2022-02-10 18:05:45 +010052 def __del__(self):
53 if (hasattr(self, "_sl")):
54 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010055
Harald Welteab6897c2023-07-09 16:21:23 +020056 def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False):
Harald Weltec91085e2022-02-10 18:05:45 +010057 # Direct try
58 existing = False
Sylvain Munautbdca2522010-12-09 13:31:58 +010059
Harald Weltec91085e2022-02-10 18:05:45 +010060 try:
61 self.reset_card()
62 if not newcardonly:
63 return
64 else:
65 existing = True
66 except NoCardError:
67 pass
Sylvain Munautbdca2522010-12-09 13:31:58 +010068
Harald Weltec91085e2022-02-10 18:05:45 +010069 # Poll ...
70 mt = time.time() + timeout if timeout is not None else None
71 pe = 0
Sylvain Munautbdca2522010-12-09 13:31:58 +010072
Harald Weltec91085e2022-02-10 18:05:45 +010073 while (mt is None) or (time.time() < mt):
74 try:
75 time.sleep(0.5)
76 self.reset_card()
77 if not existing:
78 return
79 except NoCardError:
80 existing = False
81 except ProtocolError:
82 if existing:
83 existing = False
84 else:
85 # Tolerate a couple of protocol error ... can happen if
86 # we try when the card is 'half' inserted
87 pe += 1
88 if (pe > 2):
89 raise
Sylvain Munautbdca2522010-12-09 13:31:58 +010090
Harald Weltec91085e2022-02-10 18:05:45 +010091 # Timed out ...
92 raise NoCardError()
Sylvain Munautbdca2522010-12-09 13:31:58 +010093
Harald Weltec91085e2022-02-10 18:05:45 +010094 def connect(self):
95 self.reset_card()
Sylvain Munautbdca2522010-12-09 13:31:58 +010096
Harald Welteab6897c2023-07-09 16:21:23 +020097 def get_atr(self) -> Hexstr:
Harald Weltec91085e2022-02-10 18:05:45 +010098 return self._atr
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030099
Harald Weltec91085e2022-02-10 18:05:45 +0100100 def disconnect(self):
101 pass # Nothing to do really ...
Sylvain Munautbdca2522010-12-09 13:31:58 +0100102
Harald Weltec91085e2022-02-10 18:05:45 +0100103 def reset_card(self):
104 rv = self._reset_card()
105 if rv == 0:
106 raise NoCardError()
107 elif rv < 0:
108 raise ProtocolError()
Philipp Maier621f78c2023-06-01 18:00:54 +0200109 return rv
Sylvain Munaut76504e02010-12-07 00:24:32 +0100110
Harald Weltec91085e2022-02-10 18:05:45 +0100111 def _reset_card(self):
112 self._atr = None
113 rst_meth_map = {
114 'rts': self._sl.setRTS,
115 'dtr': self._sl.setDTR,
116 }
117 rst_val_map = {'+': 0, '-': 1}
Sylvain Munaut76504e02010-12-07 00:24:32 +0100118
Harald Weltec91085e2022-02-10 18:05:45 +0100119 try:
120 rst_meth = rst_meth_map[self._rst_pin[1:]]
121 rst_val = rst_val_map[self._rst_pin[0]]
122 except:
123 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100124
Harald Weltec91085e2022-02-10 18:05:45 +0100125 rst_meth(rst_val)
126 time.sleep(0.1) # 100 ms
127 self._sl.flushInput()
128 rst_meth(rst_val ^ 1)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100129
Harald Weltec91085e2022-02-10 18:05:45 +0100130 b = self._rx_byte()
131 if not b:
132 return 0
133 if ord(b) != 0x3b:
134 return -1
135 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100136
Harald Weltec91085e2022-02-10 18:05:45 +0100137 while ord(b) == 0x3b:
138 b = self._rx_byte()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100139
Harald Weltec91085e2022-02-10 18:05:45 +0100140 if not b:
141 return -1
142 t0 = ord(b)
143 self._dbg_print("T0: 0x%x" % t0)
144 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100145
Harald Weltec91085e2022-02-10 18:05:45 +0100146 for i in range(4):
147 if t0 & (0x10 << i):
148 b = self._rx_byte()
149 self._atr.append(ord(b))
150 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100151
Harald Weltec91085e2022-02-10 18:05:45 +0100152 for i in range(0, t0 & 0xf):
153 b = self._rx_byte()
154 self._atr.append(ord(b))
155 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100156
Harald Weltec91085e2022-02-10 18:05:45 +0100157 while True:
158 x = self._rx_byte()
159 if not x:
160 break
161 self._atr.append(ord(x))
162 self._dbg_print("Extra: %x" % ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100163
Harald Weltec91085e2022-02-10 18:05:45 +0100164 return 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100165
Harald Weltec91085e2022-02-10 18:05:45 +0100166 def _dbg_print(self, s):
167 if self._debug:
168 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100169
Harald Weltec91085e2022-02-10 18:05:45 +0100170 def _tx_byte(self, b):
171 self._sl.write(b)
172 r = self._sl.read()
173 if r != b: # TX and RX are tied, so we must clear the echo
174 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
175 ord(b), '%02x' % ord(r) if r else '(nil)'))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100176
Harald Weltec91085e2022-02-10 18:05:45 +0100177 def _tx_string(self, s):
178 """This is only safe if it's guaranteed the card won't send any data
179 during the time of tx of the string !!!"""
180 self._sl.write(s)
181 r = self._sl.read(len(s))
182 if r != s: # TX and RX are tied, so we must clear the echo
183 raise ProtocolError(
184 "Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100185
Harald Weltec91085e2022-02-10 18:05:45 +0100186 def _rx_byte(self):
187 return self._sl.read()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100188
Harald Weltef9f8d7a2023-07-09 17:06:16 +0200189 def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple:
Sylvain Munaut76504e02010-12-07 00:24:32 +0100190
Harald Weltec91085e2022-02-10 18:05:45 +0100191 pdu = h2b(pdu)
192 data_len = pdu[4] # P3
Sylvain Munaut76504e02010-12-07 00:24:32 +0100193
Harald Weltec91085e2022-02-10 18:05:45 +0100194 # Send first CLASS,INS,P1,P2,P3
195 self._tx_string(pdu[0:5])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100196
Harald Weltec91085e2022-02-10 18:05:45 +0100197 # Wait ack which can be
198 # - INS: Command acked -> go ahead
199 # - 0x60: NULL, just wait some more
200 # - SW1: The card can apparently proceed ...
201 while True:
202 b = self._rx_byte()
203 if ord(b) == pdu[1]:
204 break
205 elif b != '\x60':
206 # Ok, it 'could' be SW1
207 sw1 = b
208 sw2 = self._rx_byte()
209 nil = self._rx_byte()
210 if (sw2 and not nil):
211 return '', b2h(sw1+sw2)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100212
Harald Weltec91085e2022-02-10 18:05:45 +0100213 raise ProtocolError()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100214
Harald Weltec91085e2022-02-10 18:05:45 +0100215 # Send data (if any)
216 if len(pdu) > 5:
217 self._tx_string(pdu[5:])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100218
Harald Weltec91085e2022-02-10 18:05:45 +0100219 # Receive data (including SW !)
220 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
221 to_recv = data_len - len(pdu) + 5 + 2
Sylvain Munaut76504e02010-12-07 00:24:32 +0100222
Harald Weltec91085e2022-02-10 18:05:45 +0100223 data = bytes(0)
224 while (len(data) < to_recv):
225 b = self._rx_byte()
226 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
227 continue
228 if not b:
229 break
230 data += b
Sylvain Munaut76504e02010-12-07 00:24:32 +0100231
Harald Weltec91085e2022-02-10 18:05:45 +0100232 # Split datafield from SW
233 if len(data) < 2:
234 return None, None
235 sw = data[-2:]
236 data = data[0:-2]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100237
Harald Weltec91085e2022-02-10 18:05:45 +0100238 # Return value
239 return b2h(data), b2h(sw)
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200240
Philipp Maier58e89eb2023-10-10 11:59:03 +0200241 def __str__(self) -> str:
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200242 return "serial:%s" % (self._sl.name)