blob: 03d3f389d30f6cafe1b8b04a56b0ea7b1d7492a0 [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
3""" pySim: Transport Link for serial (RS232) based readers included with simcard
4"""
5
6#
7# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22
Sylvain Munaut76504e02010-12-07 00:24:32 +010023import serial
24import time
Philipp Maier92bdd5e2021-02-22 16:14:47 +010025import os.path
Sylvain Munaut76504e02010-12-07 00:24:32 +010026
27from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010028from pySim.transport import LinkBase
Sylvain Munaut76504e02010-12-07 00:24:32 +010029from pySim.utils import h2b, b2h
30
31
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010032class SerialSimLink(LinkBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +010033
34 def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
Philipp Maier92bdd5e2021-02-22 16:14:47 +010035 if not os.path.exists(device):
36 raise ValueError("device file %s does not exist -- abort" % device)
Sylvain Munaut76504e02010-12-07 00:24:32 +010037 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
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030049 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010050
Sylvain Munautbdca2522010-12-09 13:31:58 +010051 def __del__(self):
Philipp Maier289fd282021-02-22 16:20:43 +010052 if (hasattr(self, "_sl")):
53 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010054
55 def wait_for_card(self, timeout=None, newcardonly=False):
56 # Direct try
57 existing = False
58
59 try:
60 self.reset_card()
61 if not newcardonly:
62 return
63 else:
64 existing = True
65 except NoCardError:
66 pass
67
68 # Poll ...
69 mt = time.time() + timeout if timeout is not None else None
70 pe = 0
71
72 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
89
90 # Timed out ...
91 raise NoCardError()
92
93 def connect(self):
94 self.reset_card()
95
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030096 def get_atr(self):
97 return self._atr
98
Sylvain Munautbdca2522010-12-09 13:31:58 +010099 def disconnect(self):
100 pass # Nothing to do really ...
101
102 def reset_card(self):
103 rv = self._reset_card()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100104 if rv == 0:
105 raise NoCardError()
106 elif rv < 0:
107 raise ProtocolError()
108
Sylvain Munautbdca2522010-12-09 13:31:58 +0100109 def _reset_card(self):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300110 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100111 rst_meth_map = {
112 'rts': self._sl.setRTS,
113 'dtr': self._sl.setDTR,
114 }
115 rst_val_map = { '+':0, '-':1 }
116
117 try:
118 rst_meth = rst_meth_map[self._rst_pin[1:]]
119 rst_val = rst_val_map[self._rst_pin[0]]
120 except:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200121 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100122
123 rst_meth(rst_val)
124 time.sleep(0.1) # 100 ms
125 self._sl.flushInput()
126 rst_meth(rst_val ^ 1)
127
128 b = self._rx_byte()
129 if not b:
130 return 0
131 if ord(b) != 0x3b:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200132 return -1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100133 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
134
135 while ord(b) == 0x3b:
136 b = self._rx_byte()
137
138 if not b:
139 return -1
140 t0 = ord(b)
141 self._dbg_print("T0: 0x%x" % t0)
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300142 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100143
144 for i in range(4):
145 if t0 & (0x10 << i):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300146 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100147 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300148 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100149
150 for i in range(0, t0 & 0xf):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300151 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100152 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300153 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100154
155 while True:
156 x = self._rx_byte()
157 if not x:
158 break
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100159 self._atr.append(ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100160 self._dbg_print("Extra: %x" % ord(x))
161
162 return 1
163
164 def _dbg_print(self, s):
165 if self._debug:
Vadim Yanitskiy6727f0c2020-01-22 23:38:24 +0700166 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100167
168 def _tx_byte(self, b):
169 self._sl.write(b)
170 r = self._sl.read()
171 if r != b: # TX and RX are tied, so we must clear the echo
172 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
173
174 def _tx_string(self, s):
175 """This is only safe if it's guaranteed the card won't send any data
176 during the time of tx of the string !!!"""
177 self._sl.write(s)
178 r = self._sl.read(len(s))
179 if r != s: # TX and RX are tied, so we must clear the echo
180 raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
181
182 def _rx_byte(self):
183 return self._sl.read()
184
185 def send_apdu_raw(self, pdu):
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100186 """see LinkBase.send_apdu_raw"""
Sylvain Munaut76504e02010-12-07 00:24:32 +0100187
188 pdu = h2b(pdu)
189 data_len = ord(pdu[4]) # P3
190
191 # Send first CLASS,INS,P1,P2,P3
192 self._tx_string(pdu[0:5])
193
194 # Wait ack which can be
195 # - INS: Command acked -> go ahead
196 # - 0x60: NULL, just wait some more
197 # - SW1: The card can apparently proceed ...
198 while True:
199 b = self._rx_byte()
200 if b == pdu[1]:
201 break
202 elif b != '\x60':
203 # Ok, it 'could' be SW1
204 sw1 = b
205 sw2 = self._rx_byte()
206 nil = self._rx_byte()
207 if (sw2 and not nil):
208 return '', b2h(sw1+sw2)
209
210 raise ProtocolError()
211
212 # Send data (if any)
213 if len(pdu) > 5:
214 self._tx_string(pdu[5:])
215
216 # Receive data (including SW !)
Vadim Yanitskiy99affe12020-02-15 05:03:09 +0700217 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100218 to_recv = data_len - len(pdu) + 5 + 2
219
220 data = ''
221 while (len(data) < to_recv):
222 b = self._rx_byte()
223 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
224 continue
225 if not b:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200226 break
Sylvain Munaut76504e02010-12-07 00:24:32 +0100227 data += b
228
229 # Split datafield from SW
230 if len(data) < 2:
231 return None, None
232 sw = data[-2:]
233 data = data[0:-2]
234
235 # Return value
236 return b2h(data), b2h(sw)