1 /*
2  * Implementation of T=0
3  *
4  * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
5  */
6 
7 #include "internal.h"
8 #include <sys/poll.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 typedef struct {
14 	ifd_protocol_t base;
15 
16 	int state;
17 	long timeout;
18 	unsigned int block_oriented;
19 	unsigned int max_nulls;
20 } t0_state_t;
21 
22 enum {
23 	IDLE, SENDING, RECEIVING, CONFUSED
24 };
25 
26 static int t0_xcv(ifd_protocol_t *, const void *, size_t, void *, size_t);
27 static int t0_send(ifd_protocol_t *, ct_buf_t *, int);
28 static int t0_recv(ifd_protocol_t *, ct_buf_t *, int, long);
29 static int t0_resynch(t0_state_t *);
30 
31 /*
32  * Set default T=1 protocol parameters
33  */
t0_set_defaults(t0_state_t * t0)34 static void t0_set_defaults(t0_state_t * t0)
35 {
36 	t0->state = IDLE;
37 	t0->timeout = 2000;
38 	t0->max_nulls = 800;
39 }
40 
41 /*
42  * Attach t0 protocol
43  */
t0_init(ifd_protocol_t * prot)44 static int t0_init(ifd_protocol_t * prot)
45 {
46 	t0_set_defaults((t0_state_t *) prot);
47 	return 0;
48 }
49 
50 /*
51  * Detach t0 protocol
52  */
t0_release(ifd_protocol_t * prot)53 static void t0_release(ifd_protocol_t * prot)
54 {
55 	/* NOP */
56 }
57 
58 /*
59  * Get/set parmaters for T1 protocol
60  */
t0_set_param(ifd_protocol_t * prot,int type,long value)61 static int t0_set_param(ifd_protocol_t * prot, int type, long value)
62 {
63 	t0_state_t *t0 = (t0_state_t *) prot;
64 
65 	switch (type) {
66 	case IFD_PROTOCOL_RECV_TIMEOUT:
67 		t0->timeout = value;
68 		break;
69 	case IFD_PROTOCOL_BLOCK_ORIENTED:
70 		t0->block_oriented = value;
71 		break;
72 	default:
73 		ct_error("Unsupported parameter %d", type);
74 		return -1;
75 	}
76 
77 	return 0;
78 }
79 
t0_get_param(ifd_protocol_t * prot,int type,long * result)80 static int t0_get_param(ifd_protocol_t * prot, int type, long *result)
81 {
82 	t0_state_t *t0 = (t0_state_t *) prot;
83 	long value;
84 
85 	switch (type) {
86 	case IFD_PROTOCOL_RECV_TIMEOUT:
87 		value = t0->timeout;
88 		break;
89 	case IFD_PROTOCOL_BLOCK_ORIENTED:
90 		value = t0->block_oriented;
91 		break;
92 	default:
93 		ct_error("Unsupported parameter %d", type);
94 		return -1;
95 	}
96 
97 	if (result)
98 		*result = value;
99 
100 	return 0;
101 }
102 
103 /*
104  * Send an APDU through T=0
105  */
t0_transceive(ifd_protocol_t * prot,int dad,const void * sbuf,size_t slen,void * rbuf,size_t rlen)106 static int t0_transceive(ifd_protocol_t * prot, int dad, const void *sbuf,
107 			 size_t slen, void *rbuf, size_t rlen)
108 {
109 	t0_state_t *t0 = (t0_state_t *) prot;
110 	ifd_iso_apdu_t iso;
111 	unsigned char sdata[5];
112 	unsigned int cla, cse, lc, le;
113 	int rc;
114 
115 	if (t0->state != IDLE) {
116 		if (t0_resynch(t0) < 0)
117 			return -1;
118 		t0->state = IDLE;
119 	}
120 
121 	if (slen < 4 || rlen < 2)
122 		return -1;
123 
124 	/* Check the APDU case etc */
125 	if ((rc = ifd_iso_apdu_parse(sbuf, slen, &iso)) < 0)
126 		return rc;
127 
128 	cse = iso.cse;
129 	cla = iso.cla;
130 	lc = iso.lc;
131 	le = iso.le;
132 
133 	switch (cse) {
134 	case IFD_APDU_CASE_1:
135 		/* Include a NUL lc byte */
136 		memcpy(sdata, sbuf, 4);
137 		sdata[4] = 0;
138 		sbuf = sdata;
139 		slen = 5;
140 		break;
141 	case IFD_APDU_CASE_2S:
142 	case IFD_APDU_CASE_3S:
143 		break;
144 	case IFD_APDU_CASE_4S:
145 		/* Strip off the Le byte */
146 		slen--;
147 		break;
148 	default:
149 		/* We don't handle ext APDUs */
150 		return -1;
151 	}
152 
153 	/*
154 	   if (le + 2 > slen) {
155 	   ct_error("t0_transceive: recv buffer too small");
156 	   return -1;
157 	   }
158 	 */
159 
160 	if (lc) {
161 		t0->state = SENDING;
162 		if ((rc = t0_xcv(prot, sbuf, slen, rbuf, 2)) < 0)
163 			return rc;
164 
165 		/* Can this happen? */
166 		if (rc != 2)
167 			return IFD_ERROR_COMM_ERROR;
168 
169 		/* Case 4 APDU - check whether we should
170 		 * try to get the response */
171 		if (cse == IFD_APDU_CASE_4S) {
172 			unsigned char *sw;
173 
174 			sw = (unsigned char *)rbuf;
175 
176 			if (sw[0] == 0x61) {
177 				/* additional length info */
178 				if (sw[1] != 0 && sw[1] < le)
179 					le = sw[1];
180 			} else if ((sw[0] & 0xF0) == 0x60) {
181 				/* Command not accepted, do not
182 				 * retrieve response
183 				 */
184 				goto done;
185 			}
186 
187 			/* Transmit a Get Response command */
188 			sdata[0] = cla;
189 			sdata[1] = 0xC0;
190 			sdata[2] = 0x00;
191 			sdata[3] = 0x00;
192 			sdata[4] = le;
193 
194 			t0->state = RECEIVING;
195 			rc = t0_xcv(prot, sdata, 5, rbuf, le + 2);
196 		}
197 	} else {
198 		t0->state = RECEIVING;
199 		rc = t0_xcv(prot, sbuf, slen, rbuf, le + 2);
200 	}
201 
202       done:t0->state = IDLE;
203 	return rc;
204 }
205 
t0_xcv(ifd_protocol_t * prot,const void * sdata,size_t slen,void * rdata,size_t rlen)206 static int t0_xcv(ifd_protocol_t * prot, const void *sdata, size_t slen,
207 		  void *rdata, size_t rlen)
208 {
209 	t0_state_t *t0 = (t0_state_t *) prot;
210 	ct_buf_t sbuf, rbuf;
211 	unsigned int null_count = 0;
212 	unsigned int ins;
213 
214 	/* Let the driver handle any chunking etc */
215 	if (t0->block_oriented) {
216 		int rc;
217 
218 		if ((rc = ifd_send_command(prot, sdata, slen)) >= 0)
219 			rc = ifd_recv_response(prot, rdata, rlen, t0->timeout);
220 		return rc;
221 	}
222 
223 	/* Set up the send buffer */
224 	ct_buf_set(&sbuf, (void *)sdata, slen);
225 	ct_buf_init(&rbuf, rdata, rlen);
226 
227 	/* Get the INS */
228 	ins = sbuf.base[1];
229 
230 	if (t0_send(prot, &sbuf, 5) < 0)
231 		goto failed;
232 
233 	while (1) {
234 		unsigned char byte;
235 		int count;
236 
237 		if (ifd_recv_response(prot, &byte, 1, t0->timeout) < 0)
238 			goto failed;
239 
240 		/* Null byte to extend wait time */
241 		if (byte == 0x60) {
242 			usleep(100000);
243 			if (++null_count > t0->max_nulls)
244 				goto failed;
245 			continue;
246 		}
247 
248 		/* ICC sends SW1 SW2 */
249 		if ((byte & 0xF0) == 0x60 || (byte & 0xF0) == 0x90) {
250 			/* Store SW1, then get SW2 and store it */
251 			if (ct_buf_put(&rbuf, &byte, 1) < 0
252 			    || t0_recv(prot, &rbuf, 1, t0->timeout) < 0)
253 				goto failed;
254 
255 			break;
256 		}
257 
258 		/* Send/receive data.
259 		 * ACK byte means transfer everything in one go,
260 		 * ~ACK means do it octet by octet.
261 		 * SCEZ masks off using 0xFE, but the Towitoko
262 		 * driver uses 0x0E.
263 		 * Do we need to make this configurable?
264 		 */
265 		if (((byte ^ ins) & 0xFE) == 0) {
266 			/* Send/recv as much as we can */
267 			count = -1;
268 		} else if (((~byte ^ ins) & 0xFE) == 0) {
269 			count = 1;
270 		} else {
271 			ifd_debug(2, "unexpected byte 0x%02x", byte);
272 			return -1;
273 		}
274 
275 		if (t0->state == SENDING) {
276 			if (t0_send(prot, &sbuf, count) < 0)
277 				goto failed;
278 		} else {
279 			if (t0_recv(prot, &rbuf, count, t0->timeout) < 0)
280 				goto failed;
281 			if (ct_buf_tailroom(&rbuf) == 0)
282 				break;
283 		}
284 	}
285 
286 	return ct_buf_avail(&rbuf);
287 
288       failed:t0->state = CONFUSED;
289 	return -1;
290 }
291 
t0_send(ifd_protocol_t * prot,ct_buf_t * bp,int count)292 static int t0_send(ifd_protocol_t * prot, ct_buf_t * bp, int count)
293 {
294 	int n, avail;
295 
296 	avail = ct_buf_avail(bp);
297 	if (count < 0)
298 		count = avail;
299 	if (count > avail || !avail)
300 		return -1;
301 	n = ifd_send_command(prot, ct_buf_head(bp), count);
302 	if (n >= 0)
303 		ct_buf_get(bp, NULL, count);
304 	return n;
305 }
306 
t0_recv(ifd_protocol_t * prot,ct_buf_t * bp,int count,long timeout)307 static int t0_recv(ifd_protocol_t * prot, ct_buf_t * bp, int count,
308 		   long timeout)
309 {
310 	int n;
311 
312 	if (count < 0)
313 		count = ct_buf_tailroom(bp);
314 	n = ifd_recv_response(prot, ct_buf_tail(bp), count, timeout);
315 	if (n >= 0)
316 		ct_buf_put(bp, NULL, count);
317 	return n;
318 }
319 
t0_resynch(t0_state_t * t0)320 static int t0_resynch(t0_state_t * t0)
321 {
322 	return -1;
323 }
324 
325 /*
326  * Protocol struct
327  */
328 struct ifd_protocol_ops ifd_protocol_t0 = {
329 	IFD_PROTOCOL_T0,	/* id */
330 	"T=0",			/* name */
331 	sizeof(t0_state_t),	/* size */
332 	t0_init,		/* init */
333 	t0_release,		/* release */
334 	t0_set_param,		/* set_param */
335 	t0_get_param,		/* get_param */
336 	NULL,			/* resynchronize */
337 	t0_transceive,		/* transceive */
338 	NULL,			/* sync_read */
339 	NULL,			/* sync_write */
340 };
341