blob: 6d393036ade6a91b93c5c0304ff71b90584d1729 [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
Sylvain Munaut76504e02010-12-07 00:24:32 +010022
23from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010024from pySim.transport import LinkBase
Sylvain Munaut76504e02010-12-07 00:24:32 +010025from pySim.utils import h2b, b2h
26
27
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010028class SerialSimLink(LinkBase):
Harald Welteee3501f2021-04-02 13:00:18 +020029 """ pySim: Transport Link for serial (RS232) based readers included with simcard"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010030
Harald Welteee3501f2021-04-02 13:00:18 +020031 def __init__(self, device:str='/dev/ttyUSB0', baudrate:int=9600, rst:str='-rts',
32 debug:bool=False):
Philipp Maier92bdd5e2021-02-22 16:14:47 +010033 if not os.path.exists(device):
34 raise ValueError("device file %s does not exist -- abort" % device)
Sylvain Munaut76504e02010-12-07 00:24:32 +010035 self._sl = serial.Serial(
36 port = device,
37 parity = serial.PARITY_EVEN,
38 bytesize = serial.EIGHTBITS,
39 stopbits = serial.STOPBITS_TWO,
40 timeout = 1,
41 xonxoff = 0,
42 rtscts = 0,
43 baudrate = baudrate,
44 )
45 self._rst_pin = rst
46 self._debug = debug
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030047 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010048
Sylvain Munautbdca2522010-12-09 13:31:58 +010049 def __del__(self):
Philipp Maier289fd282021-02-22 16:20:43 +010050 if (hasattr(self, "_sl")):
51 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010052
53 def wait_for_card(self, timeout=None, newcardonly=False):
54 # Direct try
55 existing = False
56
57 try:
58 self.reset_card()
59 if not newcardonly:
60 return
61 else:
62 existing = True
63 except NoCardError:
64 pass
65
66 # Poll ...
67 mt = time.time() + timeout if timeout is not None else None
68 pe = 0
69
70 while (mt is None) or (time.time() < mt):
71 try:
72 time.sleep(0.5)
73 self.reset_card()
74 if not existing:
75 return
76 except NoCardError:
77 existing = False
78 except ProtocolError:
79 if existing:
80 existing = False
81 else:
82 # Tolerate a couple of protocol error ... can happen if
83 # we try when the card is 'half' inserted
84 pe += 1
85 if (pe > 2):
86 raise
87
88 # Timed out ...
89 raise NoCardError()
90
91 def connect(self):
92 self.reset_card()
93
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030094 def get_atr(self):
95 return self._atr
96
Sylvain Munautbdca2522010-12-09 13:31:58 +010097 def disconnect(self):
98 pass # Nothing to do really ...
99
100 def reset_card(self):
101 rv = self._reset_card()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100102 if rv == 0:
103 raise NoCardError()
104 elif rv < 0:
105 raise ProtocolError()
106
Sylvain Munautbdca2522010-12-09 13:31:58 +0100107 def _reset_card(self):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300108 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100109 rst_meth_map = {
110 'rts': self._sl.setRTS,
111 'dtr': self._sl.setDTR,
112 }
113 rst_val_map = { '+':0, '-':1 }
114
115 try:
116 rst_meth = rst_meth_map[self._rst_pin[1:]]
117 rst_val = rst_val_map[self._rst_pin[0]]
118 except:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200119 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100120
121 rst_meth(rst_val)
122 time.sleep(0.1) # 100 ms
123 self._sl.flushInput()
124 rst_meth(rst_val ^ 1)
125
126 b = self._rx_byte()
127 if not b:
128 return 0
129 if ord(b) != 0x3b:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200130 return -1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100131 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
132
133 while ord(b) == 0x3b:
134 b = self._rx_byte()
135
136 if not b:
137 return -1
138 t0 = ord(b)
139 self._dbg_print("T0: 0x%x" % t0)
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300140 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100141
142 for i in range(4):
143 if t0 & (0x10 << i):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300144 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100145 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300146 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100147
148 for i in range(0, t0 & 0xf):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300149 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100150 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300151 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100152
153 while True:
154 x = self._rx_byte()
155 if not x:
156 break
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100157 self._atr.append(ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100158 self._dbg_print("Extra: %x" % ord(x))
159
160 return 1
161
162 def _dbg_print(self, s):
163 if self._debug:
Vadim Yanitskiy6727f0c2020-01-22 23:38:24 +0700164 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100165
166 def _tx_byte(self, b):
167 self._sl.write(b)
168 r = self._sl.read()
169 if r != b: # TX and RX are tied, so we must clear the echo
170 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
171
172 def _tx_string(self, s):
173 """This is only safe if it's guaranteed the card won't send any data
174 during the time of tx of the string !!!"""
175 self._sl.write(s)
176 r = self._sl.read(len(s))
177 if r != s: # TX and RX are tied, so we must clear the echo
178 raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
179
180 def _rx_byte(self):
181 return self._sl.read()
182
183 def send_apdu_raw(self, pdu):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100184
185 pdu = h2b(pdu)
186 data_len = ord(pdu[4]) # P3
187
188 # Send first CLASS,INS,P1,P2,P3
189 self._tx_string(pdu[0:5])
190
191 # Wait ack which can be
192 # - INS: Command acked -> go ahead
193 # - 0x60: NULL, just wait some more
194 # - SW1: The card can apparently proceed ...
195 while True:
196 b = self._rx_byte()
197 if b == pdu[1]:
198 break
199 elif b != '\x60':
200 # Ok, it 'could' be SW1
201 sw1 = b
202 sw2 = self._rx_byte()
203 nil = self._rx_byte()
204 if (sw2 and not nil):
205 return '', b2h(sw1+sw2)
206
207 raise ProtocolError()
208
209 # Send data (if any)
210 if len(pdu) > 5:
211 self._tx_string(pdu[5:])
212
213 # Receive data (including SW !)
Vadim Yanitskiy99affe12020-02-15 05:03:09 +0700214 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100215 to_recv = data_len - len(pdu) + 5 + 2
216
217 data = ''
218 while (len(data) < to_recv):
219 b = self._rx_byte()
220 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
221 continue
222 if not b:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200223 break
Sylvain Munaut76504e02010-12-07 00:24:32 +0100224 data += b
225
226 # Split datafield from SW
227 if len(data) < 2:
228 return None, None
229 sw = data[-2:]
230 data = data[0:-2]
231
232 # Return value
233 return b2h(data), b2h(sw)