blob: 06a082ab0a24be43320a2a246d89400a381b03d8 [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"""
Harald Weltebaec4e92023-11-03 11:49:54 +010032 name = 'Serial'
Sylvain Munaut76504e02010-12-07 00:24:32 +010033
Harald Welte0f177c12023-12-17 12:38:29 +010034 def __init__(self, opts = argparse.Namespace(device='/dev/ttyUSB0', baudrate=9600), rst: str = '-rts',
Harald Weltec91085e2022-02-10 18:05:45 +010035 debug: bool = False, **kwargs):
36 super().__init__(**kwargs)
Harald Welte0f177c12023-12-17 12:38:29 +010037 if not os.path.exists(opts.device):
38 raise ValueError("device file %s does not exist -- abort" % opts.device)
Harald Weltec91085e2022-02-10 18:05:45 +010039 self._sl = serial.Serial(
Harald Welte0f177c12023-12-17 12:38:29 +010040 port=opts.device,
Harald Weltec91085e2022-02-10 18:05:45 +010041 parity=serial.PARITY_EVEN,
42 bytesize=serial.EIGHTBITS,
43 stopbits=serial.STOPBITS_TWO,
44 timeout=1,
45 xonxoff=0,
46 rtscts=0,
Harald Welte0f177c12023-12-17 12:38:29 +010047 baudrate=opts.baudrate,
Harald Weltec91085e2022-02-10 18:05:45 +010048 )
49 self._rst_pin = rst
50 self._debug = debug
51 self._atr = None
Sylvain Munaut76504e02010-12-07 00:24:32 +010052
Harald Weltec91085e2022-02-10 18:05:45 +010053 def __del__(self):
54 if (hasattr(self, "_sl")):
55 self._sl.close()
Sylvain Munautbdca2522010-12-09 13:31:58 +010056
Harald Welteab6897c2023-07-09 16:21:23 +020057 def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False):
Harald Weltec91085e2022-02-10 18:05:45 +010058 # Direct try
59 existing = False
Sylvain Munautbdca2522010-12-09 13:31:58 +010060
Harald Weltec91085e2022-02-10 18:05:45 +010061 try:
62 self.reset_card()
63 if not newcardonly:
64 return
65 else:
66 existing = True
67 except NoCardError:
68 pass
Sylvain Munautbdca2522010-12-09 13:31:58 +010069
Harald Weltec91085e2022-02-10 18:05:45 +010070 # Poll ...
71 mt = time.time() + timeout if timeout is not None else None
72 pe = 0
Sylvain Munautbdca2522010-12-09 13:31:58 +010073
Harald Weltec91085e2022-02-10 18:05:45 +010074 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
Sylvain Munautbdca2522010-12-09 13:31:58 +010091
Harald Weltec91085e2022-02-10 18:05:45 +010092 # Timed out ...
93 raise NoCardError()
Sylvain Munautbdca2522010-12-09 13:31:58 +010094
Harald Weltec91085e2022-02-10 18:05:45 +010095 def connect(self):
96 self.reset_card()
Sylvain Munautbdca2522010-12-09 13:31:58 +010097
Harald Welteab6897c2023-07-09 16:21:23 +020098 def get_atr(self) -> Hexstr:
Harald Weltec91085e2022-02-10 18:05:45 +010099 return self._atr
Alexander Chemerisd2d660a2017-07-18 16:52:25 +0300100
Harald Weltec91085e2022-02-10 18:05:45 +0100101 def disconnect(self):
102 pass # Nothing to do really ...
Sylvain Munautbdca2522010-12-09 13:31:58 +0100103
Harald Weltec91085e2022-02-10 18:05:45 +0100104 def reset_card(self):
105 rv = self._reset_card()
106 if rv == 0:
107 raise NoCardError()
108 elif rv < 0:
109 raise ProtocolError()
Philipp Maier621f78c2023-06-01 18:00:54 +0200110 return rv
Sylvain Munaut76504e02010-12-07 00:24:32 +0100111
Harald Weltec91085e2022-02-10 18:05:45 +0100112 def _reset_card(self):
113 self._atr = None
114 rst_meth_map = {
115 'rts': self._sl.setRTS,
116 'dtr': self._sl.setDTR,
117 }
118 rst_val_map = {'+': 0, '-': 1}
Sylvain Munaut76504e02010-12-07 00:24:32 +0100119
Harald Weltec91085e2022-02-10 18:05:45 +0100120 try:
121 rst_meth = rst_meth_map[self._rst_pin[1:]]
122 rst_val = rst_val_map[self._rst_pin[0]]
123 except:
124 raise ValueError('Invalid reset pin %s' % self._rst_pin)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100125
Harald Weltec91085e2022-02-10 18:05:45 +0100126 rst_meth(rst_val)
127 time.sleep(0.1) # 100 ms
128 self._sl.flushInput()
129 rst_meth(rst_val ^ 1)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100130
Harald Weltec91085e2022-02-10 18:05:45 +0100131 b = self._rx_byte()
132 if not b:
133 return 0
134 if ord(b) != 0x3b:
135 return -1
136 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100137
Harald Weltec91085e2022-02-10 18:05:45 +0100138 while ord(b) == 0x3b:
139 b = self._rx_byte()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100140
Harald Weltec91085e2022-02-10 18:05:45 +0100141 if not b:
142 return -1
143 t0 = ord(b)
144 self._dbg_print("T0: 0x%x" % t0)
145 self._atr = [0x3b, ord(b)]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100146
Harald Weltec91085e2022-02-10 18:05:45 +0100147 for i in range(4):
148 if t0 & (0x10 << i):
149 b = self._rx_byte()
150 self._atr.append(ord(b))
151 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(b)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100152
Harald Weltec91085e2022-02-10 18:05:45 +0100153 for i in range(0, t0 & 0xf):
154 b = self._rx_byte()
155 self._atr.append(ord(b))
156 self._dbg_print("Historical = %x" % ord(b))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100157
Harald Weltec91085e2022-02-10 18:05:45 +0100158 while True:
159 x = self._rx_byte()
160 if not x:
161 break
162 self._atr.append(ord(x))
163 self._dbg_print("Extra: %x" % ord(x))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100164
Harald Weltec91085e2022-02-10 18:05:45 +0100165 return 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100166
Harald Weltec91085e2022-02-10 18:05:45 +0100167 def _dbg_print(self, s):
168 if self._debug:
169 print(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100170
Harald Weltec91085e2022-02-10 18:05:45 +0100171 def _tx_byte(self, b):
172 self._sl.write(b)
173 r = self._sl.read()
174 if r != b: # TX and RX are tied, so we must clear the echo
175 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (
176 ord(b), '%02x' % ord(r) if r else '(nil)'))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100177
Harald Weltec91085e2022-02-10 18:05:45 +0100178 def _tx_string(self, s):
179 """This is only safe if it's guaranteed the card won't send any data
180 during the time of tx of the string !!!"""
181 self._sl.write(s)
182 r = self._sl.read(len(s))
183 if r != s: # TX and RX are tied, so we must clear the echo
184 raise ProtocolError(
185 "Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100186
Harald Weltec91085e2022-02-10 18:05:45 +0100187 def _rx_byte(self):
188 return self._sl.read()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100189
Harald Weltef9f8d7a2023-07-09 17:06:16 +0200190 def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple:
Sylvain Munaut76504e02010-12-07 00:24:32 +0100191
Harald Weltec91085e2022-02-10 18:05:45 +0100192 pdu = h2b(pdu)
193 data_len = pdu[4] # P3
Sylvain Munaut76504e02010-12-07 00:24:32 +0100194
Harald Weltec91085e2022-02-10 18:05:45 +0100195 # Send first CLASS,INS,P1,P2,P3
196 self._tx_string(pdu[0:5])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100197
Harald Weltec91085e2022-02-10 18:05:45 +0100198 # Wait ack which can be
199 # - INS: Command acked -> go ahead
200 # - 0x60: NULL, just wait some more
201 # - SW1: The card can apparently proceed ...
202 while True:
203 b = self._rx_byte()
204 if ord(b) == pdu[1]:
205 break
206 elif b != '\x60':
207 # Ok, it 'could' be SW1
208 sw1 = b
209 sw2 = self._rx_byte()
210 nil = self._rx_byte()
211 if (sw2 and not nil):
212 return '', b2h(sw1+sw2)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100213
Harald Weltec91085e2022-02-10 18:05:45 +0100214 raise ProtocolError()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100215
Harald Weltec91085e2022-02-10 18:05:45 +0100216 # Send data (if any)
217 if len(pdu) > 5:
218 self._tx_string(pdu[5:])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100219
Harald Weltec91085e2022-02-10 18:05:45 +0100220 # Receive data (including SW !)
221 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1//2) ]
222 to_recv = data_len - len(pdu) + 5 + 2
Sylvain Munaut76504e02010-12-07 00:24:32 +0100223
Harald Weltec91085e2022-02-10 18:05:45 +0100224 data = bytes(0)
225 while (len(data) < to_recv):
226 b = self._rx_byte()
227 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
228 continue
229 if not b:
230 break
231 data += b
Sylvain Munaut76504e02010-12-07 00:24:32 +0100232
Harald Weltec91085e2022-02-10 18:05:45 +0100233 # Split datafield from SW
234 if len(data) < 2:
235 return None, None
236 sw = data[-2:]
237 data = data[0:-2]
Sylvain Munaut76504e02010-12-07 00:24:32 +0100238
Harald Weltec91085e2022-02-10 18:05:45 +0100239 # Return value
240 return b2h(data), b2h(sw)
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200241
Philipp Maier58e89eb2023-10-10 11:59:03 +0200242 def __str__(self) -> str:
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200243 return "serial:%s" % (self._sl.name)
Philipp Maier8c823782023-10-23 10:44:44 +0200244
245 @staticmethod
246 def argparse_add_reader_args(arg_parser: argparse.ArgumentParser):
Harald Welte0ecbf632023-11-03 12:38:42 +0100247 serial_group = arg_parser.add_argument_group('Serial Reader', """Use a simple/ultra-low-cost serial reader
248attached to a (physical or USB/virtual) RS232 port. This doesn't work with all RS232-attached smart card
249readers, only with the very primitive readers following the ancient `Phoenix` or `Smart Mouse` design.""")
Philipp Maier8c823782023-10-23 10:44:44 +0200250 serial_group.add_argument('-d', '--device', metavar='DEV', default='/dev/ttyUSB0',
251 help='Serial Device for SIM access')
252 serial_group.add_argument('-b', '--baud', dest='baudrate', type=int, metavar='BAUD', default=9600,
253 help='Baud rate used for SIM access')