blob: b841b3ee5eeb56316202e4a81ce228bcca83be51 [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" pySim: Transport Link for serial (RS232) based readers included with simcard
5"""
6
7#
8# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
24from __future__ import absolute_import
25
26import serial
27import time
Philipp Maier92bdd5e2021-02-22 16:14:47 +010028import os.path
Sylvain Munaut76504e02010-12-07 00:24:32 +010029
30from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010031from pySim.transport import LinkBase
Sylvain Munaut76504e02010-12-07 00:24:32 +010032from pySim.utils import h2b, b2h
33
34
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010035class SerialSimLink(LinkBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +010036
37 def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
Philipp Maier92bdd5e2021-02-22 16:14:47 +010038 if not os.path.exists(device):
39 raise ValueError("device file %s does not exist -- abort" % device)
Sylvain Munaut76504e02010-12-07 00:24:32 +010040 self._sl = serial.Serial(
41 port = device,
42 parity = serial.PARITY_EVEN,
43 bytesize = serial.EIGHTBITS,
44 stopbits = serial.STOPBITS_TWO,
45 timeout = 1,
46 xonxoff = 0,
47 rtscts = 0,
48 baudrate = baudrate,
49 )
50 self._rst_pin = rst
51 self._debug = debug
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030052 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010053
Sylvain Munautbdca2522010-12-09 13:31:58 +010054 def __del__(self):
55 self._sl.close()
56
57 def wait_for_card(self, timeout=None, newcardonly=False):
58 # Direct try
59 existing = False
60
61 try:
62 self.reset_card()
63 if not newcardonly:
64 return
65 else:
66 existing = True
67 except NoCardError:
68 pass
69
70 # Poll ...
71 mt = time.time() + timeout if timeout is not None else None
72 pe = 0
73
74 while (mt is None) or (time.time() < mt):
75 try:
76 time.sleep(0.5)
77 self.reset_card()
78 if not existing:
79 return
80 except NoCardError:
81 existing = False
82 except ProtocolError:
83 if existing:
84 existing = False
85 else:
86 # Tolerate a couple of protocol error ... can happen if
87 # we try when the card is 'half' inserted
88 pe += 1
89 if (pe > 2):
90 raise
91
92 # Timed out ...
93 raise NoCardError()
94
95 def connect(self):
96 self.reset_card()
97
Alexander Chemerisd2d660a2017-07-18 16:52:25 +030098 def get_atr(self):
99 return self._atr
100
Sylvain Munautbdca2522010-12-09 13:31:58 +0100101 def disconnect(self):
102 pass # Nothing to do really ...
103
104 def reset_card(self):
105 rv = self._reset_card()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100106 if rv == 0:
107 raise NoCardError()
108 elif rv < 0:
109 raise ProtocolError()
110
Sylvain Munautbdca2522010-12-09 13:31:58 +0100111 def _reset_card(self):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300112 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100113 rst_meth_map = {
114 'rts': self._sl.setRTS,
115 'dtr': self._sl.setDTR,
116 }
117 rst_val_map = { '+':0, '-':1 }
118
119 try:
120 rst_meth = rst_meth_map[self._rst_pin[1:]]
121 rst_val = rst_val_map[self._rst_pin[0]]
122 except:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200123 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100124
125 rst_meth(rst_val)
126 time.sleep(0.1) # 100 ms
127 self._sl.flushInput()
128 rst_meth(rst_val ^ 1)
129
130 b = self._rx_byte()
131 if not b:
132 return 0
133 if ord(b) != 0x3b:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200134 return -1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100135 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
136
137 while ord(b) == 0x3b:
138 b = self._rx_byte()
139
140 if not b:
141 return -1
142 t0 = ord(b)
143 self._dbg_print("T0: 0x%x" % t0)
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300144 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100145
146 for i in range(4):
147 if t0 & (0x10 << i):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300148 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100149 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300150 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100151
152 for i in range(0, t0 & 0xf):
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300153 b = self._rx_byte()
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100154 self._atr.append(ord(b))
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300155 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100156
157 while True:
158 x = self._rx_byte()
159 if not x:
160 break
Martin Hauke6cbecaa2018-02-18 17:04:50 +0100161 self._atr.append(ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100162 self._dbg_print("Extra: %x" % ord(x))
163
164 return 1
165
166 def _dbg_print(self, s):
167 if self._debug:
Vadim Yanitskiy6727f0c2020-01-22 23:38:24 +0700168 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100169
170 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)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
175
176 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("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
183
184 def _rx_byte(self):
185 return self._sl.read()
186
187 def send_apdu_raw(self, pdu):
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100188 """see LinkBase.send_apdu_raw"""
Sylvain Munaut76504e02010-12-07 00:24:32 +0100189
190 pdu = h2b(pdu)
191 data_len = ord(pdu[4]) # P3
192
193 # Send first CLASS,INS,P1,P2,P3
194 self._tx_string(pdu[0:5])
195
196 # 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 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)
211
212 raise ProtocolError()
213
214 # Send data (if any)
215 if len(pdu) > 5:
216 self._tx_string(pdu[5:])
217
218 # Receive data (including SW !)
Vadim Yanitskiy99affe12020-02-15 05:03:09 +0700219 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100220 to_recv = data_len - len(pdu) + 5 + 2
221
222 data = ''
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:
Daniel Willmann677d41b2020-10-19 10:34:31 +0200228 break
Sylvain Munaut76504e02010-12-07 00:24:32 +0100229 data += b
230
231 # Split datafield from SW
232 if len(data) < 2:
233 return None, None
234 sw = data[-2:]
235 data = data[0:-2]
236
237 # Return value
238 return b2h(data), b2h(sw)