| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | #include "internal.h" |
|---|
| 24 | #include "asn1.h" |
|---|
| 25 | #include "cardctl.h" |
|---|
| 26 | #include <string.h> |
|---|
| 27 | #include <ctype.h> |
|---|
| 28 | #include <time.h> |
|---|
| 29 | #include <stdlib.h> |
|---|
| 30 | |
|---|
| 31 | static struct sc_atr_table tcos_atrs[] = { |
|---|
| 32 | |
|---|
| 33 | { "3B:BA:13:00:81:31:86:5D:00:64:05:0A:02:01:31:80:90:00:8B", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, |
|---|
| 34 | |
|---|
| 35 | { "3B:BA:14:00:81:31:86:5D:00:64:05:14:02:02:31:80:90:00:91", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, |
|---|
| 36 | |
|---|
| 37 | { "3B:BA:96:00:81:31:86:5D:00:64:05:60:02:03:31:80:90:00:66", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, |
|---|
| 38 | |
|---|
| 39 | { "3B:BA:96:00:81:31:86:5D:00:64:05:7B:02:03:31:80:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V2, 0, NULL }, |
|---|
| 40 | |
|---|
| 41 | { "3B:BF:96:00:81:31:FE:5D:00:64:04:11:03:01:31:C0:73:F7:01:D0:00:90:00:7D", NULL, NULL, SC_CARD_TYPE_TCOS_V3, 0, NULL }, |
|---|
| 42 | { NULL, NULL, NULL, 0, 0, NULL } |
|---|
| 43 | }; |
|---|
| 44 | |
|---|
| 45 | static struct sc_card_operations tcos_ops; |
|---|
| 46 | static struct sc_card_driver tcos_drv = { |
|---|
| 47 | "TCOS 3.0", |
|---|
| 48 | "tcos", |
|---|
| 49 | &tcos_ops, |
|---|
| 50 | NULL, 0, NULL |
|---|
| 51 | }; |
|---|
| 52 | |
|---|
| 53 | static const struct sc_card_operations *iso_ops = NULL; |
|---|
| 54 | |
|---|
| 55 | typedef struct tcos_data_st { |
|---|
| 56 | unsigned int pad_flags; |
|---|
| 57 | unsigned int next_sign; |
|---|
| 58 | } tcos_data; |
|---|
| 59 | |
|---|
| 60 | |
|---|
| 61 | static int tcos_finish(sc_card_t *card) |
|---|
| 62 | { |
|---|
| 63 | free(card->drv_data); |
|---|
| 64 | return 0; |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | |
|---|
| 68 | static int tcos_match_card(sc_card_t *card) |
|---|
| 69 | { |
|---|
| 70 | int i; |
|---|
| 71 | |
|---|
| 72 | i = _sc_match_atr(card, tcos_atrs, &card->type); |
|---|
| 73 | if (i < 0) |
|---|
| 74 | return 0; |
|---|
| 75 | return 1; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | |
|---|
| 79 | static int tcos_init(sc_card_t *card) |
|---|
| 80 | { |
|---|
| 81 | unsigned long flags; |
|---|
| 82 | |
|---|
| 83 | tcos_data *data = (tcos_data *) malloc(sizeof(tcos_data)); |
|---|
| 84 | if (!data) return SC_ERROR_OUT_OF_MEMORY; |
|---|
| 85 | |
|---|
| 86 | card->name = "TCOS"; |
|---|
| 87 | card->drv_data = (void *)data; |
|---|
| 88 | card->cla = 0x00; |
|---|
| 89 | |
|---|
| 90 | flags = SC_ALGORITHM_RSA_RAW; |
|---|
| 91 | flags |= SC_ALGORITHM_RSA_PAD_PKCS1; |
|---|
| 92 | flags |= SC_ALGORITHM_RSA_HASH_NONE; |
|---|
| 93 | |
|---|
| 94 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
|---|
| 95 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
|---|
| 96 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
|---|
| 97 | |
|---|
| 98 | if (card->type == SC_CARD_TYPE_TCOS_V3) { |
|---|
| 99 | card->caps |= SC_CARD_CAP_RSA_2048|SC_CARD_CAP_APDU_EXT; |
|---|
| 100 | _sc_card_add_rsa_alg(card, 1280, flags, 0); |
|---|
| 101 | _sc_card_add_rsa_alg(card, 1536, flags, 0); |
|---|
| 102 | _sc_card_add_rsa_alg(card, 1792, flags, 0); |
|---|
| 103 | _sc_card_add_rsa_alg(card, 2048, flags, 0); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | return 0; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | |
|---|
| 113 | |
|---|
| 114 | static int tcos_construct_fci(const sc_file_t *file, |
|---|
| 115 | u8 *out, size_t *outlen) |
|---|
| 116 | { |
|---|
| 117 | u8 *p = out; |
|---|
| 118 | u8 buf[64]; |
|---|
| 119 | size_t n; |
|---|
| 120 | |
|---|
| 121 | |
|---|
| 122 | |
|---|
| 123 | *p++ = 0x6F; |
|---|
| 124 | p++; |
|---|
| 125 | |
|---|
| 126 | |
|---|
| 127 | buf[0] = (file->size >> 8) & 0xFF; |
|---|
| 128 | buf[1] = file->size & 0xFF; |
|---|
| 129 | sc_asn1_put_tag(0x81, buf, 2, p, 16, &p); |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | n = 0; |
|---|
| 133 | buf[n] = file->shareable ? 0x40 : 0; |
|---|
| 134 | switch (file->type) { |
|---|
| 135 | case SC_FILE_TYPE_WORKING_EF: |
|---|
| 136 | break; |
|---|
| 137 | case SC_FILE_TYPE_DF: |
|---|
| 138 | buf[0] |= 0x38; |
|---|
| 139 | break; |
|---|
| 140 | default: |
|---|
| 141 | return SC_ERROR_NOT_SUPPORTED; |
|---|
| 142 | } |
|---|
| 143 | buf[n++] |= file->ef_structure & 7; |
|---|
| 144 | if ( (file->ef_structure & 7) > 1) { |
|---|
| 145 | |
|---|
| 146 | buf[n++] = 0x41; |
|---|
| 147 | buf[n++] = file->record_length; |
|---|
| 148 | } |
|---|
| 149 | sc_asn1_put_tag(0x82, buf, n, p, 8, &p); |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | buf[0] = (file->id >> 8) & 0xFF; |
|---|
| 153 | buf[1] = file->id & 0xFF; |
|---|
| 154 | sc_asn1_put_tag(0x83, buf, 2, p, 16, &p); |
|---|
| 155 | |
|---|
| 156 | |
|---|
| 157 | if (file->type == SC_FILE_TYPE_DF) { |
|---|
| 158 | if (file->namelen) { |
|---|
| 159 | if (file->namelen > 16 || !file->name) |
|---|
| 160 | return SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 161 | sc_asn1_put_tag(0x84, file->name, file->namelen, |
|---|
| 162 | p, 16, &p); |
|---|
| 163 | } |
|---|
| 164 | else { |
|---|
| 165 | |
|---|
| 166 | snprintf ((char *) buf, sizeof(buf)-1, "foo-%lu", |
|---|
| 167 | (unsigned long) time (NULL)); |
|---|
| 168 | sc_asn1_put_tag(0x84, buf, strlen ((char *) buf), p, 16, &p); |
|---|
| 169 | } |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | |
|---|
| 173 | if (file->prop_attr_len && file->prop_attr) { |
|---|
| 174 | n = file->prop_attr_len; |
|---|
| 175 | memcpy(buf, file->prop_attr, n); |
|---|
| 176 | } |
|---|
| 177 | else { |
|---|
| 178 | n = 0; |
|---|
| 179 | buf[n++] = 0x01; |
|---|
| 180 | if (file->type == SC_FILE_TYPE_WORKING_EF) |
|---|
| 181 | buf[n++] = 0x00; |
|---|
| 182 | } |
|---|
| 183 | sc_asn1_put_tag(0x85, buf, n, p, 16, &p); |
|---|
| 184 | |
|---|
| 185 | |
|---|
| 186 | if (file->sec_attr_len && file->sec_attr) { |
|---|
| 187 | memcpy(buf, file->sec_attr, file->sec_attr_len); |
|---|
| 188 | n = file->sec_attr_len; |
|---|
| 189 | } |
|---|
| 190 | else { |
|---|
| 191 | |
|---|
| 192 | memcpy (buf+ 0, "\xa4\x00\x00\x00\xff\xff", 6); |
|---|
| 193 | memcpy (buf+ 6, "\xb0\x00\x00\x00\xff\xff", 6); |
|---|
| 194 | memcpy (buf+12, "\xd6\x00\x00\x00\xff\xff", 6); |
|---|
| 195 | memcpy (buf+18, "\x60\x00\x00\x00\xff\xff", 6); |
|---|
| 196 | n = 24; |
|---|
| 197 | } |
|---|
| 198 | sc_asn1_put_tag(0x86, buf, n, p, sizeof (buf), &p); |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | |
|---|
| 202 | out[1] = p - out - 2; |
|---|
| 203 | |
|---|
| 204 | *outlen = p - out; |
|---|
| 205 | return 0; |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | |
|---|
| 209 | static int tcos_create_file(sc_card_t *card, sc_file_t *file) |
|---|
| 210 | { |
|---|
| 211 | int r; |
|---|
| 212 | size_t len; |
|---|
| 213 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 214 | sc_apdu_t apdu; |
|---|
| 215 | |
|---|
| 216 | len = SC_MAX_APDU_BUFFER_SIZE; |
|---|
| 217 | r = tcos_construct_fci(file, sbuf, &len); |
|---|
| 218 | SC_TEST_RET(card->ctx, r, "tcos_construct_fci() failed"); |
|---|
| 219 | |
|---|
| 220 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); |
|---|
| 221 | apdu.cla |= 0x80; |
|---|
| 222 | apdu.lc = len; |
|---|
| 223 | apdu.datalen = len; |
|---|
| 224 | apdu.data = sbuf; |
|---|
| 225 | |
|---|
| 226 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 227 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 228 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | |
|---|
| 232 | static unsigned int map_operations (int commandbyte ) |
|---|
| 233 | { |
|---|
| 234 | unsigned int op = (unsigned int)-1; |
|---|
| 235 | |
|---|
| 236 | switch ( (commandbyte & 0xfe) ) { |
|---|
| 237 | case 0xe2: op = SC_AC_OP_UPDATE; break; |
|---|
| 238 | case 0x24: op = SC_AC_OP_UPDATE; break; |
|---|
| 239 | case 0xe0: op = SC_AC_OP_CREATE; break; |
|---|
| 240 | case 0xe4: op = SC_AC_OP_DELETE; break; |
|---|
| 241 | case 0xe8: op = SC_AC_OP_WRITE; break; |
|---|
| 242 | case 0x82: op = SC_AC_OP_READ; break; |
|---|
| 243 | case 0xe6: op = SC_AC_OP_WRITE; break; |
|---|
| 244 | case 0x88: op = SC_AC_OP_READ; break; |
|---|
| 245 | case 0x04: op = SC_AC_OP_INVALIDATE; break; |
|---|
| 246 | case 0x2a: op = SC_AC_OP_SELECT; break; |
|---|
| 247 | case 0xb0: op = SC_AC_OP_READ; break; |
|---|
| 248 | case 0xb2: op = SC_AC_OP_READ; break; |
|---|
| 249 | case 0x44: op = SC_AC_OP_REHABILITATE; break; |
|---|
| 250 | case 0xa4: op = SC_AC_OP_SELECT; break; |
|---|
| 251 | case 0xee: op = SC_AC_OP_CREATE; break; |
|---|
| 252 | case 0x2c: op = SC_AC_OP_WRITE; break; |
|---|
| 253 | case 0xd6: op = SC_AC_OP_WRITE; break; |
|---|
| 254 | case 0xdc: op = SC_AC_OP_WRITE; break; |
|---|
| 255 | case 0x20: op = SC_AC_OP_SELECT; break; |
|---|
| 256 | case 0x60: op = SC_AC_OP_CREATE; break; |
|---|
| 257 | } |
|---|
| 258 | return op; |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | |
|---|
| 262 | |
|---|
| 263 | |
|---|
| 264 | |
|---|
| 265 | |
|---|
| 266 | static void parse_sec_attr(sc_card_t *card, |
|---|
| 267 | sc_file_t *file, const u8 *buf, size_t len) |
|---|
| 268 | { |
|---|
| 269 | unsigned int op; |
|---|
| 270 | |
|---|
| 271 | |
|---|
| 272 | sc_file_add_acl_entry (file, SC_AC_OP_LIST_FILES, |
|---|
| 273 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 274 | |
|---|
| 275 | sc_file_add_acl_entry (file, SC_AC_OP_LOCK, |
|---|
| 276 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 277 | for (; len >= 6; len -= 6, buf += 6) { |
|---|
| 278 | |
|---|
| 279 | if (!memcmp(buf, "\xa4\x00\x00\x00\xff\xff", 6)) |
|---|
| 280 | sc_file_add_acl_entry (file, SC_AC_OP_SELECT, |
|---|
| 281 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 282 | else if (!memcmp(buf, "\xb0\x00\x00\x00\xff\xff", 6)) |
|---|
| 283 | sc_file_add_acl_entry (file, SC_AC_OP_READ, |
|---|
| 284 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 285 | else if (!memcmp(buf, "\xd6\x00\x00\x00\xff\xff", 6)) |
|---|
| 286 | sc_file_add_acl_entry (file, SC_AC_OP_UPDATE, |
|---|
| 287 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 288 | else if (!memcmp(buf, "\x60\x00\x00\x00\xff\xff", 6)) { |
|---|
| 289 | sc_file_add_acl_entry (file, SC_AC_OP_WRITE, |
|---|
| 290 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 291 | sc_file_add_acl_entry (file, SC_AC_OP_CREATE, |
|---|
| 292 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 293 | sc_file_add_acl_entry (file, SC_AC_OP_INVALIDATE, |
|---|
| 294 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 295 | sc_file_add_acl_entry (file, SC_AC_OP_REHABILITATE, |
|---|
| 296 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 297 | } |
|---|
| 298 | else { |
|---|
| 299 | |
|---|
| 300 | |
|---|
| 301 | |
|---|
| 302 | |
|---|
| 303 | op = map_operations (buf[0]); |
|---|
| 304 | if (op == (unsigned int)-1) |
|---|
| 305 | { |
|---|
| 306 | sc_debug (card->ctx, |
|---|
| 307 | "Unknown security command byte %02x\n", |
|---|
| 308 | buf[0]); |
|---|
| 309 | continue; |
|---|
| 310 | } |
|---|
| 311 | if (!buf[1]) |
|---|
| 312 | sc_file_add_acl_entry (file, op, |
|---|
| 313 | SC_AC_NONE, |
|---|
| 314 | SC_AC_KEY_REF_NONE); |
|---|
| 315 | else |
|---|
| 316 | sc_file_add_acl_entry (file, op, |
|---|
| 317 | SC_AC_CHV, buf[1]); |
|---|
| 318 | |
|---|
| 319 | if (!buf[2] && !buf[3]) |
|---|
| 320 | sc_file_add_acl_entry (file, op, |
|---|
| 321 | SC_AC_NONE, |
|---|
| 322 | SC_AC_KEY_REF_NONE); |
|---|
| 323 | else |
|---|
| 324 | sc_file_add_acl_entry (file, op, |
|---|
| 325 | SC_AC_TERM, |
|---|
| 326 | (buf[2]<<8)|buf[3]); |
|---|
| 327 | } |
|---|
| 328 | } |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | |
|---|
| 332 | static int tcos_select_file(sc_card_t *card, |
|---|
| 333 | const sc_path_t *in_path, |
|---|
| 334 | sc_file_t **file_out) |
|---|
| 335 | { |
|---|
| 336 | sc_context_t *ctx; |
|---|
| 337 | sc_apdu_t apdu; |
|---|
| 338 | sc_file_t *file=NULL; |
|---|
| 339 | u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; |
|---|
| 340 | int i, r, pathlen; |
|---|
| 341 | |
|---|
| 342 | assert(card != NULL && in_path != NULL); |
|---|
| 343 | ctx=card->ctx; |
|---|
| 344 | memcpy(path, in_path->value, in_path->len); |
|---|
| 345 | pathlen = in_path->len; |
|---|
| 346 | |
|---|
| 347 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x04); |
|---|
| 348 | |
|---|
| 349 | switch (in_path->type) { |
|---|
| 350 | case SC_PATH_TYPE_FILE_ID: |
|---|
| 351 | if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 352 | case SC_PATH_TYPE_FROM_CURRENT: |
|---|
| 353 | apdu.p1 = 9; |
|---|
| 354 | break; |
|---|
| 355 | case SC_PATH_TYPE_DF_NAME: |
|---|
| 356 | apdu.p1 = 4; |
|---|
| 357 | break; |
|---|
| 358 | case SC_PATH_TYPE_PATH: |
|---|
| 359 | apdu.p1 = 8; |
|---|
| 360 | if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) path += 2, pathlen -= 2; |
|---|
| 361 | if (pathlen == 0) apdu.p1 = 0; |
|---|
| 362 | break; |
|---|
| 363 | case SC_PATH_TYPE_PARENT: |
|---|
| 364 | apdu.p1 = 3; |
|---|
| 365 | pathlen = 0; |
|---|
| 366 | break; |
|---|
| 367 | default: |
|---|
| 368 | SC_FUNC_RETURN(ctx, 2, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 369 | } |
|---|
| 370 | if( pathlen == 0 ) apdu.cse = SC_APDU_CASE_2_SHORT; |
|---|
| 371 | |
|---|
| 372 | apdu.lc = pathlen; |
|---|
| 373 | apdu.data = path; |
|---|
| 374 | apdu.datalen = pathlen; |
|---|
| 375 | |
|---|
| 376 | if (file_out != NULL) { |
|---|
| 377 | apdu.resp = buf; |
|---|
| 378 | apdu.resplen = sizeof(buf); |
|---|
| 379 | apdu.le = 256; |
|---|
| 380 | } else { |
|---|
| 381 | apdu.resplen = 0; |
|---|
| 382 | apdu.le = 0; |
|---|
| 383 | apdu.p2 = 0x0C; |
|---|
| 384 | apdu.cse = (pathlen == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; |
|---|
| 385 | } |
|---|
| 386 | |
|---|
| 387 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 388 | SC_TEST_RET(ctx, r, "APDU transmit failed"); |
|---|
| 389 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 390 | if (r || file_out == NULL) SC_FUNC_RETURN(ctx, 2, r); |
|---|
| 391 | |
|---|
| 392 | if (apdu.resplen < 1 || apdu.resp[0] != 0x62){ |
|---|
| 393 | sc_debug(ctx, "received invalid template %02X\n", apdu.resp[0]); |
|---|
| 394 | SC_FUNC_RETURN(ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | file = sc_file_new(); |
|---|
| 398 | if (file == NULL) SC_FUNC_RETURN(ctx, 0, SC_ERROR_OUT_OF_MEMORY); |
|---|
| 399 | file->path = *in_path; |
|---|
| 400 | |
|---|
| 401 | for(i=2; i+1<apdu.resplen && i+1+apdu.resp[i+1]<apdu.resplen; i+=2+apdu.resp[i+1]){ |
|---|
| 402 | int j, len=apdu.resp[i+1]; |
|---|
| 403 | unsigned char type=apdu.resp[i], *d=apdu.resp+i+2; |
|---|
| 404 | |
|---|
| 405 | switch (type) { |
|---|
| 406 | case 0x80: |
|---|
| 407 | case 0x81: |
|---|
| 408 | file->size=0; |
|---|
| 409 | for(j=0; j<len; ++j) file->size = (file->size<<8) | d[j]; |
|---|
| 410 | break; |
|---|
| 411 | case 0x82: |
|---|
| 412 | file->shareable = (d[0] & 0x40) ? 1 : 0; |
|---|
| 413 | file->ef_structure = d[0] & 7; |
|---|
| 414 | switch ((d[0]>>3) & 7) { |
|---|
| 415 | case 0: file->type = SC_FILE_TYPE_WORKING_EF; break; |
|---|
| 416 | case 7: file->type = SC_FILE_TYPE_DF; break; |
|---|
| 417 | default: |
|---|
| 418 | sc_debug(ctx, "invalid file type %02X in file descriptor\n", d[0]); |
|---|
| 419 | SC_FUNC_RETURN(ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
|---|
| 420 | } |
|---|
| 421 | break; |
|---|
| 422 | case 0x83: |
|---|
| 423 | file->id = (d[0]<<8) | d[1]; |
|---|
| 424 | break; |
|---|
| 425 | case 0x84: |
|---|
| 426 | memcpy(file->name, d, len); |
|---|
| 427 | file->namelen = len; |
|---|
| 428 | break; |
|---|
| 429 | case 0x86: |
|---|
| 430 | sc_file_set_sec_attr(file, d, len); |
|---|
| 431 | break; |
|---|
| 432 | default: |
|---|
| 433 | if (len>0) sc_file_set_prop_attr(file, d, len); |
|---|
| 434 | } |
|---|
| 435 | } |
|---|
| 436 | file->magic = SC_FILE_MAGIC; |
|---|
| 437 | *file_out = file; |
|---|
| 438 | |
|---|
| 439 | parse_sec_attr(card, file, file->sec_attr, file->sec_attr_len); |
|---|
| 440 | |
|---|
| 441 | return 0; |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| 444 | |
|---|
| 445 | static int tcos_list_files(sc_card_t *card, u8 *buf, size_t buflen) |
|---|
| 446 | { |
|---|
| 447 | sc_context_t *ctx; |
|---|
| 448 | sc_apdu_t apdu; |
|---|
| 449 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], p1; |
|---|
| 450 | int r, count = 0; |
|---|
| 451 | |
|---|
| 452 | assert(card != NULL); |
|---|
| 453 | ctx = card->ctx; |
|---|
| 454 | |
|---|
| 455 | for (p1=1; p1<=2; p1++) { |
|---|
| 456 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, p1, 0); |
|---|
| 457 | apdu.cla = 0x80; |
|---|
| 458 | apdu.resp = rbuf; |
|---|
| 459 | apdu.resplen = sizeof(rbuf); |
|---|
| 460 | apdu.le = 256; |
|---|
| 461 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 462 | SC_TEST_RET(ctx, r, "APDU transmit failed"); |
|---|
| 463 | if (apdu.sw1==0x6A && (apdu.sw2==0x82 || apdu.sw2==0x88)) continue; |
|---|
| 464 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 465 | SC_TEST_RET(ctx, r, "List Dir failed"); |
|---|
| 466 | if (apdu.resplen > buflen) return SC_ERROR_BUFFER_TOO_SMALL; |
|---|
| 467 | if(ctx->debug >= 3) sc_debug(ctx, "got %d %s-FileIDs\n", apdu.resplen/2, p1==1 ? "DF" : "EF"); |
|---|
| 468 | |
|---|
| 469 | memcpy(buf, apdu.resp, apdu.resplen); |
|---|
| 470 | buf += apdu.resplen; |
|---|
| 471 | buflen -= apdu.resplen; |
|---|
| 472 | count += apdu.resplen; |
|---|
| 473 | } |
|---|
| 474 | return count; |
|---|
| 475 | } |
|---|
| 476 | |
|---|
| 477 | |
|---|
| 478 | static int tcos_delete_file(sc_card_t *card, const sc_path_t *path) |
|---|
| 479 | { |
|---|
| 480 | int r; |
|---|
| 481 | u8 sbuf[2]; |
|---|
| 482 | sc_apdu_t apdu; |
|---|
| 483 | |
|---|
| 484 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 485 | if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { |
|---|
| 486 | sc_error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); |
|---|
| 487 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 488 | } |
|---|
| 489 | sbuf[0] = path->value[0]; |
|---|
| 490 | sbuf[1] = path->value[1]; |
|---|
| 491 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); |
|---|
| 492 | apdu.cla |= 0x80; |
|---|
| 493 | apdu.lc = 2; |
|---|
| 494 | apdu.datalen = 2; |
|---|
| 495 | apdu.data = sbuf; |
|---|
| 496 | |
|---|
| 497 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 498 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 499 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 500 | } |
|---|
| 501 | |
|---|
| 502 | |
|---|
| 503 | static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) |
|---|
| 504 | { |
|---|
| 505 | sc_context_t *ctx; |
|---|
| 506 | sc_apdu_t apdu; |
|---|
| 507 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p; |
|---|
| 508 | int r, default_key, tcos3; |
|---|
| 509 | tcos_data *data; |
|---|
| 510 | |
|---|
| 511 | assert(card != NULL && env != NULL); |
|---|
| 512 | ctx = card->ctx; |
|---|
| 513 | tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); |
|---|
| 514 | data=(tcos_data *)card->drv_data; |
|---|
| 515 | |
|---|
| 516 | if (se_num || (env->operation!=SC_SEC_OPERATION_DECIPHER && env->operation!=SC_SEC_OPERATION_SIGN)){ |
|---|
| 517 | SC_FUNC_RETURN(ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 518 | } |
|---|
| 519 | if(ctx->debug >= 3){ |
|---|
| 520 | if(!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) sc_debug(ctx, "No Key-Reference in SecEnvironment\n"); |
|---|
| 521 | else sc_debug(ctx, "Key-Reference %02X (len=%d)\n", env->key_ref[0], env->key_ref_len); |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | default_key= !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len==1 && env->key_ref[0]==0x80); |
|---|
| 525 | if(ctx->debug>=3){ |
|---|
| 526 | sc_debug(ctx, "TCOS3:%d PKCS1:%d\n", tcos3, !!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | data->pad_flags = env->algorithm_flags; |
|---|
| 530 | data->next_sign = default_key; |
|---|
| 531 | |
|---|
| 532 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, tcos3 ? 0x41 : 0xC1, 0xB8); |
|---|
| 533 | p = sbuf; |
|---|
| 534 | *p++=0x80; *p++=0x01; *p++=tcos3 ? 0x0A : 0x10; |
|---|
| 535 | if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { |
|---|
| 536 | *p++ = (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) ? 0x83 : 0x84; |
|---|
| 537 | *p++ = env->key_ref_len; |
|---|
| 538 | memcpy(p, env->key_ref, env->key_ref_len); |
|---|
| 539 | p += env->key_ref_len; |
|---|
| 540 | } |
|---|
| 541 | apdu.data = sbuf; |
|---|
| 542 | apdu.lc = apdu.datalen = (p - sbuf); |
|---|
| 543 | |
|---|
| 544 | if ((r=sc_transmit_apdu(card, &apdu))) { |
|---|
| 545 | sc_perror(ctx, r, "APDU transmit failed"); |
|---|
| 546 | return r; |
|---|
| 547 | } |
|---|
| 548 | if (apdu.sw1==0x6A && (apdu.sw2==0x81 || apdu.sw2==0x88)) { |
|---|
| 549 | if (ctx->debug >= 3) sc_debug(ctx, "Detected Signature-Only key\n"); |
|---|
| 550 | if (env->operation==SC_SEC_OPERATION_SIGN && default_key) return SC_SUCCESS; |
|---|
| 551 | } |
|---|
| 552 | SC_FUNC_RETURN(ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
|---|
| 553 | } |
|---|
| 554 | |
|---|
| 555 | |
|---|
| 556 | static int tcos_restore_security_env(sc_card_t *card, int se_num) |
|---|
| 557 | { |
|---|
| 558 | return 0; |
|---|
| 559 | } |
|---|
| 560 | |
|---|
| 561 | |
|---|
| 562 | static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) |
|---|
| 563 | { |
|---|
| 564 | size_t i, dlen=datalen; |
|---|
| 565 | sc_apdu_t apdu; |
|---|
| 566 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 567 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 568 | int tcos3, r; |
|---|
| 569 | |
|---|
| 570 | assert(card != NULL && data != NULL && out != NULL); |
|---|
| 571 | tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); |
|---|
| 572 | |
|---|
| 573 | if (datalen > 255) SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 574 | |
|---|
| 575 | if(((tcos_data *)card->drv_data)->next_sign){ |
|---|
| 576 | if(datalen>48){ |
|---|
| 577 | sc_error(card->ctx, "Data to be signed is too long (TCOS supports max. 48 bytes)\n"); |
|---|
| 578 | SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 579 | } |
|---|
| 580 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); |
|---|
| 581 | memcpy(sbuf, data, datalen); |
|---|
| 582 | dlen=datalen; |
|---|
| 583 | } else { |
|---|
| 584 | int keylen= tcos3 ? 256 : 128; |
|---|
| 585 | sc_format_apdu(card, &apdu, keylen>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); |
|---|
| 586 | for(i=0; i<sizeof(sbuf);++i) sbuf[i]=0xff; |
|---|
| 587 | sbuf[0]=0x02; sbuf[1]=0x00; sbuf[2]=0x01; sbuf[keylen-datalen]=0x00; |
|---|
| 588 | memcpy(sbuf+keylen-datalen+1, data, datalen); |
|---|
| 589 | dlen=keylen+1; |
|---|
| 590 | } |
|---|
| 591 | apdu.resp = rbuf; |
|---|
| 592 | apdu.resplen = sizeof(rbuf); |
|---|
| 593 | apdu.le = tcos3 ? 256 : 128; |
|---|
| 594 | apdu.data = sbuf; |
|---|
| 595 | apdu.lc = apdu.datalen = dlen; |
|---|
| 596 | |
|---|
| 597 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 598 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 599 | if (tcos3 && apdu.p1==0x80 && apdu.sw1==0x6A && apdu.sw2==0x87) { |
|---|
| 600 | int keylen=128; |
|---|
| 601 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A,0x80,0x86); |
|---|
| 602 | for(i=0; i<sizeof(sbuf);++i) sbuf[i]=0xff; |
|---|
| 603 | sbuf[0]=0x02; sbuf[1]=0x00; sbuf[2]=0x01; sbuf[keylen-datalen]=0x00; |
|---|
| 604 | memcpy(sbuf+keylen-datalen+1, data, datalen); |
|---|
| 605 | dlen=keylen+1; |
|---|
| 606 | |
|---|
| 607 | apdu.resp = rbuf; |
|---|
| 608 | apdu.resplen = sizeof(rbuf); |
|---|
| 609 | apdu.le = 128; |
|---|
| 610 | apdu.data = sbuf; |
|---|
| 611 | apdu.lc = apdu.datalen = dlen; |
|---|
| 612 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 613 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 614 | } |
|---|
| 615 | if (apdu.sw1==0x90 && apdu.sw2==0x00) { |
|---|
| 616 | size_t len = apdu.resplen>outlen ? outlen : apdu.resplen; |
|---|
| 617 | memcpy(out, apdu.resp, len); |
|---|
| 618 | SC_FUNC_RETURN(card->ctx, 4, len); |
|---|
| 619 | } |
|---|
| 620 | SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
|---|
| 621 | } |
|---|
| 622 | |
|---|
| 623 | |
|---|
| 624 | static int tcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) |
|---|
| 625 | { |
|---|
| 626 | sc_context_t *ctx; |
|---|
| 627 | sc_apdu_t apdu; |
|---|
| 628 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 629 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 630 | tcos_data *data; |
|---|
| 631 | int tcos3, r; |
|---|
| 632 | |
|---|
| 633 | assert(card != NULL && crgram != NULL && out != NULL); |
|---|
| 634 | ctx = card->ctx; |
|---|
| 635 | tcos3=(card->type==SC_CARD_TYPE_TCOS_V3); |
|---|
| 636 | data=(tcos_data *)card->drv_data; |
|---|
| 637 | |
|---|
| 638 | SC_FUNC_CALLED(ctx, 2); |
|---|
| 639 | if(ctx->debug>=3) sc_debug(ctx, "TCOS3:%d PKCS1:%d\n",tcos3,!!(data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1)); |
|---|
| 640 | |
|---|
| 641 | sc_format_apdu(card, &apdu, crgram_len>255 ? SC_APDU_CASE_4_EXT : SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); |
|---|
| 642 | apdu.resp = rbuf; |
|---|
| 643 | apdu.resplen = sizeof(rbuf); |
|---|
| 644 | apdu.le = crgram_len; |
|---|
| 645 | |
|---|
| 646 | apdu.data = sbuf; |
|---|
| 647 | apdu.lc = apdu.datalen = crgram_len+1; |
|---|
| 648 | sbuf[0] = tcos3 ? 0x00 : ((data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) ? 0x81 : 0x02); |
|---|
| 649 | memcpy(sbuf+1, crgram, crgram_len); |
|---|
| 650 | |
|---|
| 651 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 652 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 653 | |
|---|
| 654 | if (apdu.sw1==0x90 && apdu.sw2==0x00) { |
|---|
| 655 | size_t len= (apdu.resplen>outlen) ? outlen : apdu.resplen; |
|---|
| 656 | int offset=0; |
|---|
| 657 | if(tcos3 && (data->pad_flags & SC_ALGORITHM_RSA_PAD_PKCS1) && apdu.resp[0]==0 && apdu.resp[1]==2){ |
|---|
| 658 | offset=2; while(offset<len && apdu.resp[offset]!=0) ++offset; |
|---|
| 659 | offset=(offset<len-1) ? offset+1 : 0; |
|---|
| 660 | } |
|---|
| 661 | memcpy(out, apdu.resp+offset, len-offset); |
|---|
| 662 | SC_FUNC_RETURN(card->ctx, 2, len-offset); |
|---|
| 663 | } |
|---|
| 664 | SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
|---|
| 665 | } |
|---|
| 666 | |
|---|
| 667 | |
|---|
| 668 | |
|---|
| 669 | |
|---|
| 670 | |
|---|
| 671 | static int tcos_setperm(sc_card_t *card, int enable_nullpin) |
|---|
| 672 | { |
|---|
| 673 | int r; |
|---|
| 674 | sc_apdu_t apdu; |
|---|
| 675 | |
|---|
| 676 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 677 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEE, 0x00, 0x00); |
|---|
| 678 | apdu.cla |= 0x80; |
|---|
| 679 | apdu.lc = 0; |
|---|
| 680 | apdu.datalen = 0; |
|---|
| 681 | apdu.data = NULL; |
|---|
| 682 | |
|---|
| 683 | r = sc_transmit_apdu(card, &apdu); |
|---|
| 684 | SC_TEST_RET(card->ctx, r, "APDU transmit failed"); |
|---|
| 685 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 686 | } |
|---|
| 687 | |
|---|
| 688 | |
|---|
| 689 | static int tcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) |
|---|
| 690 | { |
|---|
| 691 | int r; |
|---|
| 692 | u8 buf[64]; |
|---|
| 693 | size_t len; |
|---|
| 694 | sc_path_t tpath; |
|---|
| 695 | sc_file_t *tfile = NULL; |
|---|
| 696 | |
|---|
| 697 | if (!serial) return SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 698 | |
|---|
| 699 | |
|---|
| 700 | if (card->serialnr.len) { |
|---|
| 701 | memcpy(serial, &card->serialnr, sizeof(*serial)); |
|---|
| 702 | return SC_SUCCESS; |
|---|
| 703 | } |
|---|
| 704 | sc_format_path("3F002F02", &tpath); |
|---|
| 705 | r = sc_select_file(card, &tpath, &tfile); |
|---|
| 706 | if (r < 0) return r; |
|---|
| 707 | |
|---|
| 708 | len = tfile->size; |
|---|
| 709 | sc_file_free(tfile); |
|---|
| 710 | if (len > sizeof(buf) || len < 12) return SC_ERROR_INTERNAL; |
|---|
| 711 | |
|---|
| 712 | r = sc_read_binary(card, 0, buf, len, 0); |
|---|
| 713 | if (r < 0) return r; |
|---|
| 714 | if (buf[0] != 0x5a || buf[1] > len - 2) return SC_ERROR_INTERNAL; |
|---|
| 715 | |
|---|
| 716 | card->serialnr.len = buf[1]; |
|---|
| 717 | memcpy(card->serialnr.value, buf+2, buf[1]); |
|---|
| 718 | memcpy(serial, &card->serialnr, sizeof(*serial)); |
|---|
| 719 | |
|---|
| 720 | return SC_SUCCESS; |
|---|
| 721 | } |
|---|
| 722 | |
|---|
| 723 | |
|---|
| 724 | static int tcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) |
|---|
| 725 | { |
|---|
| 726 | switch (cmd) { |
|---|
| 727 | case SC_CARDCTL_TCOS_SETPERM: |
|---|
| 728 | return tcos_setperm(card, !!ptr); |
|---|
| 729 | case SC_CARDCTL_GET_SERIALNR: |
|---|
| 730 | return tcos_get_serialnr(card, (sc_serial_number_t *)ptr); |
|---|
| 731 | } |
|---|
| 732 | return SC_ERROR_NOT_SUPPORTED; |
|---|
| 733 | } |
|---|
| 734 | |
|---|
| 735 | |
|---|
| 736 | struct sc_card_driver * sc_get_tcos_driver(void) |
|---|
| 737 | { |
|---|
| 738 | struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); |
|---|
| 739 | |
|---|
| 740 | if (iso_ops == NULL) iso_ops = iso_drv->ops; |
|---|
| 741 | tcos_ops = *iso_drv->ops; |
|---|
| 742 | |
|---|
| 743 | tcos_ops.match_card = tcos_match_card; |
|---|
| 744 | tcos_ops.init = tcos_init; |
|---|
| 745 | tcos_ops.finish = tcos_finish; |
|---|
| 746 | tcos_ops.create_file = tcos_create_file; |
|---|
| 747 | tcos_ops.set_security_env = tcos_set_security_env; |
|---|
| 748 | tcos_ops.select_file = tcos_select_file; |
|---|
| 749 | tcos_ops.list_files = tcos_list_files; |
|---|
| 750 | tcos_ops.delete_file = tcos_delete_file; |
|---|
| 751 | tcos_ops.set_security_env = tcos_set_security_env; |
|---|
| 752 | tcos_ops.compute_signature = tcos_compute_signature; |
|---|
| 753 | tcos_ops.decipher = tcos_decipher; |
|---|
| 754 | tcos_ops.restore_security_env = tcos_restore_security_env; |
|---|
| 755 | tcos_ops.card_ctl = tcos_card_ctl; |
|---|
| 756 | |
|---|
| 757 | return &tcos_drv; |
|---|
| 758 | } |
|---|