| 1 | /* |
|---|
| 2 | * Driver for Cherry smartboard |
|---|
| 3 | * |
|---|
| 4 | * This was written just by looking at the Smartboard protocol |
|---|
| 5 | * on the wire. |
|---|
| 6 | * |
|---|
| 7 | * Some notes on the Smartboard protocol. |
|---|
| 8 | * The basic message format seems to be |
|---|
| 9 | * |
|---|
| 10 | * 00 [len] [code] |
|---|
| 11 | * |
|---|
| 12 | * 00 Seems to be some general "I'm okay, you're okay" byte. |
|---|
| 13 | * Never seen anything else, but maybe it can be used to |
|---|
| 14 | * convey some critical errors etc. |
|---|
| 15 | * len One byte; length of following data |
|---|
| 16 | * code One byte; message code. |
|---|
| 17 | * Followed by data |
|---|
| 18 | * |
|---|
| 19 | * I have no clue yet how to use the num block for PIN entry... |
|---|
| 20 | * |
|---|
| 21 | * This driver is alpha code - it works for me with a Cryptoflex card, |
|---|
| 22 | * but that doesn't mean a thing :) |
|---|
| 23 | * |
|---|
| 24 | * Copyright (C) 2003 Olaf Kirch <okir@suse.de> |
|---|
| 25 | */ |
|---|
| 26 | |
|---|
| 27 | #include "internal.h" |
|---|
| 28 | #include <sys/ioctl.h> |
|---|
| 29 | #include <termios.h> |
|---|
| 30 | #include <stdlib.h> |
|---|
| 31 | #include <string.h> |
|---|
| 32 | #include <time.h> |
|---|
| 33 | #include <unistd.h> |
|---|
| 34 | #include "ctbcs.h" |
|---|
| 35 | |
|---|
| 36 | static int smartboard_reset_ct(ifd_reader_t * reader); |
|---|
| 37 | static int smartboard_command(ifd_reader_t *, |
|---|
| 38 | unsigned char, const unsigned char *, size_t, |
|---|
| 39 | unsigned char *, void *, size_t); |
|---|
| 40 | static int __smartboard_cmd(ifd_reader_t *, unsigned char, const void *, |
|---|
| 41 | size_t); |
|---|
| 42 | static int __smartboard_rsp(ifd_reader_t *, unsigned char *, void *, size_t); |
|---|
| 43 | |
|---|
| 44 | /* |
|---|
| 45 | * Initialize the device |
|---|
| 46 | */ |
|---|
| 47 | static int smartboard_open(ifd_reader_t * reader, const char *device_name) |
|---|
| 48 | { |
|---|
| 49 | ifd_device_params_t params; |
|---|
| 50 | ifd_device_t *dev; |
|---|
| 51 | int res; |
|---|
| 52 | |
|---|
| 53 | reader->name = "Cherry Smartboard"; |
|---|
| 54 | reader->nslots = 1; |
|---|
| 55 | reader->slot[0].dad = 0; |
|---|
| 56 | |
|---|
| 57 | if (!(dev = ifd_device_open(device_name))) |
|---|
| 58 | return -1; |
|---|
| 59 | |
|---|
| 60 | ifd_device_flush(dev); |
|---|
| 61 | |
|---|
| 62 | if (ifd_device_type(dev) != IFD_DEVICE_TYPE_SERIAL) { |
|---|
| 63 | ct_error("Smartboard: must be a serial device"); |
|---|
| 64 | return -1; |
|---|
| 65 | } |
|---|
| 66 | if ((res = ifd_device_get_parameters(dev, ¶ms)) < 0) { |
|---|
| 67 | ct_error("Smartboard: failed to get serial config"); |
|---|
| 68 | return res; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | params.serial.bits = 8; |
|---|
| 72 | params.serial.parity = IFD_SERIAL_PARITY_EVEN; |
|---|
| 73 | params.serial.stopbits = 2; |
|---|
| 74 | params.serial.speed = 115200; |
|---|
| 75 | params.serial.check_parity = 1; |
|---|
| 76 | if ((res = ifd_device_set_parameters(dev, ¶ms)) < 0) { |
|---|
| 77 | ct_error("Smartboard: failed to get serial line to 115200/8E2"); |
|---|
| 78 | return res; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | /* Toggle RTS briefly */ |
|---|
| 82 | { |
|---|
| 83 | int bits = 0x4000; |
|---|
| 84 | |
|---|
| 85 | usleep(230000); |
|---|
| 86 | ioctl(dev->fd, TIOCMSET, &bits); |
|---|
| 87 | usleep(230000); |
|---|
| 88 | bits |= TIOCM_DTR; |
|---|
| 89 | ioctl(dev->fd, TIOCMSET, &bits); |
|---|
| 90 | usleep(230000); |
|---|
| 91 | bits |= TIOCM_RTS; |
|---|
| 92 | ioctl(dev->fd, TIOCMSET, &bits); |
|---|
| 93 | usleep(100000); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | ifd_serial_send_break(dev, 500000); |
|---|
| 97 | ifd_device_flush(dev); |
|---|
| 98 | |
|---|
| 99 | reader->device = dev; |
|---|
| 100 | |
|---|
| 101 | /* Reset the CT */ |
|---|
| 102 | if ((res = smartboard_reset_ct(reader)) < 0) |
|---|
| 103 | return res; |
|---|
| 104 | |
|---|
| 105 | return 0; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | /* |
|---|
| 109 | * Reset the card reader |
|---|
| 110 | */ |
|---|
| 111 | static int smartboard_reset_ct(ifd_reader_t * reader) |
|---|
| 112 | { |
|---|
| 113 | unsigned char buffer[128], code; |
|---|
| 114 | int rc; |
|---|
| 115 | |
|---|
| 116 | #if 1 |
|---|
| 117 | /* shutdown reader - occasionally needed before we can init it */ |
|---|
| 118 | rc = smartboard_command(reader, 0x6a, NULL, 0, &code, NULL, 0); |
|---|
| 119 | if (rc < 0) |
|---|
| 120 | return rc; |
|---|
| 121 | #endif |
|---|
| 122 | |
|---|
| 123 | /* init reader */ |
|---|
| 124 | rc = smartboard_command(reader, 0x60, NULL, 0, &code, buffer, |
|---|
| 125 | sizeof(buffer)); |
|---|
| 126 | if (rc < 0) |
|---|
| 127 | return rc; |
|---|
| 128 | if (code != 0x60) { |
|---|
| 129 | ct_error("smartboard_reset_ct, expected status 0x60, got 0x%x", |
|---|
| 130 | code); |
|---|
| 131 | return -1; |
|---|
| 132 | } |
|---|
| 133 | ifd_debug(1, "Detected %.*s", rc, buffer); |
|---|
| 134 | return 0; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | /* |
|---|
| 138 | * Power up the reader |
|---|
| 139 | */ |
|---|
| 140 | static int smartboard_activate(ifd_reader_t * reader) |
|---|
| 141 | { |
|---|
| 142 | ifd_debug(1, "called."); |
|---|
| 143 | return 0; |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | static int smartboard_deactivate(ifd_reader_t * reader) |
|---|
| 147 | { |
|---|
| 148 | ifd_debug(1, "called."); |
|---|
| 149 | return 0; |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | /* |
|---|
| 153 | * Get the card status |
|---|
| 154 | */ |
|---|
| 155 | static int smartboard_card_status(ifd_reader_t * reader, int idx, int *status) |
|---|
| 156 | { |
|---|
| 157 | unsigned char code, buffer[16]; |
|---|
| 158 | int rc; |
|---|
| 159 | |
|---|
| 160 | ifd_debug(1, "slot=%d", idx); |
|---|
| 161 | rc = smartboard_command(reader, 0x65, NULL, 0, &code, buffer, |
|---|
| 162 | sizeof(buffer)); |
|---|
| 163 | if (rc < 0) |
|---|
| 164 | return rc; |
|---|
| 165 | |
|---|
| 166 | *status = 0; |
|---|
| 167 | switch (code) { |
|---|
| 168 | case 0x61: |
|---|
| 169 | /* card absent: 00 00 00 01 |
|---|
| 170 | * card present: 08 00 00 02 |
|---|
| 171 | * after reset: 19 01 00 04 |
|---|
| 172 | */ |
|---|
| 173 | if (rc >= 4 && (buffer[0] & 0x08)) |
|---|
| 174 | *status = IFD_CARD_PRESENT; |
|---|
| 175 | break; |
|---|
| 176 | case 0x65: |
|---|
| 177 | ifd_debug(1, "event: card inserted."); |
|---|
| 178 | *status = IFD_CARD_PRESENT | IFD_CARD_STATUS_CHANGED; |
|---|
| 179 | break; |
|---|
| 180 | case 0x66: |
|---|
| 181 | ifd_debug(1, "event: card removed."); |
|---|
| 182 | *status = IFD_CARD_STATUS_CHANGED; |
|---|
| 183 | break; |
|---|
| 184 | default: |
|---|
| 185 | ct_error("smartboard_card_status: unexpected status code 0x%x", |
|---|
| 186 | code); |
|---|
| 187 | return -1; |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | return 0; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | /* |
|---|
| 194 | * Reset card and get ATR |
|---|
| 195 | */ |
|---|
| 196 | static int smartboard_card_reset(ifd_reader_t * reader, int slot, void *result, |
|---|
| 197 | size_t size) |
|---|
| 198 | { |
|---|
| 199 | unsigned char code; |
|---|
| 200 | int rc; |
|---|
| 201 | |
|---|
| 202 | rc = smartboard_command(reader, 0x65, NULL, 0, &code, result, size); |
|---|
| 203 | if (rc < 0) |
|---|
| 204 | return rc; |
|---|
| 205 | |
|---|
| 206 | rc = smartboard_command(reader, 0x62, NULL, 0, &code, result, size); |
|---|
| 207 | if (rc < 0) |
|---|
| 208 | return rc; |
|---|
| 209 | |
|---|
| 210 | if (code != 0x64) { |
|---|
| 211 | ct_error |
|---|
| 212 | ("smartboard_card_reset: expected status code 0x62, got 0x%x", |
|---|
| 213 | code); |
|---|
| 214 | return -1; |
|---|
| 215 | } |
|---|
| 216 | return rc; |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | /* |
|---|
| 220 | * Select a protocol for communication with the ICC. |
|---|
| 221 | * We cannot use the T=0 driver directly, because it thinks it can |
|---|
| 222 | * talk over the wire. |
|---|
| 223 | */ |
|---|
| 224 | static int smartboard_set_protocol(ifd_reader_t * reader, int nslot, int proto) |
|---|
| 225 | { |
|---|
| 226 | unsigned char cmd_t0[5] = { 0x00, 0x00, 0x0a, 0x00, 0x10 }; |
|---|
| 227 | unsigned char cmd_t1[5] = { 0x10, 0x00, 0x00, 0x75, 0x10 }; |
|---|
| 228 | unsigned char *args, code; |
|---|
| 229 | ifd_slot_t *slot; |
|---|
| 230 | int rc; |
|---|
| 231 | |
|---|
| 232 | slot = &reader->slot[nslot]; |
|---|
| 233 | if (proto == IFD_PROTOCOL_T0) { |
|---|
| 234 | args = cmd_t0; |
|---|
| 235 | } else if (proto == IFD_PROTOCOL_T1) { |
|---|
| 236 | args = cmd_t1; |
|---|
| 237 | } else { |
|---|
| 238 | ct_error("%s: protocol not supported", reader->name); |
|---|
| 239 | return -1; |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | if ((rc = |
|---|
| 243 | smartboard_command(reader, 0x61, args, 5, &code, NULL, 0)) < 0) |
|---|
| 244 | return rc; |
|---|
| 245 | if (code != 0x62) { |
|---|
| 246 | ct_error("smartboard: unexpected status code 0x%x", code); |
|---|
| 247 | return -1; |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | slot->proto = ifd_protocol_new(proto, reader, slot->dad); |
|---|
| 251 | if (slot->proto == NULL) { |
|---|
| 252 | ct_error("%s: internal error", reader->name); |
|---|
| 253 | return -1; |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | /* Tell the protocol handler that we will do the framing */ |
|---|
| 257 | ifd_protocol_set_parameter(slot->proto, IFD_PROTOCOL_BLOCK_ORIENTED, 1); |
|---|
| 258 | |
|---|
| 259 | return 0; |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | #if 0 |
|---|
| 263 | /* |
|---|
| 264 | * Perform a PIN verification |
|---|
| 265 | */ |
|---|
| 266 | static int smartboard_perform_verify(ifd_reader_t * reader, int nslot, |
|---|
| 267 | unsigned int timeout, const char *prompt, |
|---|
| 268 | const unsigned char *data, size_t data_len, |
|---|
| 269 | unsigned char *resp, size_t resp_len) |
|---|
| 270 | { |
|---|
| 271 | ...} |
|---|
| 272 | #endif |
|---|
| 273 | |
|---|
| 274 | /* |
|---|
| 275 | * Simple command |
|---|
| 276 | */ |
|---|
| 277 | static int __smartboard_cmd(ifd_reader_t * reader, unsigned char cmd, |
|---|
| 278 | const void *arg, size_t arg_len) |
|---|
| 279 | { |
|---|
| 280 | unsigned char buffer[257]; |
|---|
| 281 | |
|---|
| 282 | if (arg_len > sizeof(buffer) - 3) |
|---|
| 283 | return -1; |
|---|
| 284 | |
|---|
| 285 | buffer[0] = 0x00; /* never seen anything other than this */ |
|---|
| 286 | buffer[1] = 1 + arg_len; |
|---|
| 287 | buffer[2] = cmd; |
|---|
| 288 | memcpy(buffer + 3, arg, arg_len); |
|---|
| 289 | |
|---|
| 290 | if (ct_config.debug > 1) |
|---|
| 291 | ifd_debug(3, "sending:%s", ct_hexdump(buffer, 3 + arg_len)); |
|---|
| 292 | |
|---|
| 293 | return ifd_device_send(reader->device, buffer, 3 + arg_len); |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | static int __smartboard_rsp(ifd_reader_t * reader, unsigned char *code, |
|---|
| 297 | void *res, size_t res_len) |
|---|
| 298 | { |
|---|
| 299 | unsigned char buffer[257]; |
|---|
| 300 | unsigned int rsp_len = 0, total = 2; |
|---|
| 301 | int rc; |
|---|
| 302 | |
|---|
| 303 | while (rsp_len < total) { |
|---|
| 304 | rc = ifd_device_recv(reader->device, buffer + rsp_len, |
|---|
| 305 | total - rsp_len, -1); |
|---|
| 306 | if (rc < 0) |
|---|
| 307 | return rc; |
|---|
| 308 | if (buffer[0] != 0x00) |
|---|
| 309 | goto bad_reply; |
|---|
| 310 | rsp_len += rc; |
|---|
| 311 | if (rsp_len == 2) { |
|---|
| 312 | if (buffer[1] == 0) |
|---|
| 313 | goto bad_reply; |
|---|
| 314 | total += buffer[1]; |
|---|
| 315 | } |
|---|
| 316 | } |
|---|
| 317 | |
|---|
| 318 | if (total < 3) |
|---|
| 319 | goto bad_reply; |
|---|
| 320 | *code = buffer[2]; |
|---|
| 321 | |
|---|
| 322 | if (ct_config.debug > 1) |
|---|
| 323 | ifd_debug(3, "received:%s", ct_hexdump(buffer, total)); |
|---|
| 324 | |
|---|
| 325 | rsp_len = total - 3; |
|---|
| 326 | if (res_len > rsp_len) |
|---|
| 327 | res_len = rsp_len; |
|---|
| 328 | if (res && res_len) |
|---|
| 329 | memcpy(res, buffer + 3, res_len); |
|---|
| 330 | |
|---|
| 331 | return res_len; |
|---|
| 332 | |
|---|
| 333 | bad_reply: |
|---|
| 334 | ct_error("smartboard: bad reply from device"); |
|---|
| 335 | return -1; |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | static int smartboard_command(ifd_reader_t * reader, unsigned char cmd, |
|---|
| 339 | const unsigned char *arg, size_t arg_len, |
|---|
| 340 | unsigned char *code, void *res, size_t res_len) |
|---|
| 341 | { |
|---|
| 342 | int n = 0, rc; |
|---|
| 343 | |
|---|
| 344 | do { |
|---|
| 345 | if ((rc = __smartboard_cmd(reader, cmd, arg, arg_len)) < 0 |
|---|
| 346 | || (rc = __smartboard_rsp(reader, code, res, res_len)) < 0) |
|---|
| 347 | ct_error("smartboard: transceive error"); |
|---|
| 348 | } while (rc >= 0 && *code == 0x67 && n++ < 3); |
|---|
| 349 | |
|---|
| 350 | return rc; |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | /* |
|---|
| 354 | * Send/receive APDU |
|---|
| 355 | */ |
|---|
| 356 | static int smartboard_send(ifd_reader_t * reader, unsigned int dad, |
|---|
| 357 | const unsigned char *buffer, size_t len) |
|---|
| 358 | { |
|---|
| 359 | ifd_debug(3, "data:%s", ct_hexdump(buffer, len)); |
|---|
| 360 | return __smartboard_cmd(reader, 0x67, buffer, len); |
|---|
| 361 | } |
|---|
| 362 | |
|---|
| 363 | static int smartboard_recv(ifd_reader_t * reader, unsigned int dad, |
|---|
| 364 | unsigned char *buffer, size_t len, long timeout) |
|---|
| 365 | { |
|---|
| 366 | unsigned char code; |
|---|
| 367 | int rc; |
|---|
| 368 | |
|---|
| 369 | ifd_debug(4, "called."); |
|---|
| 370 | |
|---|
| 371 | /* Status code 63 seems to be some sort of wait time extension */ |
|---|
| 372 | while (1) { |
|---|
| 373 | if ((rc = __smartboard_rsp(reader, &code, buffer, len)) < 0) |
|---|
| 374 | return rc; |
|---|
| 375 | if (code != 0x63) |
|---|
| 376 | break; |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | if (code != 0x64) { |
|---|
| 380 | ct_error("smartboard: unexpected status code 0x%x", code); |
|---|
| 381 | return -1; |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | ifd_debug(3, "resp:%s", ct_hexdump(buffer, rc)); |
|---|
| 385 | return rc; |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | /* |
|---|
| 389 | * Driver operations |
|---|
| 390 | */ |
|---|
| 391 | static struct ifd_driver_ops smartboard_driver; |
|---|
| 392 | |
|---|
| 393 | /* |
|---|
| 394 | * Initialize this module |
|---|
| 395 | */ |
|---|
| 396 | void ifd_smartboard_register(void) |
|---|
| 397 | { |
|---|
| 398 | smartboard_driver.open = smartboard_open, |
|---|
| 399 | smartboard_driver.activate = smartboard_activate, |
|---|
| 400 | smartboard_driver.deactivate = smartboard_deactivate, |
|---|
| 401 | smartboard_driver.card_status = smartboard_card_status, |
|---|
| 402 | smartboard_driver.card_reset = smartboard_card_reset, |
|---|
| 403 | #ifdef notyet |
|---|
| 404 | smartboard_driver.perform_verify = smartboard_perform_verify, |
|---|
| 405 | #endif |
|---|
| 406 | smartboard_driver.send = smartboard_send, |
|---|
| 407 | smartboard_driver.recv = smartboard_recv, |
|---|
| 408 | smartboard_driver.set_protocol = smartboard_set_protocol, |
|---|
| 409 | ifd_driver_register("smartboard", &smartboard_driver); |
|---|
| 410 | } |
|---|