diff options
author | Robert Relyea <rrelyea@redhat.com> | 2010-07-28 20:09:28 +0300 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2010-07-28 20:09:28 +0300 |
commit | f8d3142a14d79ed183ba970419f188d35f62e9cc (patch) | |
tree | 0a5a8f960498c5d8e28dba91293c149564445c6f /cac.c |
initial
Diffstat (limited to 'cac.c')
-rw-r--r-- | cac.c | 354 |
1 files changed, 354 insertions, 0 deletions
@@ -0,0 +1,354 @@ +/* + * impement the applets for the CAC card. + */ +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" +#include <stdlib.h> +#include <string.h> + +#define CAC_GET_PROPERTIES 0x56 +#define CAC_GET_ACR 0x4c +#define CAC_READ_BUFFER 0x52 +#define CAC_UPDATE_BUFFER 0x58 +#define CAC_SIGN_DECRYPT 0x42 +#define CAC_GET_CERTIFICATE 0x36 + +/* private data for PKI applets */ +typedef struct CACPKIAppletDataStruct { + unsigned char *cert; + int cert_len; + unsigned char *cert_buffer; + int cert_buffer_len; + unsigned char *sign_buffer; + int sign_buffer_len; + VCardKey *key; +} CACPKIAppletData; + +/* + * CAC applet private data + */ +struct VCardAppletPrivateStruct { + CACPKIAppletData pki_data; +}; + +/* + * handle all the APDU's that are common to all CAC applets + */ +static VCardStatus +cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + int ef; + + switch (apdu->a_ins) { + case VCARD7816_INS_SELECT_FILE: + if (apdu->a_p1 != 0x02) { + /* let the 7816 code handle applet switches */ + return VCARD_NEXT; + } + /* handle file id setting */ + if (apdu->a_Lc != 2) { + *response = vcard_make_response(VCARD7816_STATUS_ERROR_DATA_INVALID); + return VCARD_DONE; + } + /* CAC 1.0 only supports ef = 0 */ + ef = apdu->a_body[0] | (apdu->a_body[1] << 8); + if (ef != 0 ) { + *response = vcard_make_response(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + return VCARD_DONE; + } + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case VCARD7816_INS_GET_RESPONSE: + case VCARD7816_INS_VERIFY: + /* let the 7816 code handle these */ + return VCARD_NEXT; + case CAC_GET_PROPERTIES: + case CAC_GET_ACR: + /* skip these for now, this will probably be needed */ + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + return VCARD_DONE; + } + *response = vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} + + +static VCardStatus +cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + CACPKIAppletData *pki_applet = NULL; + VCardAppletPrivate *applet_private = NULL; + VCardBufferResponse *buffer_response; + int size, next; + unsigned char *sign_buffer; + VCard7816Status status; + + applet_private = vcard_get_current_applet_private(card, apdu->a_channel); + ASSERT(applet_private); + pki_applet = &(applet_private->pki_data); + + switch (apdu->a_ins) { + case CAC_UPDATE_BUFFER: + *response = vcard_make_response(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + return VCARD_DONE; + case CAC_GET_CERTIFICATE: + if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + ASSERT(pki_applet->cert != NULL); + size = apdu->a_Le; + if (pki_applet->cert_buffer == NULL) { + pki_applet->cert_buffer=pki_applet->cert; + pki_applet->cert_buffer_len=pki_applet->cert_len; + } + size = MIN(size, pki_applet->cert_buffer_len); + next = MIN(255, pki_applet->cert_buffer_len - size); + *response = vcard_response_new_bytes(pki_applet->cert_buffer, size, next ? + VCARD7816_SW1_WARNING_CHANGE : + VCARD7816_SW1_SUCCESS, + next); + pki_applet->cert_buffer += size; + pki_applet->cert_buffer_len -= size; + if ((*response == NULL) || (next == 0)) { + pki_applet->cert_buffer=NULL; + } + if (*response == NULL) { + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + return VCARD_DONE; + case CAC_SIGN_DECRYPT: + if (apdu->a_p2 != 0) { + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + size = apdu->a_Lc; + + sign_buffer = realloc(pki_applet->sign_buffer, + pki_applet->sign_buffer_len+size); + if (sign_buffer == NULL) { + free(pki_applet->sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + return VCARD_DONE; + } + memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); + size += pki_applet->sign_buffer_len; + switch (apdu->a_p1) { + case 0x80: + /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for the rest */ + pki_applet->sign_buffer = sign_buffer; + pki_applet->sign_buffer_len = size; + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case 0x00: + /* we now have the whole buffer, do the operation, result will be in the sign_buffer */ + status = vcard_emul_rsa_op(card, pki_applet->key, sign_buffer, size); + if (status != VCARD7816_STATUS_SUCCESS) { + *response = vcard_make_response(status); + break; + } + /* if the size is too large for a single return, use the deferred + * buffer response */ + if (size >= 256) { + 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(sign_buffer, size); + if (buffer_response == NULL) { + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + break; + } + *response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES, + size & 0xff); + if (*response == NULL) { + vcard_buffer_response_delete(buffer_response); + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } else { + vcard_set_buffer_response(card,buffer_response); + } + break; + } + *response = vcard_response_new(sign_buffer, size, VCARD7816_STATUS_SUCCESS); + if (*response == NULL) { + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + break; + default: + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + free(sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + return VCARD_DONE; + case CAC_READ_BUFFER: + /* new CAC call, go ahead and use the old version for now */ + /* TODO: implement */ + *response = vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + return cac_common_process_apdu(card, apdu, response); +} + + +/* + * TODO: if we ever want to support general CAC middleware, we will need to implement + * the various containers. + */ +static VCardStatus +cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + switch (apdu->a_ins) { + case CAC_READ_BUFFER: + case CAC_UPDATE_BUFFER: + *response = vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + default: + break; + } + return cac_common_process_apdu(card, apdu, response); + +} + +/* forward delcaration so the constructor can come first */ +static void cac_delete_pki_applet_data(void *applet_private); + +/* + * utilities for creating and destroying the private applet data + */ +static void +cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) +{ + CACPKIAppletData *pki_applet_data = NULL; + if (pki_applet_data == NULL) { + return; + } + pki_applet_data = &(applet_private->pki_data); + if (pki_applet_data->cert != NULL) { + free(pki_applet_data->cert); + } + if (pki_applet_data->sign_buffer != NULL) { + free(pki_applet_data->sign_buffer); + } + if (pki_applet_data->key != NULL) { + vcard_emul_delete_key(pki_applet_data->key); + } + free(applet_private); +} + +static VCardAppletPrivate * +cac_new_pki_applet_private(const unsigned char *cert, int cert_len, VCardKey *key) +{ + CACPKIAppletData *pki_applet_data = NULL; + VCardAppletPrivate *applet_private = NULL; + applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate)); + + if (applet_private == NULL) { + goto fail; + } + pki_applet_data= &(applet_private->pki_data); + pki_applet_data->cert_buffer = NULL; + pki_applet_data->cert_buffer_len = 0; + pki_applet_data->sign_buffer = NULL; + pki_applet_data->sign_buffer_len = 0; + pki_applet_data->key = NULL; + pki_applet_data->cert = (unsigned char *)malloc(cert_len+1); + if (pki_applet_data->cert == NULL) { + goto fail; + } + /* + * if we want to support compression, then we simply change the 0 to a 1 and compress the + * cert data with libz + */ + pki_applet_data->cert[0] = 0; /* not compressed */ + memcpy(&pki_applet_data->cert[1], cert, cert_len); + pki_applet_data->cert_len = cert_len+1; + + pki_applet_data->key = key; + return applet_private; + +fail: + if (applet_private) { + cac_delete_pki_applet_private(applet_private); + } +} + + +/* + * create a new cac applet which links to a given cert + */ +static VCardApplet * +cac_new_pki_applet(int i, const unsigned char *cert, int cert_len, VCardKey *key) +{ + VCardAppletPrivate *applet_private = NULL; + VCardApplet *applet = NULL; + unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; + int pki_aid_len = sizeof (pki_aid); + + pki_aid[pki_aid_len-1] = i; + + applet_private = cac_new_pki_applet_private(cert, cert_len, key); + if (applet_private == NULL) { + goto failure; + } + applet = vcard_new_applet(cac_applet_pki_process_apdu, pki_aid, pki_aid_len); + if (applet == NULL) { + goto failure; + } + vcard_set_applet_private(applet, applet_private, cac_delete_pki_applet_private); + applet_private = NULL; + + return applet; + +failure: + if (applet_private != NULL) { + cac_delete_pki_applet_private(applet_private); + } + return NULL; +} + + +static unsigned char cac_default_container_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; +/* + * Initialize the cac card. This is the only public function in this file. All the rest + * are connected through function pointers. + */ +VCardStatus +cac_card_init(VCard *card, const unsigned char *cert[], int cert_len[], + VCardKey *key[] /* adopt the keys*/, int cert_count) +{ + int i; + VCardApplet *applet; + + /* CAC Cards are VM Cards */ + vcard_set_type(card,VCARD_VM); + + /* create one PKI applet for each cert */ + for (i=0; i < cert_count; i++) { + applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + } + + /* create a default blank container applet */ + applet = vcard_new_applet(cac_applet_container_process_apdu, cac_default_container_aid, + sizeof(cac_default_container_aid)); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + return VCARD_DONE; + +failure: + return VCARD_FAIL; +} + + + |