source: trunk/src/ifd/proto-gbp.c @ 964

Revision 964, 9.6 KB checked in by aj, 5 years ago (diff)

indent changes only.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * Implementation of the Gemplus Block Protocol.
3 * This is a simpified version of T=1. The major
4 * difference is that any command sends just *one*
5 * block of data to the reader, and receives *one*
6 * block only.
7 *
8 * Beware, entirely untested!
9 *
10 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
11 */
12
13#include "internal.h"
14#include <sys/poll.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18
19typedef struct {
20        ifd_protocol_t base;
21        int state;
22        int block_oriented;
23
24        unsigned char ns;
25
26        unsigned int timeout, wtx;
27        unsigned int retries;
28} gbp_state_t;
29
30#define GBP_I_BLOCK             0x00
31#define GBP_R_BLOCK             0x80
32#define GBP_S_BLOCK             0xC0
33#define GBP_MORE_BLOCKS         0x20
34
35/* I block */
36#define GBP_I_SEQ_SHIFT         6
37
38/* R block */
39#define GBP_IS_ERROR(pcb)       ((pcb) & 0x0F)
40#define GBP_EDC_ERROR           0x01
41#define GBP_OTHER_ERROR         0x02
42#define GBP_R_SEQ_SHIFT         4
43
44/* S block stuff */
45#define GBP_S_IS_RESPONSE(pcb)  ((pcb) & GBP_S_RESPONSE)
46#define GBP_S_TYPE(pcb)         ((pcb) & 0x0F)
47#define GBP_S_RESPONSE          0x20
48#define GBP_S_RESYNC            0x00
49#define GBP_S_IFS               0x01
50#define GBP_S_ABORT             0x02
51#define GBP_S_WTX               0x03
52
53#define GBP_BUFFER_SIZE         (3 + 256 + 2)
54
55/* internal state, do not mess with it. */
56/* should be != DEAD after reset/init */
57enum {
58        ALIVE, RESYNCH, DEAD
59};
60
61static unsigned int gbp_block_type(unsigned char);
62static unsigned int gbp_seq(unsigned char);
63static unsigned int gbp_build(gbp_state_t *, unsigned char *,
64                              unsigned char, ct_buf_t *);
65static unsigned int gbp_compute_checksum(gbp_state_t *,
66                                         unsigned char *, size_t);
67static int gbp_verify_checksum(gbp_state_t *, unsigned char *, size_t);
68static int gbp_xcv(gbp_state_t *, unsigned char *, size_t, size_t);
69
70/*
71 * Set default GBP protocol parameters
72 */
73static void gbp_set_defaults(gbp_state_t * gp)
74{
75        gp->retries = 3;
76        /* This timeout is rather insane, but we need this right now
77         * to support cryptoflex keygen */
78        gp->timeout = 20000;
79        gp->ns = 0;
80}
81
82/*
83 * Attach GBP protocol
84 */
85static int gbp_init(ifd_protocol_t * prot)
86{
87        gbp_state_t *gp = (gbp_state_t *) prot;
88
89        gbp_set_defaults(gp);
90
91        /* If the device is attached through USB etc, assume the
92         * device will do the framing for us */
93        if (prot->reader->device->type != IFD_DEVICE_TYPE_SERIAL)
94                gp->block_oriented = 1;
95        return 0;
96}
97
98/*
99 * Detach gp protocol
100 */
101static void gbp_release(ifd_protocol_t * prot)
102{
103        /* NOP */
104}
105
106/*
107 * Get/set parmaters for T1 protocol
108 */
109static int gbp_set_param(ifd_protocol_t * prot, int type, long value)
110{
111        gbp_state_t *gp = (gbp_state_t *) prot;
112
113        switch (type) {
114        case IFD_PROTOCOL_RECV_TIMEOUT:
115                gp->timeout = value;
116                break;
117        case IFD_PROTOCOL_BLOCK_ORIENTED:
118                gp->block_oriented = value;
119                break;
120        default:
121                ct_error("Unsupported parameter %d", type);
122                return -1;
123        }
124
125        return 0;
126}
127
128static int gbp_get_param(ifd_protocol_t * prot, int type, long *result)
129{
130        gbp_state_t *gp = (gbp_state_t *) prot;
131        long value;
132
133        switch (type) {
134        case IFD_PROTOCOL_RECV_TIMEOUT:
135                value = gp->timeout;
136                break;
137        case IFD_PROTOCOL_BLOCK_ORIENTED:
138                value = gp->block_oriented;
139                break;
140        default:
141                ct_error("Unsupported parameter %d", type);
142                return -1;
143        }
144
145        if (result)
146                *result = value;
147
148        return 0;
149}
150
151/*
152 * Send an APDU through GBP
153 */
154static int gbp_transceive(ifd_protocol_t * prot, int dad, const void *snd_buf,
155                          size_t snd_len, void *rcv_buf, size_t rcv_len)
156{
157        gbp_state_t *gp = (gbp_state_t *) prot;
158        ct_buf_t sbuf, rbuf;
159        unsigned char sdata[GBP_BUFFER_SIZE];
160        unsigned int slen, retries, resyncs;
161        unsigned char send_seq;
162
163        if (snd_len == 0 || snd_len > 255) {
164                ct_error("GBP: invalid packet length %u\n", snd_len);
165                return -1;
166        }
167
168        retries = gp->retries;
169        resyncs = 3;
170
171        /* Initialize send/recv buffer */
172        ct_buf_set(&sbuf, (void *)snd_buf, snd_len);
173        ct_buf_init(&rbuf, rcv_buf, rcv_len);
174
175        /* Send the first block */
176        slen = gbp_build(gp, sdata, GBP_I_BLOCK, &sbuf);
177
178        send_seq = gp->ns;
179        if (gp->state == DEAD) {
180                gp->ns = 0;
181                slen = gbp_build(gp, sdata, GBP_S_BLOCK | GBP_S_RESYNC, NULL);
182                gp->state = RESYNCH;
183        }
184
185        while (1) {
186                unsigned char pcb;
187                int n;
188
189                if (retries-- == 0)
190                        goto resync;
191
192                if ((n = gbp_xcv(gp, sdata, slen, sizeof(sdata))) < 0) {
193                        ifd_debug(1, "fatal: transmit/receive failed");
194                        gp->state = DEAD;
195                        goto error;
196                }
197
198                if (!gbp_verify_checksum(gp, sdata, n)) {
199                        ifd_debug(1, "checksum failed");
200                        slen =
201                            gbp_build(gp, sdata, GBP_R_BLOCK | GBP_EDC_ERROR,
202                                      NULL);
203                        continue;
204                }
205
206                pcb = sdata[1];
207                switch (gbp_block_type(pcb)) {
208                case GBP_I_BLOCK:
209                        /* I-Block means "Information" - this is the response from
210                         * the card. */
211                        if (gbp_seq(pcb) != send_seq) {
212                                /* If the block sent by the card doesn't match
213                                 * what we expected it to send, reply with
214                                 * an R block */
215                                slen = gbp_build(gp, sdata,
216                                                 GBP_R_BLOCK | GBP_OTHER_ERROR,
217                                                 NULL);
218                                continue;
219                        }
220
221                        /* Advance to next seq nr */
222                        gp->ns ^= 1;
223
224                        if (ct_buf_put(&rbuf, sdata + 3, sdata[2]) < 0)
225                                goto error;
226                        goto done;
227
228                case GBP_R_BLOCK:
229                        /* R-Block means "Repeat" */
230                        if (gbp_seq(pcb) != gp->ns) {
231                                slen = gbp_build(gp, sdata,
232                                                 GBP_R_BLOCK | GBP_OTHER_ERROR,
233                                                 NULL);
234                                continue;
235                        }
236
237                        ifd_debug(1, "received R block%s%s",
238                                  (pcb & GBP_EDC_ERROR) ? ", EDC error" : "",
239                                  (pcb & GBP_OTHER_ERROR) ? ", other error" :
240                                  "");
241
242                        /* Retransmit block */
243                        slen = gbp_build(gp, sdata, GBP_I_BLOCK, &sbuf);
244                        break;
245
246                case GBP_S_BLOCK:
247                        if (GBP_S_IS_RESPONSE(pcb) && gp->state == RESYNCH) {
248                                gp->state = ALIVE;
249                                resyncs = 3;
250                                retries = gp->retries;
251                                slen = gbp_build(gp, sdata, GBP_I_BLOCK, &sbuf);
252                                continue;
253                        }
254
255                        ifd_debug(1, "unexpected S block from reader\n");
256                        goto resync;
257                }
258
259                /* Everything went just splendid */
260                retries = gp->retries;
261                continue;
262
263              resync:
264                /* the number or resyncs is limited, too */
265                if (resyncs == 0)
266                        goto error;
267                resyncs--;
268                gp->ns = 0;
269                slen = gbp_build(gp, sdata, GBP_S_BLOCK | GBP_S_RESYNC, NULL);
270                gp->state = RESYNCH;
271                continue;
272        }
273
274      done:
275        return ct_buf_avail(&rbuf);
276
277      error:
278        gp->state = DEAD;
279        return -1;
280}
281
282static int gbp_resynchronize(ifd_protocol_t * p, int nad)
283{
284        gbp_state_t *gp = (gbp_state_t *) p;
285        unsigned char block[4];
286        unsigned int retries = 3;
287
288        if (p->reader && p->reader->device)
289                ifd_device_flush(p->reader->device);
290
291        while (retries--) {
292                gp->ns = 0;
293
294                block[0] = nad;
295                block[1] = GBP_S_BLOCK | GBP_S_RESYNC;
296                block[2] = 0;
297                gbp_compute_checksum(gp, block, 3);
298
299                if (gbp_xcv(gp, block, 4, sizeof(block)) != 4) {
300                        ifd_debug(1, "fatal: transmit/receive failed");
301                        break;
302                }
303
304                if (!gbp_verify_checksum(gp, block, 4)) {
305                        ifd_debug(1, "checksum failed");
306                        continue;
307                }
308
309                if (block[1] == (GBP_S_BLOCK | GBP_S_RESPONSE | GBP_S_RESYNC)) {
310                        gp->state = ALIVE;
311                        return 0;
312                }
313        }
314
315        gp->state = DEAD;
316        return -1;
317}
318
319static unsigned gbp_block_type(unsigned char pcb)
320{
321        switch (pcb & 0xC0) {
322        case GBP_R_BLOCK:
323                return GBP_R_BLOCK;
324        case GBP_S_BLOCK:
325                return GBP_S_BLOCK;
326        default:
327                return GBP_I_BLOCK;
328        }
329}
330
331static unsigned int gbp_seq(unsigned char pcb)
332{
333        switch (pcb & 0xC0) {
334        case GBP_R_BLOCK:
335                return (pcb >> GBP_R_SEQ_SHIFT) & 1;
336        case GBP_S_BLOCK:
337                return 0;
338        default:
339                return (pcb >> GBP_I_SEQ_SHIFT) & 1;
340        }
341}
342
343static unsigned int gbp_build(gbp_state_t * gp, unsigned char *block,
344                              unsigned char pcb, ct_buf_t * bp)
345{
346        unsigned int len;
347
348        len = bp ? ct_buf_avail(bp) : 0;
349
350        /* Add the sequence number */
351        switch (gbp_block_type(pcb)) {
352        case GBP_R_BLOCK:
353                pcb |= gp->ns << GBP_R_SEQ_SHIFT;
354                break;
355        case GBP_I_BLOCK:
356                pcb |= gp->ns << GBP_I_SEQ_SHIFT;
357                break;
358        }
359
360        block[0] = 0x42;
361        block[1] = pcb;
362        block[2] = len;
363
364        if (len)
365                memcpy(block + 3, ct_buf_head(bp), len);
366
367        return gbp_compute_checksum(gp, block, len + 3);
368}
369
370/*
371 * Protocol struct
372 */
373struct ifd_protocol_ops ifd_protocol_gbp = {
374        IFD_PROTOCOL_GBP,       /* id */
375        "GBP",                  /* name */
376        sizeof(gbp_state_t),    /* size */
377        gbp_init,               /* init */
378        gbp_release,            /* release */
379        gbp_set_param,          /* set_param */
380        gbp_get_param,          /* get_param */
381        gbp_resynchronize,      /* resynchronize */
382        gbp_transceive,         /* transceive */
383        NULL,                   /* sync_read */
384        NULL,                   /* sync_write */
385};
386
387/*
388 * Build/verify checksum
389 */
390static unsigned int gbp_compute_checksum(gbp_state_t * gp, unsigned char *data,
391                                         size_t len)
392{
393        csum_lrc_compute(data, len, data + len);
394        return len + 1;
395}
396
397static int gbp_verify_checksum(gbp_state_t * gp, unsigned char *rbuf,
398                               size_t len)
399{
400        unsigned char csum;
401
402        csum_lrc_compute(rbuf, len, &csum);
403        return csum == 0;
404}
405
406/*
407 * Send/receive block
408 */
409static int gbp_xcv(gbp_state_t * gp, unsigned char *block, size_t slen,
410                   size_t rmax)
411{
412        ifd_protocol_t *prot = &gp->base;
413        ifd_device_t *dev = prot->reader->device;
414        unsigned int rlen, timeout;
415        int n, m;
416
417        if (ct_config.debug >= 3)
418                ifd_debug(3, "sending %s", ct_hexdump(block, slen));
419
420        n = ifd_device_send(dev, block, slen);
421        if (n < 0)
422                return n;
423
424        rlen = 3 + 256 + 1;
425
426        /* timeout. For now our WTX treatment is very dumb */
427        timeout = gp->timeout + 1000 * gp->wtx;
428        gp->wtx = 0;
429
430        if (gp->block_oriented) {
431                /* Note - Linux USB seems to have an off by one error, you
432                 * actually need the + 1 to get the RC byte */
433                rlen++;
434                if (rlen < rmax)
435                        rmax = rlen;
436
437                /* Get the response en bloc */
438                n = ifd_device_recv(dev, block, rmax, timeout);
439                if (n >= 0) {
440                        m = block[2] + 3 + 1;
441                        if (m < n)
442                                n = m;
443                }
444        } else {
445                /* Get the header */
446                if (ifd_device_recv(dev, block, 3, timeout) < 0)
447                        return -1;
448
449                n = block[2] + 1;
450                if (n + 3 > rmax || block[2] >= 254) {
451                        ct_error("receive buffer too small");
452                        return -1;
453                }
454
455                /* Now get the rest */
456                if (ifd_device_recv(dev, block + 3, n, gp->timeout) < 0)
457                        return -1;
458
459                n += 3;
460        }
461
462        if (n >= 0 && ct_config.debug >= 3)
463                ifd_debug(3, "received %s", ct_hexdump(block, n));
464
465        return n;
466}
Note: See TracBrowser for help on using the repository browser.