| 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 | |
|---|
| 19 | typedef 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 */ |
|---|
| 57 | enum { |
|---|
| 58 | ALIVE, RESYNCH, DEAD |
|---|
| 59 | }; |
|---|
| 60 | |
|---|
| 61 | static unsigned int gbp_block_type(unsigned char); |
|---|
| 62 | static unsigned int gbp_seq(unsigned char); |
|---|
| 63 | static unsigned int gbp_build(gbp_state_t *, unsigned char *, |
|---|
| 64 | unsigned char, ct_buf_t *); |
|---|
| 65 | static unsigned int gbp_compute_checksum(gbp_state_t *, |
|---|
| 66 | unsigned char *, size_t); |
|---|
| 67 | static int gbp_verify_checksum(gbp_state_t *, unsigned char *, size_t); |
|---|
| 68 | static int gbp_xcv(gbp_state_t *, unsigned char *, size_t, size_t); |
|---|
| 69 | |
|---|
| 70 | /* |
|---|
| 71 | * Set default GBP protocol parameters |
|---|
| 72 | */ |
|---|
| 73 | static 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 | */ |
|---|
| 85 | static 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 | */ |
|---|
| 101 | static void gbp_release(ifd_protocol_t * prot) |
|---|
| 102 | { |
|---|
| 103 | /* NOP */ |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | /* |
|---|
| 107 | * Get/set parmaters for T1 protocol |
|---|
| 108 | */ |
|---|
| 109 | static 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 | |
|---|
| 128 | static 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 | */ |
|---|
| 154 | static 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 | |
|---|
| 282 | static 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 | |
|---|
| 319 | static 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 | |
|---|
| 331 | static 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 | |
|---|
| 343 | static 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 | */ |
|---|
| 373 | struct 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 | */ |
|---|
| 390 | static 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 | |
|---|
| 397 | static 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 | */ |
|---|
| 409 | static 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 | } |
|---|