summaryrefslogblamecommitdiffstats
path: root/libcacard/card_7816.c
blob: c28bb60fe6982dd6b4a3d154d806fd330ea22b6f (plain) (tree)
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756




















































                                                                                

                                                                    







































































                                                                            
                                                    





                                                     
                                                                    















                                                                     
                                                                    






















                                                                       
                                     
         
                         



                                                
                                     



                                              
                         













































                                                                    
                                                                            





                                    
                                                       































































































                                                                              

                                                        



                                              
                         



                                              
                         











                                  
                             
     
                 






































































































































































































































































































































































































                                                                                
                                                





                                                                          
/*
 * Implement the 7816 portion of the card spec
 *
 * This code is licensed under the GNU LGPL, version 2.1 or later.
 * See the COPYING.LIB file in the top-level directory.
 */

#include "qemu-common.h"

#include "vcard.h"
#include "vcard_emul.h"
#include "card_7816.h"

/*
 * set the status bytes based on the status word
 */
static void
vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
{
    unsigned char sw1, sw2;
    response->b_status = status; /* make sure the status and swX representations
                                  * are consistent */
    sw1 = (status >> 8) & 0xff;
    sw2 = status & 0xff;
    response->b_sw1 = sw1;
    response->b_sw2 = sw2;
    response->b_data[response->b_len] = sw1;
    response->b_data[response->b_len+1] = sw2;
}

/*
 * set the status bytes in a response buffer
 */
static void
vcard_response_set_status_bytes(VCardResponse *response,
                               unsigned char sw1, unsigned char sw2)
{
    response->b_status = sw1 << 8 | sw2;
    response->b_sw1 = sw1;
    response->b_sw2 = sw2;
    response->b_data[response->b_len] = sw1;
    response->b_data[response->b_len+1] = sw2;
}

/*
 * allocate a VCardResponse structure, plus space for the data buffer, and
 * set up everything but the resonse bytes.
 */
VCardResponse *
vcard_response_new_data(unsigned char *buf, int len)
{
    VCardResponse *new_response;

    new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
    new_response->b_data = g_malloc(len + 2);
    memcpy(new_response->b_data, buf, len);
    new_response->b_total_len = len+2;
    new_response->b_len = len;
    new_response->b_type = VCARD_MALLOC;
    return new_response;
}

static VCardResponse *
vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
{
    VCardResponse *response;
    VCardBufferResponse *buffer_response;

    buffer_response = vcard_get_buffer_response(card);
    if (buffer_response) {
        vcard_set_buffer_response(card, NULL);
        vcard_buffer_response_delete(buffer_response);
    }
    buffer_response = vcard_buffer_response_new(buf, len);
    if (buffer_response == NULL) {
        return NULL;
    }
    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
                                               len > 255 ? 0 : len);
    if (response == NULL) {
        return NULL;
    }
    vcard_set_buffer_response(card, buffer_response);
    return response;
}

/*
 * general buffer to hold results from APDU calls
 */
VCardResponse *
vcard_response_new(VCard *card, unsigned char *buf,
                   int len, int Le, vcard_7816_status_t status)
{
    VCardResponse *new_response;

    if (len > Le) {
        return vcard_init_buffer_response(card, buf, len);
    }
    new_response = vcard_response_new_data(buf, len);
    if (new_response == NULL) {
        return NULL;
    }
    vcard_response_set_status(new_response, status);
    return new_response;
}

/*
 * general buffer to hold results from APDU calls
 */
VCardResponse *
vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
                         unsigned char sw1, unsigned char sw2)
{
    VCardResponse *new_response;

    if (len > Le) {
        return vcard_init_buffer_response(card, buf, len);
    }
    new_response = vcard_response_new_data(buf, len);
    if (new_response == NULL) {
        return NULL;
    }
    vcard_response_set_status_bytes(new_response, sw1, sw2);
    return new_response;
}

/*
 * get a new Response buffer that only has a status.
 */
static VCardResponse *
vcard_response_new_status(vcard_7816_status_t status)
{
    VCardResponse *new_response;

    new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
    new_response->b_data = &new_response->b_sw1;
    new_response->b_len = 0;
    new_response->b_total_len = 2;
    new_response->b_type = VCARD_MALLOC_STRUCT;
    vcard_response_set_status(new_response, status);
    return new_response;
}

/*
 * same as above, but specify the status as separate bytes
 */
VCardResponse *
vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
{
    VCardResponse *new_response;

    new_response = (VCardResponse *)g_malloc(sizeof(VCardResponse));
    new_response->b_data = &new_response->b_sw1;
    new_response->b_len = 0;
    new_response->b_total_len = 2;
    new_response->b_type = VCARD_MALLOC_STRUCT;
    vcard_response_set_status_bytes(new_response, sw1, sw2);
    return new_response;
}


/*
 * free the response buffer. The Buffer has a type to handle the buffer
 * allocated in other ways than through malloc.
 */
void
vcard_response_delete(VCardResponse *response)
{
    if (response == NULL) {
        return;
    }
    switch (response->b_type) {
    case VCARD_MALLOC:
        /* everything was malloc'ed */
        if (response->b_data) {
            g_free(response->b_data);
        }
        g_free(response);
        break;
    case VCARD_MALLOC_DATA:
        /* only the data buffer was malloc'ed */
        if (response->b_data) {
            g_free(response->b_data);
        }
        break;
    case VCARD_MALLOC_STRUCT:
        /* only the structure was malloc'ed */
        g_free(response);
        break;
    case VCARD_STATIC:
        break;
    }
}

/*
 * decode the class bit and set our generic type field, channel, and
 * secure messaging values.
 */
static vcard_7816_status_t
vcard_apdu_set_class(VCardAPDU *apdu) {
    apdu->a_channel = 0;
    apdu->a_secure_messaging = 0;
    apdu->a_type = apdu->a_cla & 0xf0;
    apdu->a_gen_type = VCARD_7816_ISO;

    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
    switch (apdu->a_type) {
        /* we only support the basic types */
    case 0x00:
    case 0x80:
    case 0x90:
    case 0xa0:
        apdu->a_channel = apdu->a_cla & 3;
        apdu->a_secure_messaging = apdu->a_cla & 0xe;
        break;
    case 0xb0:
    case 0xc0:
        break;

    case 0x10:
    case 0x20:
    case 0x30:
    case 0x40:
    case 0x50:
    case 0x60:
    case 0x70:
        /* Reserved for future use */
        apdu->a_gen_type = VCARD_7816_RFU;
        break;
    case 0xd0:
    case 0xe0:
    case 0xf0:
    default:
        apdu->a_gen_type =
            (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
        break;
    }
    return VCARD7816_STATUS_SUCCESS;
}

/*
 * set the Le and Lc fields according to table 5 of the
 * 7816-4 part 4 spec
 */
static vcard_7816_status_t
vcard_apdu_set_length(VCardAPDU *apdu)
{
    int L, Le;

    /* process according to table 5 of the 7816-4 Part 4 spec.
     * variable names match the variables in the spec */
    L = apdu->a_len-4; /* fixed APDU header */
    apdu->a_Lc = 0;
    apdu->a_Le = 0;
    apdu->a_body = NULL;
    switch (L) {
    case 0:
        /* 1 minimal apdu */
        return VCARD7816_STATUS_SUCCESS;
    case 1:
        /* 2S only return values apdu */
        /*   zero maps to 256 here */
        apdu->a_Le = apdu->a_header->ah_Le ?
                         apdu->a_header->ah_Le : 256;
        return VCARD7816_STATUS_SUCCESS;
    default:
        /* if the ah_Le byte is zero and we have more than
         * 1 byte in the header, then we must be using extended Le and Lc.
         * process the extended now. */
        if (apdu->a_header->ah_Le == 0) {
            if (L < 3) {
                /* coding error, need at least 3 bytes */
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
            }
            /* calculate the first extended value. Could be either Le or Lc */
            Le = (apdu->a_header->ah_body[0] << 8)
               || apdu->a_header->ah_body[1];
            if (L == 3) {
                /* 2E extended, return data only */
                /*   zero maps to 65536 */
                apdu->a_Le = Le ? Le : 65536;
                return VCARD7816_STATUS_SUCCESS;
            }
            if (Le == 0) {
                /* reserved for future use, probably for next time we need
                 * to extend the lengths */
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
            }
            /* we know that the first extended value is Lc now */
            apdu->a_Lc = Le;
            apdu->a_body = &apdu->a_header->ah_body[2];
            if (L == Le+3) {
                /* 3E extended, only body parameters */
                return VCARD7816_STATUS_SUCCESS;
            }
            if (L == Le+5) {
                /* 4E extended, parameters and return data */
                Le = (apdu->a_data[apdu->a_len-2] << 8)
                   || apdu->a_data[apdu->a_len-1];
                apdu->a_Le = Le ? Le : 65536;
                return VCARD7816_STATUS_SUCCESS;
            }
            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
        }
        /* not extended */
        apdu->a_Lc = apdu->a_header->ah_Le;
        apdu->a_body = &apdu->a_header->ah_body[0];
        if (L ==  apdu->a_Lc + 1) {
            /* 3S only body parameters */
            return VCARD7816_STATUS_SUCCESS;
        }
        if (L ==  apdu->a_Lc + 2) {
            /* 4S parameters and return data */
            Le = apdu->a_data[apdu->a_len-1];
            apdu->a_Le = Le ?  Le : 256;
            return VCARD7816_STATUS_SUCCESS;
        }
        break;
    }
    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}

/*
 * create a new APDU from a raw set of bytes. This will decode all the
 * above fields. users of VCARDAPDU's can then depend on the already decoded
 * values.
 */
VCardAPDU *
vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
{
    VCardAPDU *new_apdu;

    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
    if (len < 4) {
        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
        return NULL;
    }

    new_apdu = (VCardAPDU *)g_malloc(sizeof(VCardAPDU));
    new_apdu->a_data = g_malloc(len);
    memcpy(new_apdu->a_data, raw_apdu, len);
    new_apdu->a_len = len;
    *status = vcard_apdu_set_class(new_apdu);
    if (*status != VCARD7816_STATUS_SUCCESS) {
        g_free(new_apdu);
        return NULL;
    }
    *status = vcard_apdu_set_length(new_apdu);
    if (*status != VCARD7816_STATUS_SUCCESS) {
        g_free(new_apdu);
        new_apdu = NULL;
    }
    return new_apdu;
}

void
vcard_apdu_delete(VCardAPDU *apdu)
{
    if (apdu == NULL) {
        return;
    }
    if (apdu->a_data) {
        g_free(apdu->a_data);
    }
    g_free(apdu);
}


/*
 * declare response buffers for all the 7816 defined error codes
 */
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(
                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
VCARD_RESPONSE_NEW_STATIC_STATUS(
                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)

/*
 * return a single response code. This function cannot fail. It will always
 * return a response.
 */
VCardResponse *
vcard_make_response(vcard_7816_status_t status)
{
    VCardResponse *response = NULL;

    switch (status) {
    /* known 7816 response codes */
    case VCARD7816_STATUS_SUCCESS:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_SUCCESS);
    case VCARD7816_STATUS_WARNING:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING);
    case VCARD7816_STATUS_WARNING_RET_CORUPT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_RET_CORUPT);
    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
    case VCARD7816_STATUS_WARNING_CHANGE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_CHANGE);
    case VCARD7816_STATUS_WARNING_FILE_FILLED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_WARNING_FILE_FILLED);
    case VCARD7816_STATUS_EXC_ERROR:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR);
    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
    case VCARD7816_STATUS_ERROR_DATA_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_INVALID);
    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
    case VCARD7816_STATUS_ERROR_CLA_INVALID:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_CLA_INVALID);
    case VCARD7816_STATUS_ERROR_GENERAL:
        return VCARD_RESPONSE_GET_STATIC(
                    VCARD7816_STATUS_ERROR_GENERAL);
    default:
        /* we don't know this status code, create a response buffer to
         * hold it */
        response = vcard_response_new_status(status);
        if (response == NULL) {
            /* couldn't allocate the buffer, return memmory error */
            return VCARD_RESPONSE_GET_STATIC(
                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
        }
    }
    assert(response);
    return response;
}

/*
 * Add File card support here if you need it.
 */
static VCardStatus
vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
                                   VCardResponse **response)
{
    /* TODO: if we want to support a virtual file system card, we do it here.
     * It would probably be a pkcs #15 card type */
    *response = vcard_make_response(
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    return VCARD_DONE;
}

/*
 * VM card (including java cards)
 */
static VCardStatus
vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
                          VCardResponse **response)
{
    int bytes_to_copy, next_byte_count, count;
    VCardApplet *current_applet;
    VCardBufferResponse *buffer_response;
    vcard_7816_status_t status;

    /* parse the class first */
    if (apdu->a_gen_type !=  VCARD_7816_ISO) {
        *response = vcard_make_response(
                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
        return VCARD_DONE;
    }

    /* use a switch so that if we need to support secure channel stuff later,
     * we know where to put it */
    switch (apdu->a_secure_messaging) {
    case 0x0: /* no SM */
        break;
    case 0x4: /* proprietary SM */
    case 0x8: /* header not authenticated */
    case 0xc: /* header authenticated */
    default:
        /* for now, don't try to support secure channel stuff in the
         * virtual card. */
        *response = vcard_make_response(
                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
        return VCARD_DONE;
    }

    /* now parse the instruction */
    switch (apdu->a_ins) {
    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */
    case  VCARD7816_INS_READ_BINARY: /* applet control op */
    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */
    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */
    case  VCARD7816_INS_READ_RECORD: /* file op */
    case  VCARD7816_INS_WRITE_RECORD: /* file op */
    case  VCARD7816_INS_UPDATE_RECORD: /* file op */
    case  VCARD7816_INS_APPEND_RECORD: /* file op */
    case  VCARD7816_INS_ENVELOPE:
    case  VCARD7816_INS_PUT_DATA:
        *response = vcard_make_response(
                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
        break;

    case  VCARD7816_INS_SELECT_FILE:
        if (apdu->a_p1 != 0x04) {
            *response = vcard_make_response(
                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
            break;
        }

        /* side effect, deselect the current applet if no applet has been found
         * */
        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
        vcard_select_applet(card, apdu->a_channel, current_applet);
        if (current_applet) {
            unsigned char *aid;
            int aid_len;
            aid = vcard_applet_get_aid(current_applet, &aid_len);
            *response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
                                          VCARD7816_STATUS_SUCCESS);
        } else {
            *response = vcard_make_response(
                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
        }
        break;

    case  VCARD7816_INS_VERIFY:
        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
            *response = vcard_make_response(
                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
        } else {
            if (apdu->a_Lc == 0) {
                /* handle pin count if possible */
                count = vcard_emul_get_login_count(card);
                if (count < 0) {
                    *response = vcard_make_response(
                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
                } else {
                    if (count > 0xf) {
                        count = 0xf;
                    }
                    *response = vcard_response_new_status_bytes(
                                                VCARD7816_SW1_WARNING_CHANGE,
                                                                0xc0 | count);
                    if (*response == NULL) {
                        *response = vcard_make_response(
                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
                    }
                }
            } else {
                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
                *response = vcard_make_response(status);
            }
        }
        break;

    case VCARD7816_INS_GET_RESPONSE:
        buffer_response = vcard_get_buffer_response(card);
        if (!buffer_response) {
            *response = vcard_make_response(
                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
            /* handle error */
            break;
        }
        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
        *response = vcard_response_new_bytes(
                        card, buffer_response->current, bytes_to_copy,
                        apdu->a_Le,
                        next_byte_count ?
                        VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
                        next_byte_count);
        buffer_response->current += bytes_to_copy;
        buffer_response->len -= bytes_to_copy;
        if (*response == NULL || (next_byte_count == 0)) {
            vcard_set_buffer_response(card, NULL);
            vcard_buffer_response_delete(buffer_response);
        }
        if (*response == NULL) {
            *response =
                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
        }
        break;

    case VCARD7816_INS_GET_DATA:
        *response =
            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
        break;

    default:
        *response =
            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
        break;
    }

    /* response should have been set somewhere */
    assert(*response != NULL);
    return VCARD_DONE;
}


/*
 * APDU processing starts here. This routes the card processing stuff to the
 * right location.
 */
VCardStatus
vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
{
    VCardStatus status;
    VCardBufferResponse *buffer_response;

    /* first handle any PTS commands, which aren't really APDU's */
    if (apdu->a_type == VCARD_7816_PTS) {
        /* the PTS responses aren't really responses either */
        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
        /* PTS responses have no status bytes */
        (*response)->b_total_len = (*response)->b_len;
        return VCARD_DONE;
    }
    buffer_response = vcard_get_buffer_response(card);
    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
        /* clear out buffer_response, return an error */
        vcard_set_buffer_response(card, NULL);
        vcard_buffer_response_delete(buffer_response);
        *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
        return VCARD_DONE;
    }

    status = vcard_process_applet_apdu(card, apdu, response);
    if (status != VCARD_NEXT) {
        return status;
    }
    switch (vcard_get_type(card)) {
    case VCARD_FILE_SYSTEM:
        return vcard7816_file_system_process_apdu(card, apdu, response);
    case VCARD_VM:
        return vcard7816_vm_process_apdu(card, apdu, response);
    case VCARD_DIRECT:
        /* if we are type direct, then the applet should handle everything */
        assert(!"VCARD_DIRECT: applet failure");
        break;
    }
    *response =
        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
    return VCARD_DONE;
}