blob: e6803befc3527912bebfd210844461c81ec046d4 [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 Maier4af63dc2023-10-26 12:17:32 +020021import os
Philipp Maier8c823782023-10-23 10:44:44 +020022import argparse
Harald Weltef9f8d7a2023-07-09 17:06:16 +020023from typing import Optional
Sylvain Munaut76504e02010-12-07 00:24:32 +010024
25from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010026from pySim.transport import LinkBase
Harald Weltef9f8d7a2023-07-09 17:06:16 +020027from pySim.utils import h2b, b2h, Hexstr, ResTuple
Sylvain Munaut76504e02010-12-07 00:24:32 +010028
29
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010030class SerialSimLink(LinkBase):
Harald Weltec91085e2022-02-10 18:05:45 +010031 """ pySim: Transport Link for serial (RS232) based readers included with simcard"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010032
Harald Welte0f177c12023-12-17 12:38:29 +010033 def __init__(self, opts = argparse.Namespace(device='/dev/ttyUSB0', baudrate=9600), rst: str = '-rts',
Harald Weltec91085e2022-02-10 18:05:45 +010034 debug: bool = False, **kwargs):
35 super().__init__(**kwargs)
Philipp Maier4af63dc2023-10-26 12:17:32 +020036 if os.environ.get('PYSIM_INTEGRATION_TEST') == "1":
37 print("Using serial reader interface")
38 else:
Harald Welte0f177c12023-12-17 12:38:29 +010039 print("Using serial reader interface at port %s" % opts.device)
40 if not os.path.exists(opts.device):
41 raise ValueError("device file %s does not exist -- abort" % opts.device)
Harald Weltec91085e2022-02-10 18:05:45 +010042 self._sl = serial.Serial(
Harald Welte0f177c12023-12-17 12:38:29 +010043 port=opts.device,
Harald Weltec91085e2022-02-10 18:05:45 +010044 parity=serial.PARITY_EVEN,
45 bytesize=serial.EIGHTBITS,
46 stopbits=serial.STOPBITS_TWO,
47 timeout=1,
48 xonxoff=0,
49 rtscts=0,
Harald Welte0f177c12023-12-17 12:38:29 +010050 baudrate=opts.baudrate,
Harald Weltec91085e2022-02-10 18:05:45 +010051 )
52 self._rst_pin = rst
53 self._debug = debug
54 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010055
Harald Weltec91085e2022-02-10 18:05:45 +010056 def __del__(self):
57 if (hasattr(self, "_sl")):
58 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010059
Harald Welteab6897c2023-07-09 16:21:23 +020060 def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False):
Harald Weltec91085e2022-02-10 18:05:45 +010061 # Direct try
62 existing = False
Sylvain Munautbdca2522010-12-09 13:31:58 +010063
Harald Weltec91085e2022-02-10 18:05:45 +010064 try:
65 self.reset_card()
66 if not newcardonly:
67 return
68 else:
69 existing = True
70 except NoCardError:
71 pass
Sylvain Munautbdca2522010-12-09 13:31:58 +010072
Harald Weltec91085e2022-02-10 18:05:45 +010073 # Poll ...
74 mt = time.time() + timeout if timeout is not None else None
75 pe = 0
Sylvain Munautbdca2522010-12-09 13:31:58 +010076
Harald Weltec91085e2022-02-10 18:05:45 +010077 while (mt is None) or (time.time() < mt):
78 try:
79 time.sleep(0.5)
80 self.reset_card()
81 if not existing:
82 return
83 except NoCardError:
84 existing = False
85 except ProtocolError:
86 if existing:
87 existing = False
88 else:
89 # Tolerate a couple of protocol error ... can happen if
90 # we try when the card is 'half' inserted
91 pe += 1
92 if (pe > 2):
93 raise
Sylvain Munautbdca2522010-12-09 13:31:58 +010094
Harald Weltec91085e2022-02-10 18:05:45 +010095 # Timed out ...
96 raise NoCardError()
Sylvain Munautbdca2522010-12-09 13:31:58 +010097
Harald Weltec91085e2022-02-10 18:05:45 +010098 def connect(self):
99 self.reset_card()
Sylvain Munautbdca2522010-12-09 13:31:58 +0100100
Harald Welteab6897c2023-07-09 16:21:23 +0200101 def get_atr(self) -> Hexstr:
Harald Weltec91085e2022-02-10 18:05:45 +0100102 return self._atr
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 def disconnect(self):
105 pass # Nothing to do really ...
Sylvain Munautbdca2522010-12-09 13:31:58 +0100106
Harald Weltec91085e2022-02-10 18:05:45 +0100107 def reset_card(self):
108 rv = self._reset_card()
109 if rv == 0:
110 raise NoCardError()
111 elif rv < 0:
112 raise ProtocolError()
Philipp Maier621f78c2023-06-01 18:00:54 +0200113 return rv
Sylvain Munaut76504e02010-12-07 00:24:32 +0100114
Harald Weltec91085e2022-02-10 18:05:45 +0100115 def _reset_card(self):
116 self._atr = None
117 rst_meth_map = {
118 'rts': self._sl.setRTS,
119 'dtr': self._sl.setDTR,
120 }
121 rst_val_map = {'+': 0, '-': 1}
Sylvain Munaut76504e02010-12-07 00:24:32 +0100122
Harald Weltec91085e2022-02-10 18:05:45 +0100123 try:
124 rst_meth = rst_meth_map[self._rst_pin[1:]]
125 rst_val = rst_val_map[self._rst_pin[0]]
126 except:
127 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100128
Harald Weltec91085e2022-02-10 18:05:45 +0100129 rst_meth(rst_val)
130 time.sleep(0.1) # 100 ms
131 self._sl.flushInput()
132 rst_meth(rst_val ^ 1)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100133
Harald Weltec91085e2022-02-10 18:05:45 +0100134 b = self._rx_byte()
135 if not b:
136 return 0
137 if ord(b) != 0x3b:
138 return -1
139 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100140
Harald Weltec91085e2022-02-10 18:05:45 +0100141 while ord(b) == 0x3b:
142 b = self._rx_byte()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100143
Harald Weltec91085e2022-02-10 18:05:45 +0100144 if not b:
145 return -1
146 t0 = ord(b)
147 self._dbg_print("T0: 0x%x" % t0)
148 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100149
Harald Weltec91085e2022-02-10 18:05:45 +0100150 for i in range(4):
151 if t0 & (0x10 << i):
152 b = self._rx_byte()
153 self._atr.append(ord(b))
154 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100155
Harald Weltec91085e2022-02-10 18:05:45 +0100156 for i in range(0, t0 & 0xf):
157 b = self._rx_byte()
158 self._atr.append(ord(b))
159 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100160
Harald Weltec91085e2022-02-10 18:05:45 +0100161 while True:
162 x = self._rx_byte()
163 if not x:
164 break
165 self._atr.append(ord(x))
166 self._dbg_print("Extra: %x" % ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100167
Harald Weltec91085e2022-02-10 18:05:45 +0100168 return 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100169
Harald Weltec91085e2022-02-10 18:05:45 +0100170 def _dbg_print(self, s):
171 if self._debug:
172 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100173
Harald Weltec91085e2022-02-10 18:05:45 +0100174 def _tx_byte(self, b):
175 self._sl.write(b)
176 r = self._sl.read()
177 if r != b: # TX and RX are tied, so we must clear the echo
178 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
179 ord(b), '%02x' % ord(r) if r else '(nil)'))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100180
Harald Weltec91085e2022-02-10 18:05:45 +0100181 def _tx_string(self, s):
182 """This is only safe if it's guaranteed the card won't send any data
183 during the time of tx of the string !!!"""
184 self._sl.write(s)
185 r = self._sl.read(len(s))
186 if r != s: # TX and RX are tied, so we must clear the echo
187 raise ProtocolError(
188 "Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100189
Harald Weltec91085e2022-02-10 18:05:45 +0100190 def _rx_byte(self):
191 return self._sl.read()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100192
Harald Weltef9f8d7a2023-07-09 17:06:16 +0200193 def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple:
Sylvain Munaut76504e02010-12-07 00:24:32 +0100194
Harald Weltec91085e2022-02-10 18:05:45 +0100195 pdu = h2b(pdu)
196 data_len = pdu[4] # P3
Sylvain Munaut76504e02010-12-07 00:24:32 +0100197
Harald Weltec91085e2022-02-10 18:05:45 +0100198 # Send first CLASS,INS,P1,P2,P3
199 self._tx_string(pdu[0:5])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100200
Harald Weltec91085e2022-02-10 18:05:45 +0100201 # Wait ack which can be
202 # - INS: Command acked -> go ahead
203 # - 0x60: NULL, just wait some more
204 # - SW1: The card can apparently proceed ...
205 while True:
206 b = self._rx_byte()
207 if ord(b) == pdu[1]:
208 break
209 elif b != '\x60':
210 # Ok, it 'could' be SW1
211 sw1 = b
212 sw2 = self._rx_byte()
213 nil = self._rx_byte()
214 if (sw2 and not nil):
215 return '', b2h(sw1+sw2)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100216
Harald Weltec91085e2022-02-10 18:05:45 +0100217 raise ProtocolError()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100218
Harald Weltec91085e2022-02-10 18:05:45 +0100219 # Send data (if any)
220 if len(pdu) > 5:
221 self._tx_string(pdu[5:])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100222
Harald Weltec91085e2022-02-10 18:05:45 +0100223 # Receive data (including SW !)
224 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
225 to_recv = data_len - len(pdu) + 5 + 2
Sylvain Munaut76504e02010-12-07 00:24:32 +0100226
Harald Weltec91085e2022-02-10 18:05:45 +0100227 data = bytes(0)
228 while (len(data) < to_recv):
229 b = self._rx_byte()
230 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
231 continue
232 if not b:
233 break
234 data += b
Sylvain Munaut76504e02010-12-07 00:24:32 +0100235
Harald Weltec91085e2022-02-10 18:05:45 +0100236 # Split datafield from SW
237 if len(data) < 2:
238 return None, None
239 sw = data[-2:]
240 data = data[0:-2]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100241
Harald Weltec91085e2022-02-10 18:05:45 +0100242 # Return value
243 return b2h(data), b2h(sw)
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200244
Philipp Maier58e89eb2023-10-10 11:59:03 +0200245 def __str__(self) -> str:
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200246 return "serial:%s" % (self._sl.name)
Philipp Maier8c823782023-10-23 10:44:44 +0200247
248 @staticmethod
249 def argparse_add_reader_args(arg_parser: argparse.ArgumentParser):
250 serial_group = arg_parser.add_argument_group('Serial Reader')
251 serial_group.add_argument('-d', '--device', metavar='DEV', default='/dev/ttyUSB0',
252 help='Serial Device for SIM access')
253 serial_group.add_argument('-b', '--baud', dest='baudrate', type=int, metavar='BAUD', default=9600,
254 help='Baud rate used for SIM access')