blob: c61b4b53f1634a335f28fdcafcc3b4b507ea0c83 [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
28
29from pySim.exceptions import NoCardError, ProtocolError
30from pySim.utils import h2b, b2h
31
32
33class SerialSimLink(object):
34
35 def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
36 self._sl = serial.Serial(
37 port = device,
38 parity = serial.PARITY_EVEN,
39 bytesize = serial.EIGHTBITS,
40 stopbits = serial.STOPBITS_TWO,
41 timeout = 1,
42 xonxoff = 0,
43 rtscts = 0,
44 baudrate = baudrate,
45 )
46 self._rst_pin = rst
47 self._debug = debug
48
49 rv = self.reset_card()
50 if rv == 0:
51 raise NoCardError()
52 elif rv < 0:
53 raise ProtocolError()
54
55 def __del__(self):
56 self._sl.close()
57
58 def reset_card(self):
59 rst_meth_map = {
60 'rts': self._sl.setRTS,
61 'dtr': self._sl.setDTR,
62 }
63 rst_val_map = { '+':0, '-':1 }
64
65 try:
66 rst_meth = rst_meth_map[self._rst_pin[1:]]
67 rst_val = rst_val_map[self._rst_pin[0]]
68 except:
69 raise ValueError('Invalid reset pin %s' % self._rst_pin);
70
71 rst_meth(rst_val)
72 time.sleep(0.1) # 100 ms
73 self._sl.flushInput()
74 rst_meth(rst_val ^ 1)
75
76 b = self._rx_byte()
77 if not b:
78 return 0
79 if ord(b) != 0x3b:
80 return -1;
81 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
82
83 while ord(b) == 0x3b:
84 b = self._rx_byte()
85
86 if not b:
87 return -1
88 t0 = ord(b)
89 self._dbg_print("T0: 0x%x" % t0)
90
91 for i in range(4):
92 if t0 & (0x10 << i):
93 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(self._rx_byte())))
94
95 for i in range(0, t0 & 0xf):
96 self._dbg_print("Historical = %x" % ord(self._rx_byte()))
97
98 while True:
99 x = self._rx_byte()
100 if not x:
101 break
102 self._dbg_print("Extra: %x" % ord(x))
103
104 return 1
105
106 def _dbg_print(self, s):
107 if self._debug:
108 print s
109
110 def _tx_byte(self, b):
111 self._sl.write(b)
112 r = self._sl.read()
113 if r != b: # TX and RX are tied, so we must clear the echo
114 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
115
116 def _tx_string(self, s):
117 """This is only safe if it's guaranteed the card won't send any data
118 during the time of tx of the string !!!"""
119 self._sl.write(s)
120 r = self._sl.read(len(s))
121 if r != s: # TX and RX are tied, so we must clear the echo
122 raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
123
124 def _rx_byte(self):
125 return self._sl.read()
126
127 def send_apdu_raw(self, pdu):
128 """send_apdu_raw(pdu): Sends an APDU with minimal processing
129
130 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
131 return : tuple(data, sw), where
132 data : string (in hex) of returned data (ex. "074F4EFFFF")
133 sw : string (in hex) of status word (ex. "9000")
134 """
135
136 pdu = h2b(pdu)
137 data_len = ord(pdu[4]) # P3
138
139 # Send first CLASS,INS,P1,P2,P3
140 self._tx_string(pdu[0:5])
141
142 # Wait ack which can be
143 # - INS: Command acked -> go ahead
144 # - 0x60: NULL, just wait some more
145 # - SW1: The card can apparently proceed ...
146 while True:
147 b = self._rx_byte()
148 if b == pdu[1]:
149 break
150 elif b != '\x60':
151 # Ok, it 'could' be SW1
152 sw1 = b
153 sw2 = self._rx_byte()
154 nil = self._rx_byte()
155 if (sw2 and not nil):
156 return '', b2h(sw1+sw2)
157
158 raise ProtocolError()
159
160 # Send data (if any)
161 if len(pdu) > 5:
162 self._tx_string(pdu[5:])
163
164 # Receive data (including SW !)
165 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ]
166 to_recv = data_len - len(pdu) + 5 + 2
167
168 data = ''
169 while (len(data) < to_recv):
170 b = self._rx_byte()
171 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
172 continue
173 if not b:
174 break;
175 data += b
176
177 # Split datafield from SW
178 if len(data) < 2:
179 return None, None
180 sw = data[-2:]
181 data = data[0:-2]
182
183 # Return value
184 return b2h(data), b2h(sw)
185
186 def send_apdu(self, pdu):
187 """send_apdu(pdu): Sends an APDU and auto fetch response data
188
189 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
190 return : tuple(data, sw), where
191 data : string (in hex) of returned data (ex. "074F4EFFFF")
192 sw : string (in hex) of status word (ex. "9000")
193 """
194 data, sw = self.send_apdu_raw(pdu)
195
196 if (sw is not None) and (sw[0:2] == '9f'):
197 pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
198 data, sw = self.send_apdu_raw(pdu_gr)
199
200 return data, sw
201
202 def send_apdu_checksw(self, pdu, sw="9000"):
203 """send_apdu_checksw(pdu,sw): Sends an APDU and check returned SW
204
205 pdu : string of hexadecimal characters (ex. "A0A40000023F00")
206 sw : string of 4 hexadecimal characters (ex. "9000")
207 return : tuple(data, sw), where
208 data : string (in hex) of returned data (ex. "074F4EFFFF")
209 sw : string (in hex) of status word (ex. "9000")
210 """
211 rv = self.send_apdu(pdu)
212 if sw.lower() != rv[1]:
213 raise RuntimeError("SW match failed ! Expected %s and got %s." % (sw.lower(), rv[1]))
214 return rv