| 31 | | /* SLE44 */ |
| 32 | | { "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_GENERIC, 0, NULL }, |
| 33 | | /* SLE66S */ |
| 34 | | { "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_GENERIC, 0, NULL }, |
| 35 | | /* SLE66CX320P */ |
| 36 | | { "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_GENERIC, 0, NULL }, |
| 37 | | /* SLE66CX322P */ |
| 38 | | { "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_GENERIC, 0, NULL }, |
| | 32 | /* Infineon SLE44 */ |
| | 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 | /* Infineon SLE66S */ |
| | 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 | /* Infineon SLE66CX320P */ |
| | 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 | /* Infoneon SLE66CX322P */ |
| | 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 | /* Philips P5CT072 */ |
| | 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 }, |
| 222 | | unsigned int op = (unsigned int)-1; |
| 223 | | |
| 224 | | switch ( (commandbyte & 0xfe) ) |
| 225 | | { |
| 226 | | case 0xe2: /* append record */ op = SC_AC_OP_UPDATE; break; |
| 227 | | case 0x24: /* change password */ op = SC_AC_OP_UPDATE; break; |
| 228 | | case 0xe0: /* create */ op = SC_AC_OP_CREATE; break; |
| 229 | | case 0xe4: /* delete */ op = SC_AC_OP_DELETE; break; |
| 230 | | case 0xe8: /* exclude sfi */ op = SC_AC_OP_WRITE; break; |
| 231 | | case 0x82: /* external auth */ op = SC_AC_OP_READ; break; |
| 232 | | case 0xe6: /* include sfi */ op = SC_AC_OP_WRITE; break; |
| 233 | | case 0x88: /* internal auth */ op = SC_AC_OP_READ; break; |
| 234 | | case 0x04: /* invalidate */ op = SC_AC_OP_INVALIDATE; break; |
| 235 | | case 0x2a: /* perform sec. op */ op = SC_AC_OP_SELECT; break; |
| 236 | | case 0xb0: /* read binary */ op = SC_AC_OP_READ; break; |
| 237 | | case 0xb2: /* read record */ op = SC_AC_OP_READ; break; |
| 238 | | case 0x44: /* rehabilitate */ op = SC_AC_OP_REHABILITATE; break; |
| 239 | | case 0xa4: /* select */ op = SC_AC_OP_SELECT; break; |
| 240 | | case 0xee: /* set permanent */ op = SC_AC_OP_CREATE; break; |
| 241 | | case 0x2c: /* unblock password */op = SC_AC_OP_WRITE; break; |
| 242 | | case 0xd6: /* update binary */ op = SC_AC_OP_WRITE; break; |
| 243 | | case 0xdc: /* update record */ op = SC_AC_OP_WRITE; break; |
| 244 | | case 0x20: /* verify password */ op = SC_AC_OP_SELECT; break; |
| 245 | | case 0x60: /* admin group */ op = SC_AC_OP_CREATE; break; |
| 246 | | } |
| 247 | | return op; |
| 248 | | } |
| 249 | | |
| 250 | | |
| | 234 | unsigned int op = (unsigned int)-1; |
| | 235 | |
| | 236 | switch ( (commandbyte & 0xfe) ) { |
| | 237 | case 0xe2: /* append record */ op = SC_AC_OP_UPDATE; break; |
| | 238 | case 0x24: /* change password */ op = SC_AC_OP_UPDATE; break; |
| | 239 | case 0xe0: /* create */ op = SC_AC_OP_CREATE; break; |
| | 240 | case 0xe4: /* delete */ op = SC_AC_OP_DELETE; break; |
| | 241 | case 0xe8: /* exclude sfi */ op = SC_AC_OP_WRITE; break; |
| | 242 | case 0x82: /* external auth */ op = SC_AC_OP_READ; break; |
| | 243 | case 0xe6: /* include sfi */ op = SC_AC_OP_WRITE; break; |
| | 244 | case 0x88: /* internal auth */ op = SC_AC_OP_READ; break; |
| | 245 | case 0x04: /* invalidate */ op = SC_AC_OP_INVALIDATE; break; |
| | 246 | case 0x2a: /* perform sec. op */ op = SC_AC_OP_SELECT; break; |
| | 247 | case 0xb0: /* read binary */ op = SC_AC_OP_READ; break; |
| | 248 | case 0xb2: /* read record */ op = SC_AC_OP_READ; break; |
| | 249 | case 0x44: /* rehabilitate */ op = SC_AC_OP_REHABILITATE; break; |
| | 250 | case 0xa4: /* select */ op = SC_AC_OP_SELECT; break; |
| | 251 | case 0xee: /* set permanent */ op = SC_AC_OP_CREATE; break; |
| | 252 | case 0x2c: /* unblock password */op = SC_AC_OP_WRITE; break; |
| | 253 | case 0xd6: /* update binary */ op = SC_AC_OP_WRITE; break; |
| | 254 | case 0xdc: /* update record */ op = SC_AC_OP_WRITE; break; |
| | 255 | case 0x20: /* verify password */ op = SC_AC_OP_SELECT; break; |
| | 256 | case 0x60: /* admin group */ op = SC_AC_OP_CREATE; break; |
| | 257 | } |
| | 258 | return op; |
| | 259 | } |
| 322 | | /* Arghh. duplicated from iso7816.c */ |
| 323 | | static void tcos_process_fci(sc_context_t *ctx, sc_file_t *file, |
| 324 | | const u8 *buf, size_t buflen) |
| 325 | | { |
| 326 | | size_t taglen, len = buflen; |
| 327 | | const u8 *tag = NULL, *p = buf; |
| 328 | | |
| 329 | | if (ctx->debug >= 3) |
| 330 | | sc_debug(ctx, "processing FCI bytes\n"); |
| 331 | | tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); |
| 332 | | if (tag != NULL && taglen == 2) { |
| 333 | | file->id = (tag[0] << 8) | tag[1]; |
| 334 | | if (ctx->debug >= 3) |
| 335 | | sc_debug(ctx, " file identifier: 0x%02X%02X\n", tag[0], |
| 336 | | tag[1]); |
| 337 | | } |
| 338 | | tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); |
| 339 | | if (tag != NULL && taglen >= 2) { |
| 340 | | int bytes = (tag[0] << 8) + tag[1]; |
| 341 | | if (ctx->debug >= 3) |
| 342 | | sc_debug(ctx, " bytes in file: %d\n", bytes); |
| 343 | | file->size = bytes; |
| 344 | | } |
| 345 | | if (tag == NULL) { |
| 346 | | tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); |
| 347 | | if (tag != NULL && taglen >= 2) { |
| 348 | | int bytes = (tag[0] << 8) + tag[1]; |
| 349 | | if (ctx->debug >= 3) |
| 350 | | sc_debug(ctx, " bytes in file: %d\n", bytes); |
| 351 | | file->size = bytes; |
| 352 | | } |
| 353 | | } |
| 354 | | tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); |
| 355 | | if (tag != NULL) { |
| 356 | | if (taglen > 0) { |
| 357 | | unsigned char byte = tag[0]; |
| 358 | | const char *type; |
| 359 | | |
| 360 | | file->shareable = byte & 0x40 ? 1 : 0; |
| 361 | | if (ctx->debug >= 3) |
| 362 | | sc_debug(ctx, " shareable: %s\n", |
| 363 | | (byte & 0x40) ? "yes" : "no"); |
| 364 | | file->ef_structure = byte & 0x07; |
| 365 | | switch ((byte >> 3) & 7) { |
| 366 | | case 0: |
| 367 | | type = "working EF"; |
| 368 | | file->type = SC_FILE_TYPE_WORKING_EF; |
| 369 | | break; |
| 370 | | case 1: |
| 371 | | type = "internal EF"; |
| 372 | | file->type = SC_FILE_TYPE_INTERNAL_EF; |
| 373 | | break; |
| 374 | | case 7: |
| 375 | | type = "DF"; |
| 376 | | file->type = SC_FILE_TYPE_DF; |
| 377 | | break; |
| 378 | | default: |
| 379 | | type = "unknown"; |
| 380 | | break; |
| 381 | | } |
| 382 | | if (ctx->debug >= 3) { |
| 383 | | sc_debug(ctx, " type: %s\n", type); |
| 384 | | sc_debug(ctx, " EF structure: %d\n", |
| 385 | | byte & 0x07); |
| 386 | | } |
| 387 | | } |
| 388 | | } |
| 389 | | tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); |
| 390 | | if (tag != NULL && taglen > 0 && taglen <= 16) { |
| 391 | | char name[17]; |
| 392 | | size_t i; |
| 393 | | |
| 394 | | memcpy(file->name, tag, taglen); |
| 395 | | file->namelen = taglen; |
| 396 | | |
| 397 | | for (i = 0; i < taglen; i++) { |
| 398 | | if (isalnum(tag[i]) || ispunct(tag[i]) |
| 399 | | || isspace(tag[i])) |
| 400 | | name[i] = tag[i]; |
| 401 | | else |
| 402 | | name[i] = '?'; |
| 403 | | } |
| 404 | | name[taglen] = 0; |
| 405 | | if (ctx->debug >= 3) |
| 406 | | sc_debug(ctx, "File name: %s\n", name); |
| 407 | | } |
| 408 | | tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); |
| 409 | | if (tag != NULL && taglen) { |
| 410 | | sc_file_set_prop_attr(file, tag, taglen); |
| 411 | | } else |
| 412 | | file->prop_attr_len = 0; |
| 413 | | tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); |
| 414 | | if (tag != NULL && taglen) { |
| 415 | | sc_file_set_prop_attr(file, tag, taglen); |
| 416 | | } |
| 417 | | tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); |
| 418 | | if (tag != NULL && taglen) { |
| 419 | | sc_file_set_sec_attr(file, tag, taglen); |
| 420 | | } |
| 421 | | file->magic = SC_FILE_MAGIC; |
| 422 | | } |
| 423 | | |
| 424 | | |
| 425 | | /* This is a special version of the standard select_file which is |
| 426 | | needed to cope with some starngeness in APDU construction. It is |
| 427 | | probably better to have this specfic for TCOS, so that support for |
| 428 | | other cards does not break. */ |
| 429 | | static int hacked_iso7816_select_file(sc_card_t *card, |
| 430 | | const sc_path_t *in_path, |
| 431 | | sc_file_t **file_out) |
| | 331 | |
| | 332 | static int tcos_select_file(sc_card_t *card, |
| | 333 | const sc_path_t *in_path, |
| | 334 | sc_file_t **file_out) |
| 498 | | if (r) |
| 499 | | SC_FUNC_RETURN(card->ctx, 2, r); |
| 500 | | |
| 501 | | switch (apdu.resp[0]) { |
| 502 | | case 0x6F: |
| 503 | | file = sc_file_new(); |
| 504 | | if (file == NULL) |
| 505 | | SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); |
| 506 | | file->path = *in_path; |
| 507 | | if (apdu.resp[1] <= apdu.resplen) |
| 508 | | tcos_process_fci(card->ctx, file, |
| 509 | | apdu.resp+2, apdu.resp[1]); |
| 510 | | *file_out = file; |
| 511 | | break; |
| 512 | | case 0x00: /* proprietary coding */ |
| 513 | | SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
| 514 | | default: |
| 515 | | SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); |
| 516 | | } |
| | 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 | |
| 596 | | /* Crypto operations */ |
| 597 | | |
| 598 | | |
| 599 | | /* TCOS has two kind of RSA-keys: signature-keys and encryption-keys |
| 600 | | signature-keys: can be used for sign-operations only and can be used |
| 601 | | only within the default security environment. hence must be |
| 602 | | stored as local key 0, a SetSecEnv-cmd must not be used (if you |
| 603 | | do - even with default parameters - it will fail with 6A88) |
| 604 | | encryption-keys: can be used for both sign- and decipher-operations, |
| 605 | | can be used within any security environment, a SetSecEnv-cmd |
| 606 | | must be used (even if you want to use the default security environment |
| 607 | | you must a SetSecEnv-cmd with default parameters) |
| 608 | | Unfortunately we cannot find out wether the referenced key is a |
| 609 | | signature-key or encryption-key when this routine is called. Therefore |
| 610 | | we have a problem if the key-reference is 0x80. If the referenced key |
| 611 | | was a signature-key a SetSecEnv must not be used, if the key was an |
| 612 | | encryption-key it must be used. |
| 613 | | Therefore we suppress error-messages in this case, try a SetSecEnv-cmd |
| 614 | | with default parameters and watch out for 6A88-responses [pk_opensc@web.de] |
| 615 | | */ |
| 616 | | static int tcos_set_security_env(sc_card_t *card, |
| 617 | | const sc_security_env_t *env, |
| 618 | | int se_num) |
| | 502 | |
| | 503 | static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) |
| 627 | | |
| 628 | | if (se_num) SC_FUNC_RETURN(ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
| 629 | | |
| 630 | | if(ctx->debug >= 3) sc_debug(ctx, "Security Environment Ref=%d:%02X\n", env->key_ref_len, *env->key_ref); |
| 631 | | if(env->operation == SC_SEC_OPERATION_SIGN && |
| 632 | | (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len==1 && *env->key_ref==0x80)) |
| 633 | | ){ |
| 634 | | if (ctx->debug >= 3) sc_debug(ctx, "Sign-Operation with Default Security Environment\n"); |
| 635 | | sign_with_def_env=1; |
| 636 | | } |
| 637 | | |
| 638 | | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); |
| 639 | | switch (env->operation) { |
| 640 | | case SC_SEC_OPERATION_DECIPHER: |
| 641 | | case SC_SEC_OPERATION_SIGN: |
| 642 | | apdu.p1 = 0xC1; |
| 643 | | apdu.p2 = 0xB8; |
| 644 | | /* save padding flags and default secEnv indictor */ |
| 645 | | ((tcos_data *)card->drv_data)->pad_flags = env->algorithm_flags; |
| 646 | | ((tcos_data *)card->drv_data)->sign_with_def_env = sign_with_def_env; |
| 647 | | break; |
| 648 | | default: |
| 649 | | return SC_ERROR_INVALID_ARGUMENTS; |
| 650 | | } |
| 651 | | |
| 652 | | apdu.le = 0; |
| | 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 | // Key-Reference 0x80 ?? |
| | 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); |