blob: f4b1621e2e9b15a2f4e8f31641025215e28b1f9d [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)
35 if not os.path.exists(device):
36 raise ValueError("device file %s does not exist -- abort" % device)
37 self._sl = serial.Serial(
38 port=device,
39 parity=serial.PARITY_EVEN,
40 bytesize=serial.EIGHTBITS,
41 stopbits=serial.STOPBITS_TWO,
42 timeout=1,
43 xonxoff=0,
44 rtscts=0,
45 baudrate=baudrate,
46 )
47 self._rst_pin = rst
48 self._debug = debug
49 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010050
Harald Weltec91085e2022-02-10 18:05:45 +010051 def __del__(self):
52 if (hasattr(self, "_sl")):
53 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010054
Harald Welteab6897c2023-07-09 16:21:23 +020055 def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False):
Harald Weltec91085e2022-02-10 18:05:45 +010056 # Direct try
57 existing = False
Sylvain Munautbdca2522010-12-09 13:31:58 +010058
Harald Weltec91085e2022-02-10 18:05:45 +010059 try:
60 self.reset_card()
61 if not newcardonly:
62 return
63 else:
64 existing = True
65 except NoCardError:
66 pass
Sylvain Munautbdca2522010-12-09 13:31:58 +010067
Harald Weltec91085e2022-02-10 18:05:45 +010068 # Poll ...
69 mt = time.time() + timeout if timeout is not None else None
70 pe = 0
Sylvain Munautbdca2522010-12-09 13:31:58 +010071
Harald Weltec91085e2022-02-10 18:05:45 +010072 while (mt is None) or (time.time() < mt):
73 try:
74 time.sleep(0.5)
75 self.reset_card()
76 if not existing:
77 return
78 except NoCardError:
79 existing = False
80 except ProtocolError:
81 if existing:
82 existing = False
83 else:
84 # Tolerate a couple of protocol error ... can happen if
85 # we try when the card is 'half' inserted
86 pe += 1
87 if (pe > 2):
88 raise
Sylvain Munautbdca2522010-12-09 13:31:58 +010089
Harald Weltec91085e2022-02-10 18:05:45 +010090 # Timed out ...
91 raise NoCardError()
Sylvain Munautbdca2522010-12-09 13:31:58 +010092
Harald Weltec91085e2022-02-10 18:05:45 +010093 def connect(self):
94 self.reset_card()
Sylvain Munautbdca2522010-12-09 13:31:58 +010095
Harald Welteab6897c2023-07-09 16:21:23 +020096 def get_atr(self) -> Hexstr:
Harald Weltec91085e2022-02-10 18:05:45 +010097 return self._atr
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030098
Harald Weltec91085e2022-02-10 18:05:45 +010099 def disconnect(self):
100 pass # Nothing to do really ...
Sylvain Munautbdca2522010-12-09 13:31:58 +0100101
Harald Weltec91085e2022-02-10 18:05:45 +0100102 def reset_card(self):
103 rv = self._reset_card()
104 if rv == 0:
105 raise NoCardError()
106 elif rv < 0:
107 raise ProtocolError()
Philipp Maier621f78c2023-06-01 18:00:54 +0200108 return rv
Sylvain Munaut76504e02010-12-07 00:24:32 +0100109
Harald Weltec91085e2022-02-10 18:05:45 +0100110 def _reset_card(self):
111 self._atr = None
112 rst_meth_map = {
113 'rts': self._sl.setRTS,
114 'dtr': self._sl.setDTR,
115 }
116 rst_val_map = {'+': 0, '-': 1}
Sylvain Munaut76504e02010-12-07 00:24:32 +0100117
Harald Weltec91085e2022-02-10 18:05:45 +0100118 try:
119 rst_meth = rst_meth_map[self._rst_pin[1:]]
120 rst_val = rst_val_map[self._rst_pin[0]]
121 except:
122 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100123
Harald Weltec91085e2022-02-10 18:05:45 +0100124 rst_meth(rst_val)
125 time.sleep(0.1) # 100 ms
126 self._sl.flushInput()
127 rst_meth(rst_val ^ 1)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100128
Harald Weltec91085e2022-02-10 18:05:45 +0100129 b = self._rx_byte()
130 if not b:
131 return 0
132 if ord(b) != 0x3b:
133 return -1
134 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100135
Harald Weltec91085e2022-02-10 18:05:45 +0100136 while ord(b) == 0x3b:
137 b = self._rx_byte()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100138
Harald Weltec91085e2022-02-10 18:05:45 +0100139 if not b:
140 return -1
141 t0 = ord(b)
142 self._dbg_print("T0: 0x%x" % t0)
143 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100144
Harald Weltec91085e2022-02-10 18:05:45 +0100145 for i in range(4):
146 if t0 & (0x10 << i):
147 b = self._rx_byte()
148 self._atr.append(ord(b))
149 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100150
Harald Weltec91085e2022-02-10 18:05:45 +0100151 for i in range(0, t0 & 0xf):
152 b = self._rx_byte()
153 self._atr.append(ord(b))
154 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100155
Harald Weltec91085e2022-02-10 18:05:45 +0100156 while True:
157 x = self._rx_byte()
158 if not x:
159 break
160 self._atr.append(ord(x))
161 self._dbg_print("Extra: %x" % ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100162
Harald Weltec91085e2022-02-10 18:05:45 +0100163 return 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100164
Harald Weltec91085e2022-02-10 18:05:45 +0100165 def _dbg_print(self, s):
166 if self._debug:
167 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100168
Harald Weltec91085e2022-02-10 18:05:45 +0100169 def _tx_byte(self, b):
170 self._sl.write(b)
171 r = self._sl.read()
172 if r != b: # TX and RX are tied, so we must clear the echo
173 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
174 ord(b), '%02x' % ord(r) if r else '(nil)'))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100175
Harald Weltec91085e2022-02-10 18:05:45 +0100176 def _tx_string(self, s):
177 """This is only safe if it's guaranteed the card won't send any data
178 during the time of tx of the string !!!"""
179 self._sl.write(s)
180 r = self._sl.read(len(s))
181 if r != s: # TX and RX are tied, so we must clear the echo
182 raise ProtocolError(
183 "Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100184
Harald Weltec91085e2022-02-10 18:05:45 +0100185 def _rx_byte(self):
186 return self._sl.read()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100187
Harald Weltef9f8d7a2023-07-09 17:06:16 +0200188 def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple:
Sylvain Munaut76504e02010-12-07 00:24:32 +0100189
Harald Weltec91085e2022-02-10 18:05:45 +0100190 pdu = h2b(pdu)
191 data_len = pdu[4] # P3
Sylvain Munaut76504e02010-12-07 00:24:32 +0100192
Harald Weltec91085e2022-02-10 18:05:45 +0100193 # Send first CLASS,INS,P1,P2,P3
194 self._tx_string(pdu[0:5])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100195
Harald Weltec91085e2022-02-10 18:05:45 +0100196 # Wait ack which can be
197 # - INS: Command acked -> go ahead
198 # - 0x60: NULL, just wait some more
199 # - SW1: The card can apparently proceed ...
200 while True:
201 b = self._rx_byte()
202 if ord(b) == pdu[1]:
203 break
204 elif b != '\x60':
205 # Ok, it 'could' be SW1
206 sw1 = b
207 sw2 = self._rx_byte()
208 nil = self._rx_byte()
209 if (sw2 and not nil):
210 return '', b2h(sw1+sw2)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100211
Harald Weltec91085e2022-02-10 18:05:45 +0100212 raise ProtocolError()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100213
Harald Weltec91085e2022-02-10 18:05:45 +0100214 # Send data (if any)
215 if len(pdu) > 5:
216 self._tx_string(pdu[5:])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100217
Harald Weltec91085e2022-02-10 18:05:45 +0100218 # Receive data (including SW !)
219 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
220 to_recv = data_len - len(pdu) + 5 + 2
Sylvain Munaut76504e02010-12-07 00:24:32 +0100221
Harald Weltec91085e2022-02-10 18:05:45 +0100222 data = bytes(0)
223 while (len(data) < to_recv):
224 b = self._rx_byte()
225 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
226 continue
227 if not b:
228 break
229 data += b
Sylvain Munaut76504e02010-12-07 00:24:32 +0100230
Harald Weltec91085e2022-02-10 18:05:45 +0100231 # Split datafield from SW
232 if len(data) < 2:
233 return None, None
234 sw = data[-2:]
235 data = data[0:-2]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100236
Harald Weltec91085e2022-02-10 18:05:45 +0100237 # Return value
238 return b2h(data), b2h(sw)
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200239
240 def __str__(self):
241 return "serial:%s" % (self._sl.name)