diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 71e1dfc2..9a161f55 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -36,7 +36,8 @@ libopensc_la_SOURCES = \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - compression.c p15card-helper.c pkcs15-rutoken.c + pkcs15-rutoken.c pkcs15-prkey-rutoken.c \ + compression.c p15card-helper.c libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ libopensc_la_LIBADD = @LIBSCCONF@ $(OPENSSL_LIBS) $(OPENCT_LIBS) $(PCSC_LIBS) $(LTLIB_LIBS) diff --git a/src/libopensc/card-rutoken.c b/src/libopensc/card-rutoken.c index ee88442b..b0eda20e 100644 --- a/src/libopensc/card-rutoken.c +++ b/src/libopensc/card-rutoken.c @@ -20,6 +20,7 @@ */ #include "internal.h" #include "cardctl.h" +#include #include #include #include @@ -36,38 +37,36 @@ #include #endif - - #define FDESCR_DF 0x38 /*00111000b*/ #define FDESCR_EF 0x01 #define ID_RESERVED_CURDF 0x3FFF /*Reserved ID for current DF*/ -int get_prkey_from_bin(u8* data, int len, struct sc_pkcs15_prkey **key); +#ifdef HAVE_OPENSSL +int get_prkey_from_bin(const u8 *data, size_t len, struct sc_pkcs15_prkey **key); +#endif #ifdef BIG_ENDIAN_RUTOKEN #define MF_PATH "\x3F\x00" #else #define MF_PATH "\x00\x3F" #endif + struct auth_senv { unsigned int algorithm; - int key_file_id; - size_t key_size; - unsigned int algorithm_flags; - sc_path_t path; }; typedef struct auth_senv auth_senv_t; static const sc_SecAttrV2_t default_sec_attr = { - 0x40, - 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 2 + 0x42, + 0, 1, 0, 0, 0, 0, 1, + 0, 2, 0, 0, 0, 0, 2 }; static const struct sc_card_operations *iso_ops = NULL; -struct sc_card_operations rutoken_ops; +static struct sc_card_operations rutoken_ops; + static struct sc_card_driver rutoken_drv = { "ruToken driver", "rutoken", @@ -80,9 +79,6 @@ static struct sc_atr_table rutoken_atrs[] = { { NULL, NULL, NULL, 0, 0, NULL } }; -static int make_le_path(u8 *hPath, size_t len); -static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo); - const char *hexdump(const void *data, size_t len) { static char string[1024]; @@ -123,10 +119,9 @@ static int rutoken_match_card(sc_card_t *card) static int rutoken_init(sc_card_t *card) { int ret = SC_ERROR_MEMORY_FAILURE; -#ifdef DEBUG - /* if(!card->ctx->debug) card->ctx->debug = 1; */ -#endif + SC_FUNC_CALLED(card->ctx, 1); + card->name = "rutoken card"; card->drv_data = malloc(sizeof(auth_senv_t)); card->caps |= SC_CARD_CAP_RSA_2048 | SC_CARD_CAP_NO_FCI | SC_CARD_CAP_RNG; @@ -137,7 +132,10 @@ static int rutoken_init(sc_card_t *card) } /* add algorithm TODO: may nid som other flag */ - unsigned int flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; /* SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_MD5_SHA1 | SC_ALGORITHM_RSA_PAD_NONE*/ + unsigned int flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; + /* SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 + | SC_ALGORITHM_RSA_HASH_MD5_SHA1 + | SC_ALGORITHM_RSA_PAD_NONE */ _sc_card_add_rsa_alg(card, 256, flags, 0); _sc_card_add_rsa_alg(card, 512, flags, 0); @@ -145,7 +143,8 @@ static int rutoken_init(sc_card_t *card) _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); sc_algorithm_info_t info; - flags = SC_ALGORITHM_GOST_CRYPT_PZ | SC_ALGORITHM_GOST_CRYPT_GAMM | SC_ALGORITHM_GOST_CRYPT_GAMMOS; + flags = SC_ALGORITHM_GOST_CRYPT_PZ | SC_ALGORITHM_GOST_CRYPT_GAMM + | SC_ALGORITHM_GOST_CRYPT_GAMMOS; memset(&info, 0, sizeof(info)); info.algorithm = SC_ALGORITHM_GOST; info.flags = flags; @@ -220,7 +219,8 @@ int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) for (i = 0; i < err_count; i++) { if (rutoken_errors[i].SWs == ((sw1 << 8) | sw2)) { - if ( rutoken_errors[i].errorstr ) sc_debug(card->ctx, rutoken_errors[i].errorstr); + if ( rutoken_errors[i].errorstr ) + sc_debug(card->ctx, rutoken_errors[i].errorstr); /*SC_FUNC_RETURN(card->ctx, 1, rutoken_errors[i].errorno);*/ return rutoken_errors[i].errorno; } @@ -230,7 +230,7 @@ int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_CARD_CMD_FAILED); } -int rutoken_dir_up(sc_card_t *card) +static int rutoken_dir_up(sc_card_t *card) { u8 rbuf[256]; int r = 0; @@ -251,6 +251,27 @@ int rutoken_dir_up(sc_card_t *card) return 0; } +/* make little endian path from normal path. + return 1 if right len, otherwise 0 */ +static int make_le_path(u8 *hPath, size_t len) +{ +#ifdef BIG_ENDIAN_RUTOKEN + /* we don't need it any more */ + return 1; +#else + int i, ret = (len > 1) && !(len & 1); /* && (len <= SC_MAX_PATH_SIZE); */ + if (ret) + { + for(i = 0; i < len; i += 2) + { + u8 b = hPath[i]; + hPath[i] = hPath[i+1]; + hPath[i+1] = b; + } + } + return ret; +#endif +} static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) { @@ -287,7 +308,8 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) rutoken_dir_up(card); /* 00 a4 00 02 02 prev id - next */ - while(1){ + while(1) + { sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x06); apdu.cla = 0x00; apdu.lc = 2; @@ -317,29 +339,7 @@ static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) return len; } -/* make little endian path from normal path. - return 1 if right len, otherwise 0 */ -static int make_le_path(u8 *hPath, size_t len) -{ -#ifdef BIG_ENDIAN_RUTOKEN - /* we don't need it any more */ - return 1; -#else - int i, ret = (len > 1) && !(len & 1); /* && (len <= SC_MAX_PATH_SIZE); */ - if (ret) - { - for(i = 0; i < len; i += 2) - { - u8 b = hPath[i]; - hPath[i] = hPath[i+1]; - hPath[i+1] = b; - } - } - return ret; -#endif -} - -void rutoken_process_fcp(sc_card_t *card, u8 *pIn, sc_file_t *file) +static void rutoken_process_fcp(sc_card_t *card, u8 *pIn, sc_file_t *file) { #ifdef BIG_ENDIAN_RUTOKEN file->size = pIn[3] + ((u_int16_t)pIn[2])*256; @@ -465,13 +465,11 @@ static int rutoken_select_file(sc_card_t *card, static int rutoken_set_file_attributes(sc_card_t *card, sc_file_t *file) { int ret = SC_ERROR_NOT_SUPPORTED; - return ret; } */ -static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, - u8 *out) +static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, u8 *out) { SC_FUNC_CALLED(card->ctx, 1); @@ -493,7 +491,6 @@ static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, case SC_FILE_TYPE_WORKING_EF: out[4] = 0x01; /* set the length (write to wBodyLen) */ - /* *((unsigned short int*)(out) + 1) = (unsigned short int)file->size; */ #ifdef BIG_ENDIAN_RUTOKEN out[2] = file->size / 256; out[3] = file->size % 256; @@ -507,7 +504,6 @@ static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED); } /* set file ID */ - /* *((unsigned short int*)(out) + 3) = (unsigned short int)file->id; */ #ifdef BIG_ENDIAN_RUTOKEN out[6] = file->id / 256; out[7] = file->id % 256; @@ -521,7 +517,6 @@ static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, else memcpy(out + 17, &default_sec_attr, SEC_ATTR_SIZE); - SC_FUNC_RETURN(card->ctx, 1, SC_NO_ERROR); } @@ -578,12 +573,43 @@ static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) SC_FUNC_RETURN(card->ctx, 1, rutoken_check_sw(card, apdu.sw1, apdu.sw2)); } +static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, + const u8 *data, size_t data_len, int *tries_left) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, ref_qualifier); + apdu.lc = data_len; + apdu.datalen = data_len; + apdu.data = data; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, + ref_qualifier); + ret = sc_transmit_apdu(card, &apdu); + if(ret >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if(ret == SC_ERROR_PIN_CODE_INCORRECT) + *tries_left = (int)(apdu.sw2 & 0x0f); + } + } + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + static int rutoken_logout(sc_card_t *card) { sc_apdu_t apdu; int ret = SC_ERROR_CARD_CMD_FAILED; sc_path_t path; + SC_FUNC_CALLED(card->ctx, 1); sc_format_path("3F00", &path); if (rutoken_select_file(card, &path, NULL) == SC_SUCCESS) { @@ -595,20 +621,81 @@ static int rutoken_logout(sc_card_t *card) SC_FUNC_RETURN(card->ctx, 1, ret); } +static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, + int ref_qualifier, const u8 *old, size_t oldlen, + const u8 *newref, size_t newlen, int *tries_left) +{ + int left; + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + if(old && oldlen) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0 + && apdu.sw1 != 0x90 && apdu.sw2 != 0x00) + { + rutoken_logout(card); + rutoken_verify(card, type, ref_qualifier, old, oldlen, &left); + } + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); + apdu.lc = newlen; + apdu.datalen = newlen; + apdu.data = newref; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) + *tries_left = (int)(apdu.sw2 & 0x0f); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, + int ref_qualifier, const u8 *puk, size_t puklen, + const u8 *newref, size_t newlen) +{ +#ifdef FORCE_VERIFY_RUTOKEN + int left; +#endif + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); +#ifdef FORCE_VERIFY_RUTOKEN + if(puk && puklen) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0 + && apdu.sw1 != 0x90 && apdu.sw2 != 0x00) + { + rutoken_logout(card); + rutoken_verify(card, type, ref_qualifier, puk, puklen, &left); + } + } +#endif + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + SC_FUNC_RETURN(card->ctx, 1, ret); +} + static int rutoken_restore_security_env(sc_card_t *card, int se_num) { int ret = SC_ERROR_CARD_CMD_FAILED; sc_apdu_t apdu; + SC_FUNC_CALLED(card->ctx, 1); - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 3, se_num); if(sc_transmit_apdu(card, &apdu) >= 0) ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); - SC_FUNC_RETURN(card->ctx, 1, ret); } -int rutoken_set_security_env(sc_card_t *card, +static int rutoken_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { @@ -620,19 +707,12 @@ int rutoken_set_security_env(sc_card_t *card, int ret = SC_NO_ERROR; if(env->algorithm == SC_ALGORITHM_RSA) { - const char PRK_DF[] = "3F0000000000FF001001"; - sc_debug(card->ctx, "RSA\n"); senv->algorithm = SC_ALGORITHM_RSA_RAW; - if(env->operation == SC_SEC_OPERATION_DECIPHER || env->operation == SC_SEC_OPERATION_SIGN) - { - sc_format_path(PRK_DF, &senv->path); - sc_append_path(&senv->path, &env->file_ref); - } - else ret = SC_ERROR_INVALID_ARGUMENTS; return ret; } else senv->algorithm = SC_ALGORITHM_GOST; + if (env->key_ref_len != 1) { sc_error(card->ctx, "No or invalid key reference\n"); @@ -671,24 +751,20 @@ int rutoken_set_security_env(sc_card_t *card, if(sc_transmit_apdu(card, &apdu) >= 0) ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); } - /* set driver data */ - if (ret == SC_NO_ERROR) - { - /* TODO: add check */ - senv->algorithm = SC_ALGORITHM_GOST; - senv->algorithm_flags = env->algorithm_flags; - senv->key_file_id = env->key_ref[0]; - senv->key_size = 256; - } SC_FUNC_RETURN(card->ctx, 1, ret); } -void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) +static void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) { if(data) { +#ifdef BIG_ENDIAN_RUTOKEN + data[0] = (u8)(pHdr->wDOBodyLen / 0x100); + data[1] = (u8)(pHdr->wDOBodyLen % 0x100); +#else data[0] = (u8)(pHdr->wDOBodyLen % 0x100); data[1] = (u8)(pHdr->wDOBodyLen / 0x100); +#endif data[2] = (u8)(pHdr->OTID.byObjectType); data[3] = (u8)(pHdr->OTID.byObjectID); data[4] = (u8)(pHdr->OP.byObjectOptions); @@ -700,12 +776,6 @@ void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) } } -void rutoken_set_do(u8 *data, sc_DO_V2_t * pDO) -{ - rutoken_set_do_hdr(data, &pDO->HDR); - memcpy(data + SC_RUTOKEN_DO_HDR_LEN, pDO->abyDOBody, pDO->HDR.wDOBodyLen); -} - static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) { int ret = SC_ERROR_CARD_CMD_FAILED; @@ -761,7 +831,8 @@ static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) } else { - rutoken_set_do(data, pDO); + rutoken_set_do_hdr(data, &pDO->HDR); + memcpy(data + SC_RUTOKEN_DO_HDR_LEN, pDO->abyDOBody, pDO->HDR.wDOBodyLen); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x62); apdu.data= data; @@ -864,135 +935,204 @@ static int rutoken_get_serial(sc_card_t *card, sc_serial_number_t *pSerial) static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen, int p1, int p2, int isIV) { + const size_t cipher_chunk = 248; /* cipher_chunk <= SC_MAX_APDU_BUFFER_SIZE */ + size_t len, outlen_tail = outlen; + u8 *buf; + int ret; sc_apdu_t apdu; - int rv = SC_NO_ERROR; - sc_debug(card->ctx,": crgram_len %i; outlen %i\n", crgram_len, outlen); - if (!out || !outlen) + SC_FUNC_CALLED(card->ctx, 1); + sc_debug(card->ctx, ": crgram_len %i; outlen %i\n", crgram_len, outlen); + + if (!out) return SC_ERROR_INVALID_ARGUMENTS; + if (crgram_len < 16 || ((crgram_len) % 8)) + return SC_ERROR_WRONG_LENGTH; + + buf = malloc(cipher_chunk); + if (!buf) + return SC_ERROR_OUT_OF_MEMORY; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); - - apdu.resp = (u8*)malloc(SC_MAX_APDU_BUFFER_SIZE); - if (!apdu.resp) - return SC_ERROR_OUT_OF_MEMORY; - apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; - if (crgram_len < 16 || ((crgram_len) % 8)) - rv = SC_ERROR_WRONG_LENGTH; - size_t cur_len = 0; - unsigned char is_first = 1; - unsigned int cur_data_len; - - while( (rv == SC_NO_ERROR) && (cur_len != crgram_len) && (outlen > cur_len)) + do { - cur_data_len = (crgram_len - cur_len) % 248; - if(!cur_data_len) cur_data_len = 248; - - apdu.data = crgram + cur_len; - apdu.lc = apdu.datalen = cur_data_len; - apdu.cla = crgram_len - cur_len - cur_data_len > 0 ? 0x10 : 0x00; - apdu.le = apdu.resplen = 248; - - if((rv = sc_transmit_apdu(card, &apdu)) >= 0 && - (rv = rutoken_check_sw(card, apdu.sw1, apdu.sw2)) == SC_NO_ERROR) + len = (crgram_len > cipher_chunk) ? cipher_chunk : crgram_len; + apdu.lc = len; + apdu.datalen = len; + apdu.data = crgram; + crgram += len; + crgram_len -= len; + + apdu.cla = (crgram_len == 0) ? 0x00 : 0x10; + apdu.le = len; + apdu.resplen = len; + apdu.resp = buf; + + if (sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + else + ret = SC_ERROR_CARD_CMD_FAILED; + if (ret == SC_NO_ERROR) { - if(isIV && is_first) + if (isIV) { - /* break initialization vector */ - memcpy(out, apdu.resp + 8, apdu.resplen - 8); - out += apdu.resplen - 8; - cur_len += apdu.resplen; - is_first = 0; + apdu.resp += 8; + apdu.resplen -= 8; + isIV = 0; } + if (apdu.resplen > outlen_tail) + ret = SC_ERROR_BUFFER_TOO_SMALL; else { - /* memcpy(out + cur_len, apdu.resp, apdu.resplen); */ memcpy(out, apdu.resp, apdu.resplen); - cur_len += apdu.resplen; out += apdu.resplen; + outlen_tail -= apdu.resplen; } } - } - if (rv == SC_NO_ERROR) rv = (cur_len == crgram_len) ? isIV ? cur_len - 8 : cur_len : SC_ERROR_BUFFER_TOO_SMALL; + } while (ret == SC_NO_ERROR && crgram_len != 0); - if (apdu.resp) - free(apdu.resp); + free(buf); - sc_debug(card->ctx, "return decipher len %d\n", rv); - return rv; -} + sc_debug(card->ctx, "len out cipher %d\n", outlen - outlen_tail); + if (ret == SC_NO_ERROR) + ret = (outlen_tail == 0) ? (int)outlen : SC_ERROR_WRONG_LENGTH; -/* Launcher for chipher */ -static int rutoken_decipher(sc_card_t *card, struct sc_rutoken_decipherinfo *ptr) -{ - return rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); + SC_FUNC_RETURN(card->ctx, 1, ret); } /* Launcher for chipher */ -static int rutoken_encipher(sc_card_t *card, struct sc_rutoken_decipherinfo *ptr) +static int rutoken_cipher_gost(sc_card_t *card, + struct sc_rutoken_decipherinfo *ptr, char is_enchiper) { - return rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); + int ret; + + if (is_enchiper) + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); + else + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); + if (ret > 0) + { + if ((size_t)ret == ptr->outlen) + ret = SC_SUCCESS; + else + ret = SC_ERROR_INTERNAL; /* SC_ERROR_DECRYPT_FAILED; */ + } + return ret; + } -int rutoken_read_file(sc_card_t *card, sc_path_t *path, u8 **out, int *len) +static int rutoken_compute_mac_gost(sc_card_t *card, + const u8 *in, size_t ilen, + u8 *out, size_t olen) { - int r; - u8 *data = NULL; - sc_file_t *file = NULL; - r = sc_lock(card); - SC_TEST_RET(card->ctx, r, "sc_lock() failed"); - *len = 0; - r = rutoken_select_file(card, path, &file); - if (r == SC_SUCCESS) + const size_t signing_chunk = 248; + size_t len; + int ret; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!in || !out || olen != 4 || ilen == 0) + return SC_ERROR_INVALID_ARGUMENTS; + do { - data = (u8 *) malloc(file->size); - if (data == NULL) - r = SC_ERROR_OUT_OF_MEMORY; - } - if (r == SC_SUCCESS) - r = sc_read_binary(card, 0, data, file->size, 0); - if (file && r == file->size) + sc_format_apdu(card, &apdu, + ilen > signing_chunk ? + SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, + 0x2A, 0x90, 0x80); + len = (ilen > signing_chunk) ? signing_chunk : ilen; + apdu.lc = len; + apdu.datalen = len; + apdu.data = in; + in += len; + ilen -= len; + if (ilen == 0) { - *len = r; - *out = data; - r = SC_SUCCESS; + apdu.cla = 0x00; + apdu.le = olen; + apdu.resplen = olen; + apdu.resp = out; } else - free(data); - sc_unlock(card); + apdu.cla = 0x10; + if (sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + else + ret = SC_ERROR_CARD_CMD_FAILED; + } while (ret == SC_NO_ERROR && ilen != 0); - if(file) sc_file_free(file); - - return r; + SC_FUNC_RETURN(card->ctx, 1, ret); } - -int rutoken_read_prkey(sc_card_t *card, - sc_path_t *path, - struct sc_pkcs15_prkey **out) -{ - int ret, len = 0; - u8 *data = NULL; - ret = rutoken_read_file(card, path, &data, &len); - if (ret == SC_SUCCESS) - { - ret = get_prkey_from_bin(data, len, out); - } - - return ret; -} - /* RSA emulation */ #ifdef HAVE_OPENSSL + +static int rutoken_get_current_fileid(sc_card_t *card, u8 id[2]) +{ + sc_apdu_t apdu; + int ret = SC_ERROR_CARD_CMD_FAILED; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x11); + apdu.resp = id; + apdu.resplen = sizeof(id); + apdu.le = 2; + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_read_prkey(sc_card_t *card, struct sc_pkcs15_prkey **out) +{ + int r; + u8 id[2]; + u8 *data; + sc_path_t path; + sc_file_t *file = NULL; + + r = sc_lock(card); + if(r != SC_SUCCESS) + return r; + + r = rutoken_get_current_fileid(card, id); + if(r == SC_SUCCESS) + { + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, sizeof(id), 0, -1); + r = rutoken_select_file(card, &path, &file); + } + if(r == SC_SUCCESS && file) + { + data = malloc(file->size); + if(data == NULL) + r = SC_ERROR_OUT_OF_MEMORY; + else + { + r = sc_read_binary(card, 0, data, file->size, 0); + if(r > 0 && (size_t)r == file->size) + r = get_prkey_from_bin(data, file->size, out); + memset(data, 0, file->size); + free(data); + } + } + if(file) + sc_file_free(file); + sc_unlock(card); + return r; +} + #define GETBN(bn) ((bn)->len? BN_bin2bn((bn)->data, (bn)->len, NULL) : NULL) -static int extract_key(sc_card_t *card, sc_path_t *path, EVP_PKEY **pk) + +static int extract_key(sc_card_t *card, EVP_PKEY **pk) { struct sc_pkcs15_prkey *key = NULL; int r; - r = rutoken_read_prkey(card, path, &key); + SC_FUNC_CALLED(card->ctx, 1); + + r = rutoken_read_prkey(card, &key); if (r < 0) return r; @@ -1032,26 +1172,21 @@ static int extract_key(sc_card_t *card, sc_path_t *path, EVP_PKEY **pk) return r; } -static int sign_ext(sc_card_t *card, sc_path_t *path, - const u8 *data, size_t len, u8 *out, size_t out_len) +static int sign_ext(sc_card_t *card, const u8 *data, size_t len, u8 *out, size_t out_len) { - auth_senv_t *senv = (auth_senv_t *)card->drv_data; EVP_PKEY *pkey = NULL; - int ret; + int ret, r; - out_len = 0; - ret = extract_key(card, path, &pkey); - if (!senv) ret = SC_ERROR_INTERNAL; - if (ret >= 0) - { - switch (senv->algorithm) - { - case SC_ALGORITHM_RSA_RAW: - ret = RSA_private_encrypt(len, data, out, pkey->pkey.rsa, - RSA_PKCS1_PADDING); - if ( ret >= 0) - ret = out_len; - else + SC_FUNC_CALLED(card->ctx, 1); + + if (out_len < len) + return SC_ERROR_INVALID_ARGUMENTS; + + ret = extract_key(card, &pkey); + if (ret == SC_SUCCESS) + { + r = RSA_private_encrypt(len, data, out, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if ( r < 0) { ret = SC_ERROR_INTERNAL; char error[1024]; @@ -1060,143 +1195,125 @@ static int sign_ext(sc_card_t *card, sc_path_t *path, sc_error(card->ctx, error); ERR_free_strings(); } - break; - } } - if(pkey)EVP_PKEY_free(pkey); + if(pkey) + EVP_PKEY_free(pkey); return ret; } -#if 0 -static int decipher_ext(sc_card_t *card, sc_path_t *path, - const u8 *data, size_t len, u8 *out, size_t out_len) +static int decipher_ext(sc_card_t *card, const u8 *data, size_t len, u8 *out, size_t out_len) { - //int r = SC_ERROR_NOT_SUPPORTED; - auth_senv_t *senv = (auth_senv_t *)card->drv_data; EVP_PKEY *pkey = NULL; + int ret; - int r; + SC_FUNC_CALLED(card->ctx, 1); - out_len = 0; - r = extract_key(card, path, &pkey); - if (r < 0) - return r; + if (out_len < len) + return SC_ERROR_INVALID_ARGUMENTS; - switch (senv->algorithm) + ret = extract_key(card, &pkey); + if (ret == SC_SUCCESS) { - case SC_ALGORITHM_RSA_RAW: - r = RSA_private_decrypt(len, data, out, pkey->pkey.rsa, - RSA_NO_PADDING); - if ( r >= 0) - out_len = r; - else + ret = RSA_private_decrypt(len, data, out, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if ( ret < 0) { - r = SC_ERROR_INTERNAL; - char error[1024]; - ERR_load_crypto_strings(); - ERR_error_string(ERR_get_error(), error); - sc_error(card->ctx, error); - ERR_free_strings(); - } - break; - } - if(pkey)EVP_PKEY_free(pkey); - /* - EVP_PKEY_RSA *pkey = NULL; - - r = extract_key(card, &senv->path, &pkey); - if (r < 0) - return r; - - switch (senv->algorithm) { - case SC_ALGORITHM_RSA_RAW: - r = EVP_PKEY_decrypt(out, data, len, pkey); - if (r <= 0) { - fprintf(stderr, "Decryption failed.\n"); - r = SC_ERROR_INTERNAL; + ret = SC_ERROR_INTERNAL; char error[1024]; ERR_load_crypto_strings(); ERR_error_string(ERR_get_error(), error); sc_error(card->ctx, error); ERR_free_strings(); } - break; - default: - fprintf(stderr, "Key type not supported.\n"); - r = SC_ERROR_NOT_SUPPORTED; } - - if(pkey)EVP_PKEY_free(pkey);*/ - return r; + if(pkey) + EVP_PKEY_free(pkey); + return ret; } -#endif -static int rutoken_decipher_rsa(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) +static int rutoken_decipher(sc_card_t *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) { -#if 0 - //TODO: Soft RSA encryption. Uncomment and check that; auth_senv_t *senv = (auth_senv_t *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!senv) + return SC_ERROR_INTERNAL; + if(senv->algorithm == SC_ALGORITHM_GOST) { return rutoken_cipher_p(card, data, datalen, out, outlen, 0x80, 0x86, 1); } else if (senv->algorithm == SC_ALGORITHM_RSA_RAW) { - int key_id = senv->key_file_id; - return decipher_ext(card, &senv->path, data, datalen, out, outlen); + return decipher_ext(card, data, datalen, out, outlen); } else return SC_ERROR_NOT_SUPPORTED; -#endif - return SC_ERROR_NOT_SUPPORTED; } -static int rutoken_compute_signature_gost(sc_card_t *card, const u8 *in, size_t ilen, u8 * out, size_t olen) +static int rutoken_compute_signature(struct sc_card *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) { - sc_debug(card->ctx, "sign gost"); - int ret = SC_ERROR_CARD_CMD_FAILED; - sc_apdu_t apdu = {0}; - u8 *buff[256] = {0}; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x90, 0x80); - apdu.lc = apdu.datalen = ilen; - apdu.data = in; - apdu.resplen = olen; - apdu.resp = (u8*)buff; - apdu.le = 4; - if(sc_transmit_apdu(card, &apdu) >= 0) - { - memcpy(out, apdu.resp, apdu.resplen); - ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); - } - SC_FUNC_RETURN(card->ctx, 4, apdu.resplen); -} - - -static int rutoken_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, - u8 * out, size_t outlen) -{ - sc_debug(card->ctx, "sign"); auth_senv_t *senv = (auth_senv_t *)card->drv_data; - if(senv->algorithm == SC_ALGORITHM_GOST) + + SC_FUNC_CALLED(card->ctx, 1); + + if (!senv) + return SC_ERROR_INTERNAL; + + if (senv->algorithm == SC_ALGORITHM_GOST) { - return rutoken_compute_signature_gost(card, data, datalen, out, outlen); + return rutoken_compute_mac_gost(card, data, datalen, out, outlen); } else if (senv->algorithm == SC_ALGORITHM_RSA_RAW) { - return sign_ext(card, &senv->path, data, datalen, out, outlen); + return sign_ext(card, data, datalen, out, outlen); } + else return SC_ERROR_NOT_SUPPORTED; } #endif +static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t count) +{ + int ret = SC_NO_ERROR; + sc_apdu_t apdu; + u8 rbuf[32]; + size_t n; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); + apdu.le = sizeof(rbuf); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + while (count > 0) + { + ret = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if (ret != SC_SUCCESS) + break; + if (apdu.resplen != sizeof(rbuf)) + { + ret = SC_ERROR_UNKNOWN; + break; + } + n = count < sizeof(rbuf) ? count : sizeof(rbuf); + memcpy(rnd, rbuf, n); + count -= n; + rnd += n; + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} static int rutoken_get_info(sc_card_t *card, void *buff) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - int r; + int r = SC_ERROR_CARD_CMD_FAILED; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x89); apdu.resp = rbuf; @@ -1210,25 +1327,27 @@ static int rutoken_get_info(sc_card_t *card, void *buff) SC_FUNC_RETURN(card->ctx, 1, r); } -static int rutoken_tries_left(sc_card_t *card, int *chv_tries_left) +static int rutoken_format(sc_card_t *card, int apdu_ins) { - int ret = SC_ERROR_INCORRECT_PARAMETERS; + int ret = SC_ERROR_CARD_CMD_FAILED; sc_apdu_t apdu; - if (*chv_tries_left != 1 && *chv_tries_left != 2 && *chv_tries_left != 0) - SC_FUNC_RETURN(card->ctx, 1, ret); - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, *chv_tries_left); - if((ret = sc_transmit_apdu(card, &apdu)) >= 0) - { + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, apdu_ins, 0x00, 0x00); + apdu.cla = 0x80; + if(sc_transmit_apdu(card, &apdu) >= 0) ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); - if (ret == SC_ERROR_PIN_CODE_INCORRECT) - *chv_tries_left = (int)(apdu.sw2 & 0x0f); - } + SC_FUNC_RETURN(card->ctx, 1, ret); } static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { - int ret = ptr != NULL ? SC_NO_ERROR : SC_ERROR_INVALID_ARGUMENTS; + int ret = (ptr != NULL + /*|| cmd == SC_CARDCTL_ERASE_CARD */ + || cmd == SC_CARDCTL_RUTOKEN_FORMAT_INIT + || cmd == SC_CARDCTL_RUTOKEN_FORMAT_END + ) ? SC_NO_ERROR : SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_CALLED(card->ctx, 1); if(ret == SC_NO_ERROR) @@ -1257,26 +1376,21 @@ static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) ret = rutoken_get_info(card, ptr); break; case SC_CARDCTL_RUTOKEN_GOST_ENCIPHER: - ret = rutoken_encipher(card, ptr); + ret = rutoken_cipher_gost(card, ptr, 1); break; case SC_CARDCTL_RUTOKEN_GOST_DECIPHER: - ret = rutoken_decipher(card, ptr); + ret = rutoken_cipher_gost(card, ptr, 0); break; - case SC_CARDCTL_RUTOKEN_TRIES_LEFT: - ret = rutoken_tries_left(card, ptr); + /* case SC_CARDCTL_ERASE_CARD: */ + case SC_CARDCTL_RUTOKEN_FORMAT_INIT: + /* ret = rutoken_format(card, 0x7a); *//* APDU: INIT RUTOKEN */ + ret = rutoken_format(card, 0x8a); /* APDU: NEW INIT RUTOKEN */ + break; + case SC_CARDCTL_RUTOKEN_FORMAT_END: + ret = rutoken_format(card, 0x7b); /* APDU: FORMAT END */ break; default: sc_debug(card->ctx, "cmd = %d", cmd); -#if 0 - { - sc_apdu_t *pApdu = ptr; - if(sc_transmit_apdu(card, pApdu) >= 0) - ret = rutoken_check_sw(card, pApdu->sw1, pApdu->sw2); - else - ret = SC_ERROR_CARD_CMD_FAILED; - break; - } -#endif ret = SC_ERROR_NOT_SUPPORTED; break; } @@ -1299,16 +1413,24 @@ static struct sc_card_driver * sc_get_driver(void) rutoken_ops.delete_file = rutoken_delete_file; rutoken_ops.list_files = rutoken_list_files; rutoken_ops.card_ctl = rutoken_card_ctl; - /* rutoken_ops.verify = rutoken_verify; */ + rutoken_ops.get_challenge = rutoken_get_challenge; - #ifdef HAVE_OPENSSL - rutoken_ops.decipher = rutoken_decipher_rsa; +#ifdef HAVE_OPENSSL + rutoken_ops.decipher = rutoken_decipher; rutoken_ops.compute_signature = rutoken_compute_signature; - #endif +#else + rutoken_ops.decipher = NULL; + rutoken_ops.compute_signature = NULL; +#endif rutoken_ops.set_security_env = rutoken_set_security_env; rutoken_ops.restore_security_env = rutoken_restore_security_env; + + rutoken_ops.verify = rutoken_verify; rutoken_ops.logout = rutoken_logout; + rutoken_ops.change_reference_data = rutoken_change_reference_data; + rutoken_ops.reset_retry_counter = rutoken_reset_retry_counter; + rutoken_ops.pin_cmd = NULL; rutoken_ops.read_record = NULL; rutoken_ops.write_record = NULL; rutoken_ops.append_record = NULL; diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index a81beecc..2bba7f07 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -142,8 +142,8 @@ enum { SC_CARDCTL_ASEPCOS_ACTIVATE_FILE, /* - * ruToken specific calls - */ + * ruToken specific calls + */ SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'), /* PUT_DATA */ SC_CARDCTL_RUTOKEN_CREATE_DO, @@ -155,7 +155,8 @@ enum { SC_CARDCTL_RUTOKEN_GET_DO_INFO, SC_CARDCTL_RUTOKEN_GOST_ENCIPHER, SC_CARDCTL_RUTOKEN_GOST_DECIPHER, - SC_CARDCTL_RUTOKEN_TRIES_LEFT + SC_CARDCTL_RUTOKEN_FORMAT_INIT, + SC_CARDCTL_RUTOKEN_FORMAT_END }; enum { @@ -544,8 +545,8 @@ typedef struct sc_DO_INFO_V2 { u8 pDoData[256]; } sc_DO_INFO_t; -struct sc_rutoken_decipherinfo{ - u8 *inbuf; +struct sc_rutoken_decipherinfo { + const u8 *inbuf; size_t inlen; u8 *outbuf; size_t outlen; diff --git a/src/libopensc/pkcs15-rutoken.c b/src/libopensc/pkcs15-rutoken.c index 86b81e28..e96cdea4 100644 --- a/src/libopensc/pkcs15-rutoken.c +++ b/src/libopensc/pkcs15-rutoken.c @@ -1,5 +1,5 @@ /* - * ruToken specific operation for PKCS15 initialization + * PKCS15 emulation layer for ruToken * * Copyright (C) 2007 Pavel Mironchik * Copyright (C) 2007 Eugene Hermann @@ -30,756 +30,71 @@ #include #include #include -#include "../pkcs15init/pkcs15-init.h" -#include "../pkcs15init/profile.h" +#include -#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ -SC_PKCS15_PRKEY_USAGE_DECRYPT | \ -SC_PKCS15_PRKEY_USAGE_WRAP | \ -SC_PKCS15_PRKEY_USAGE_UNWRAP | \ -SC_PKCS15_PRKEY_USAGE_SIGN - -#define P15_DF(T) T & SC_PKCS15_TYPE_CERT ? SC_PKCS15_CDF \ - : T & SC_PKCS15_TYPE_PUBKEY ? SC_PKCS15_PUKDF : \ - T & SC_PKCS15_TYPE_PRKEY_RSA ? SC_PKCS15_PRKDF : SC_PKCS15_DODF - -int rutoken_erase(struct sc_profile *, sc_card_t *); - -#define MAX_ID 255 - -const sc_SecAttrV2_t map_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; -const sc_SecAttrV2_t pr_sec_attr = {0x43, 1, 1, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 0, 2}; -const sc_SecAttrV2_t pb_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; - -const char GCHV_DF[] = "3F0000000000"; -const char APP_DF[] = "3F0000000000FF00"; -const char PRK_DF[] = "3F0000000000FF001001"; -const char PUK_DF[] = "3F0000000000FF001002"; -const char C_DF[] = "3F0000000000FF001003"; - -enum DF_IDs -{ - PrKDFid = 0x1001, - PuKDFid = 0x1002, - CDFid = 0x1003, - DFsId = 0xefff, - DFsSize = 2048 -}; - -/* BLOB definition */ - -typedef struct _RSAPUBKEY { - int magic; - int bitlen; - int pubexp; -} RSAPUBKEY; - -typedef struct _PUBLICKEYSTRUC { - u8 bType; - u8 bVersion; - u_int16_t reserved; - u_int32_t aiKeyAlg; -} BLOBHEADER; - -typedef struct _PRIVATEKEYBLOB { - BLOBHEADER blobheader; - RSAPUBKEY rsapubkey; - u8 *modulus; - u8 *prime1; - u8 *prime2; - u8 *exponent1; - u8 *exponent2; - u8 *coefficient; - u8 *privateExponent; -} PRIVATEKEYBLOB; - -void ArrayReverse(u8 *buf, int size); -int bin_to_privite_blob(PRIVATEKEYBLOB *pr_blob, u8 *buf, int buf_len); - -/* BLOB */ - -int create_privite_blob(PRIVATEKEYBLOB *pr_blob, const struct sc_pkcs15_prkey_rsa *key){ - int bitlen = key->modulus.len*8; - /* blobheader */ - /* u8 bType; */ - pr_blob->blobheader.bType = 0x07; - /* u8 bVersion; */ - pr_blob->blobheader.bVersion = 0x02; - /* u16 reserved; */ - pr_blob->blobheader.reserved = 0; - /* u32 aiKeyAlg; */ - pr_blob->blobheader.aiKeyAlg = 0x0000a400; - - pr_blob->rsapubkey.magic = 0x32415352; /* "RSA2" */ - pr_blob->rsapubkey.bitlen = bitlen; - int n = key->exponent.len; - while (n > 0) - { - ((u8*)&pr_blob->rsapubkey.pubexp)[key->exponent.len - n] = key->exponent.data[n - 1]; - n--; - } - pr_blob->modulus = malloc(bitlen/8); - pr_blob->prime1 = malloc(bitlen/16); - pr_blob->prime2 = malloc(bitlen/16); - pr_blob->exponent1 = malloc(bitlen/16); - pr_blob->exponent2 = malloc(bitlen/16); - pr_blob->coefficient = malloc(bitlen/16); - pr_blob->privateExponent = malloc(bitlen/8); - - - memcpy(pr_blob->modulus, key->modulus.data, key->modulus.len); - ArrayReverse(pr_blob->modulus, key->modulus.len); - memcpy(pr_blob->prime1, key->p.data, key->p.len); - ArrayReverse(pr_blob->prime1, key->p.len); - memcpy(pr_blob->prime2, key->q.data, key->q.len); - ArrayReverse(pr_blob->prime2, key->q.len); - memcpy(pr_blob->exponent1, key->dmp1.data, key->dmp1.len); - ArrayReverse(pr_blob->exponent1, key->dmp1.len); - memcpy(pr_blob->exponent2, key->dmq1.data, key->dmq1.len); - ArrayReverse(pr_blob->exponent2, key->dmq1.len); - memcpy(pr_blob->coefficient, key->iqmp.data, key->iqmp.len); - ArrayReverse(pr_blob->coefficient, key->iqmp.len); - memcpy(pr_blob->privateExponent, key->d.data, key->d.len); - ArrayReverse(pr_blob->privateExponent, key->d.len); - return 0; -} - -int get_sc_pksc15_prkey_rsa(const PRIVATEKEYBLOB *pr_blob, struct sc_pkcs15_prkey_rsa *key){ - int bitlen = pr_blob->rsapubkey.bitlen; - long exp = 0x00010001; - key->modulus.data = malloc(bitlen/8); - key->modulus.len = bitlen/8; - key->p.data = malloc(bitlen/16); - key->p.len = bitlen/16; - key->q.data = malloc(bitlen/16); - key->q.len = bitlen/16; - key->dmp1.data = malloc(bitlen/16); - key->dmp1.len = bitlen/16; - key->dmq1.data = malloc(bitlen/16); - key->dmq1.len = bitlen/16 - 1; - key->iqmp.data = malloc(bitlen/16); - key->iqmp.len = bitlen/16; - key->d.data = malloc(bitlen/8); - key->d.len = bitlen/8; - key->exponent.data = malloc(3); - memcpy(key->exponent.data, &exp, 3); - key->exponent.len = 3; - - memcpy(key->modulus.data, pr_blob->modulus, key->modulus.len); - ArrayReverse(key->modulus.data, key->modulus.len); - memcpy(key->p.data, pr_blob->prime1, key->p.len); - ArrayReverse(key->p.data, key->p.len); - memcpy(key->q.data, pr_blob->prime2, key->q.len); - ArrayReverse(key->q.data, key->q.len); - memcpy(key->dmp1.data, pr_blob->exponent1, key->dmp1.len); - ArrayReverse(key->dmp1.data, key->dmp1.len); - memcpy(key->dmq1.data, pr_blob->exponent2, key->dmq1.len); - ArrayReverse(key->dmq1.data, key->dmq1.len); - memcpy(key->iqmp.data, pr_blob->coefficient, key->iqmp.len); - ArrayReverse(key->iqmp.data, key->iqmp.len); - memcpy(key->d.data, pr_blob->privateExponent, key->d.len); - ArrayReverse(key->d.data, key->d.len); - return 0; -} - -int get_privite_blob_len(const PRIVATEKEYBLOB *pr_blob){ - return sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 9*(pr_blob->rsapubkey.bitlen/16); -} - -int free_privite_blob(PRIVATEKEYBLOB *pr_blob){ - free(pr_blob->modulus); - free(pr_blob->prime1); - free(pr_blob->prime2); - free(pr_blob->exponent1); - free(pr_blob->exponent2); - free(pr_blob->coefficient); - free(pr_blob->privateExponent); - return 0; -} - - -int privite_blob_to_bin(const PRIVATEKEYBLOB *pr_blob, u8 *buf, size_t *buf_len){ - - if(*buf_len < get_privite_blob_len(pr_blob) + 2) - return -1; - - buf[0] = 2; - buf[1] = 1; - u8 *tmp = buf + 2; - memcpy(tmp, &pr_blob->blobheader, sizeof(pr_blob->blobheader)); - tmp += sizeof(pr_blob->blobheader); - - memcpy(tmp, &pr_blob->rsapubkey, sizeof(pr_blob->rsapubkey)); - tmp += sizeof(pr_blob->rsapubkey); - - memcpy(tmp, pr_blob->modulus, pr_blob->rsapubkey.bitlen/8); - tmp += pr_blob->rsapubkey.bitlen/8; - - memcpy(tmp, pr_blob->prime1, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(tmp, pr_blob->prime2, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(tmp, pr_blob->exponent1, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(tmp, pr_blob->exponent2, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(tmp, pr_blob->coefficient, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(tmp, pr_blob->privateExponent, pr_blob->rsapubkey.bitlen/8); - tmp += pr_blob->rsapubkey.bitlen/8; - *buf_len = get_privite_blob_len(pr_blob) + 2; - return 0; -} - -int get_prkey_from_bin(u8* data, int len, struct sc_pkcs15_prkey **key) -{ - int ret = -1; - *key = malloc(sizeof(struct sc_pkcs15_prkey)); - if(data && *key) - { - PRIVATEKEYBLOB pr_blob; - memset(*key, 0, sizeof(struct sc_pkcs15_prkey)); - bin_to_privite_blob(&pr_blob, data, len); - ret = get_sc_pksc15_prkey_rsa(&pr_blob, &(*key)->u.rsa); - (*key)->algorithm = SC_ALGORITHM_RSA; - free_privite_blob(&pr_blob); - } - return ret; -} - -void ArrayReverse(u8 *buf, int size) -{ - int i, j; - u8 *tmp = malloc(size); - if (tmp) - { - for(i = 0, j = size - 1; i < size; i++, j--) - tmp[i] = buf[j]; - memcpy(buf, tmp, size); - free(tmp); - } -} - -int bin_to_privite_blob(PRIVATEKEYBLOB *pr_blob, u8 *buf, int buf_len){ - - u8 *tmp = buf + 2; - memcpy(&pr_blob->blobheader, tmp, sizeof(pr_blob->blobheader)); - tmp += sizeof(pr_blob->blobheader); - - memcpy(&pr_blob->rsapubkey, tmp, sizeof(pr_blob->rsapubkey)); - tmp += sizeof(pr_blob->rsapubkey); - - int bitlen = pr_blob->rsapubkey.bitlen; - pr_blob->modulus = malloc(bitlen/8); - pr_blob->prime1 = malloc(bitlen/16); - pr_blob->prime2 = malloc(bitlen/16); - pr_blob->exponent1 = malloc(bitlen/16); - pr_blob->exponent2 = malloc(bitlen/16); - pr_blob->coefficient = malloc(bitlen/16); - pr_blob->privateExponent = malloc(bitlen/8); - - memcpy(pr_blob->modulus, tmp, pr_blob->rsapubkey.bitlen/8); - tmp += pr_blob->rsapubkey.bitlen/8; - - memcpy(pr_blob->prime1, tmp, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(pr_blob->prime2, tmp, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(pr_blob->exponent1, tmp, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(pr_blob->exponent2, tmp, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(pr_blob->coefficient, tmp, pr_blob->rsapubkey.bitlen/16); - tmp += pr_blob->rsapubkey.bitlen/16; - - memcpy(pr_blob->privateExponent, tmp, pr_blob->rsapubkey.bitlen/8); - tmp += pr_blob->rsapubkey.bitlen/8; - - return 0; -} - -/* - * Create/override new EF. - */ -int rutoken_create_file(sc_card_t *card, sc_path_t *path, sc_file_t *ef) -{ - int ret = SC_SUCCESS; - if(path) - { - ret = card->ops->select_file(card, path, NULL); - if (ret == SC_SUCCESS) - { - sc_path_t del_path; - del_path.len = 2; - del_path.type = SC_PATH_TYPE_FILE_ID; - del_path.value[0] = (u8)(ef->id / 256); - del_path.value[1] = (u8)(ef->id % 256); - if (card->ops->select_file(card, &del_path, NULL) == SC_SUCCESS) - ret = card->ops->delete_file(card, &del_path); - } - } - if (ret == SC_SUCCESS) - { - ret = card->ops->create_file(card, ef); - } - - return ret; -} - -/* - * Create a DF - */ -static int -rutoken_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df) -{ - int ret = SC_SUCCESS; - sc_file_t *file = NULL; - - SC_FUNC_CALLED(card->ctx, 1); - ret = card->ops->select_file(card, &df->path, &file); - if (ret == SC_ERROR_FILE_NOT_FOUND) - ret = card->ops->create_file(card, df); - else if(file && file->type != SC_FILE_TYPE_DF) - ret = SC_ERROR_WRONG_CARD; - - if(file) - sc_file_free(file); - return ret; -} - - -/* - * Select a key reference - */ - -static int -rutoken_select_key_reference(sc_profile_t *profile, sc_card_t *card, - sc_pkcs15_prkey_info_t *key_info) -{ - SC_FUNC_CALLED(card->ctx, 1); - /* hocus-pocus :) */ - sc_format_path(PRK_DF, &key_info->path); - sc_append_file_id(&key_info->path, key_info->key_reference); - //g_nKeyRef = key_info->key_reference; - return key_info->key_reference >= 0 && key_info->key_reference <= MAX_ID ? SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; -} - -/* - * Create a private key object. - * This is a no-op. - */ -static int -rutoken_create_key(sc_profile_t *profile, sc_card_t *card, - sc_pkcs15_object_t *obj) -{ - SC_FUNC_CALLED(card->ctx, 1); - return 0; -} - -/* Send the pkcs15 profile to its doom. NOTE:This change current DF! */ -static void fix_pkcs15(sc_profile_t *profile, sc_card_t *card, int type, int x) -{ - sc_file_t *df; - - if(profile->df[type]) - df = profile->df[type]; - else - { - df = sc_file_new(); - profile->df[type] = df; - } - - switch(type) - { - case SC_PKCS15_PRKDF: - sc_format_path(PRK_DF, &df->path); - break; - case SC_PKCS15_PUKDF: - sc_format_path(PUK_DF, &df->path); - break; - case SC_PKCS15_CDF: - sc_format_path(C_DF, &df->path); - break; - default: - return; - } - df->id = DFsId; - df->size = DFsSize; - df->type = SC_FILE_TYPE_WORKING_EF; - df->ef_structure = SC_FILE_EF_TRANSPARENT; - - sc_append_file_id(&df->path, df->id); - sc_path_t odf_path; - sc_format_path("3f0000000000dfff", &odf_path); - if(card->ops->select_file(card, &odf_path, NULL) == SC_SUCCESS) - { - odf_path.len = 0; - odf_path.type = SC_PATH_TYPE_FILE_ID; - card->ops->delete_file(card, &odf_path); - } -} - -int rutoken_check_df(sc_card_t *card, int df_id) -{ - int ret = -1; - sc_path_t path; - - sc_file_t *file = sc_file_new(); - if(file) - { - sc_format_path(GCHV_DF, &path); - ret = card->ops->select_file(card, &path, NULL); - } - else - ret = SC_ERROR_OUT_OF_MEMORY; - if (ret == SC_SUCCESS) - { - sc_format_path(APP_DF, &file->path); - file->type = SC_FILE_TYPE_DF; - // FIXME: change to 'df' secattr - sc_file_set_sec_attr(file, (u8*)&pb_sec_attr, SEC_ATTR_SIZE); - /* appdf (ff00) */ - file->id = 0xff00; - ret = rutoken_create_dir(NULL, card, file); - } - if (ret == SC_SUCCESS) - { - /* p15df */ - sc_format_path(APP_DF, &path); - card->ops->select_file(card, &path, NULL); - file->id = df_id; - file->path = path; - sc_append_file_id(&file->path, df_id); - ret = rutoken_create_dir(NULL, card, file); - } - if(file) sc_file_free(file); - return ret; -} - -/* - * create private key files - */ -int rutoken_create_prkeyfile(sc_card_t *card, - sc_pkcs15_prkey_info_t *key_info, - sc_file_t **prkf, size_t prsize) -{ - int ret; - SC_FUNC_CALLED(card->ctx, 1); - sc_path_t path; - int id = key_info->key_reference; - { - sc_file_t *file = sc_file_new(); - if (file) ret = rutoken_check_df(card, PrKDFid); - else ret = SC_ERROR_OUT_OF_MEMORY; - if (ret == SC_SUCCESS) - { - /* create key file */ - sc_format_path(PRK_DF, &path); - file->type = SC_FILE_TYPE_WORKING_EF; - file->id = id; - file->size = prsize; - sc_file_set_sec_attr(file, (u8*)&pr_sec_attr, SEC_ATTR_SIZE); - ret = rutoken_create_file(card, &path, file); - } - if (file) sc_file_free(file); - } - return ret; -} - - -/* Store a private key object. */ -static int -rutoken_store_key(sc_profile_t *profile, sc_card_t *card, - sc_pkcs15_object_t *obj, - sc_pkcs15_prkey_t *key) -{ - SC_FUNC_CALLED(card->ctx, 1); - sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; - const int nKeyBufSize = 2048; - u8 *prkeybuf = NULL; - size_t prsize; - int ret; - if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) - return SC_ERROR_NOT_SUPPORTED; - - prkeybuf = calloc(nKeyBufSize, 1); - if(!prkeybuf) - return SC_ERROR_OUT_OF_MEMORY; - - /* encode private key - * create key file - * write a key */ - prsize = nKeyBufSize; - - if((ret = profile->ops->encode_private_key(profile, card, &key->u.rsa, prkeybuf, &prsize, 0)) == 0 && - ( ret = rutoken_create_prkeyfile(card, key_info, NULL, prsize)) == 0) - { - if((ret = sc_update_binary(card, 0, prkeybuf, prsize, 0)) == prsize) - { - fix_pkcs15(profile, card, P15_DF(obj->type), 0); - } - } - free(prkeybuf); - return ret; -} -static int -rutoken_encode_private_key(sc_profile_t *profile, sc_card_t *card, - struct sc_pkcs15_prkey_rsa *rsa, - u8 *key, size_t *keysize, int key_ref) -{ - PRIVATEKEYBLOB prkeyblob; - create_privite_blob(&prkeyblob, rsa); - int r = privite_blob_to_bin(&prkeyblob, key, keysize); - free_privite_blob(&prkeyblob); - return r; -} - - - -int rutoken_id_in(int id, const u8 *buf, int buflen) -{ - int i; - for (i = 0; i*2 < buflen; i++) - if (id == (int)buf[i*2] * 0x100 + buf[i*2 + 1]) return 1; - return 0; -} - -int rutoken_find_id(sc_card_t *card, const sc_path_t *path) -{ - int ret = SC_SUCCESS; - sc_file_t *file = NULL; - u8 *files = malloc(2048); - if (!files) return SC_ERROR_OUT_OF_MEMORY; - if(path) - { - if((ret = card->ops->select_file(card, path, &file)) == SC_SUCCESS) - ret = file->type == SC_FILE_TYPE_DF ? SC_SUCCESS : SC_ERROR_NOT_ALLOWED; - } - if(ret == SC_SUCCESS) - { - ret = card->ops->list_files(card, files, 2048); - if(ret >= 0) - { - int i; - for (i = 0; i < MAX_ID; i++) - if(!rutoken_id_in(i, files, ret)) {ret = i; break;} - } - } - free(files); - if(file)sc_file_free(file); - return ret; -} - -int rutoken_new_file(struct sc_profile *profile, struct sc_card *card, - unsigned int type, unsigned int idx, struct sc_file **file) -{ - SC_FUNC_CALLED(card->ctx, 1); - int ret = SC_SUCCESS, id; - sc_path_t path; - switch (type & SC_PKCS15_TYPE_CLASS_MASK) - { - case SC_PKCS15_TYPE_CERT: - - ret = rutoken_check_df(card, CDFid); - /* find first unlished file id */ - if (ret == SC_SUCCESS) ret = (id = rutoken_find_id(card, NULL)) >= 0 ? SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; - sc_format_path(C_DF, &path); - break; - case SC_PKCS15_TYPE_PUBKEY: - ret = rutoken_check_df(card, PuKDFid); - if (ret == SC_SUCCESS) ret = (id = rutoken_find_id(card, NULL)) >= 0 ? SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; - sc_format_path(PUK_DF, &path); - break; - case SC_PKCS15_TYPE_PRKEY_RSA: - default: - ret = SC_ERROR_NOT_SUPPORTED; - } - - if(ret == SC_SUCCESS) - { - *file = sc_file_new(); - (*file)->size = 0; - (*file)->id = id; - sc_append_file_id(&path, (*file)->id); - (*file)->path = path; - sc_file_set_sec_attr(*file, (u8*)&pb_sec_attr, SEC_ATTR_SIZE); - (*file)->type = SC_FILE_TYPE_WORKING_EF; - /* If target file exist than remove it */ - if (card->ops->select_file(card, &(*file)->path, NULL) == SC_SUCCESS) - { - sc_path_t del_path; - del_path.len = 0; - del_path.type = SC_PATH_TYPE_FILE_ID; - card->ops->delete_file(card, &del_path); - } - fix_pkcs15(profile, card, P15_DF(type), 0); - } - return ret; -} - -int rutoken_delete_object(struct sc_profile *profile, struct sc_card *card, - unsigned int type, const void *data, const sc_path_t *path) -{ - int ret = -1, tries_left = 2; - /* try to logon as user*/ - card->ops->logout(card); - - u8 *pin; - ret = card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_TRIES_LEFT, &tries_left); - while(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left > 0) - { - pin = (u8*)getpass("Please enter User PIN: "); - ret = sc_verify(card, SC_AC_CHV, 2, pin, 8, NULL); - if(ret != SC_SUCCESS) - { - tries_left = 2; - card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_TRIES_LEFT, &tries_left); - fprintf(stderr, "PIN code verification failed: %s\n%d tries left\n", sc_strerror(ret), tries_left); - } - } - if( (ret == SC_SUCCESS) && - (ret = card->ops->select_file(card, path, NULL) == SC_SUCCESS)) - { - sc_path_t del_path; - memset(&del_path, 0, sizeof(del_path)); - del_path.type = SC_PATH_TYPE_FILE_ID; - del_path.value[0] = path->value[path->len - 2]; - del_path.value[1] = path->value[path->len - 1]; - del_path.len = 2; - - ret = sc_delete_file(card, &del_path); - } - return ret; -} - - -/* -* Inicialization routine -*/ - -/* Complete initialization */ -int rutoken_check_sw(sc_card_t *, unsigned int, unsigned int); -int rutoken_finalize_card(sc_card_t *card) -{ - int ret = SC_ERROR_CARD_CMD_FAILED; - SC_FUNC_CALLED(card->ctx, 1); - sc_apdu_t apdu = {0}; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x7b, 0x00, 0x00); - apdu.cla = 0x80; - if(sc_transmit_apdu(card, &apdu) >= 0) - ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); - - SC_FUNC_RETURN(card->ctx, 1, ret); -} - -/* Try to delete pkcs15 structure */ -int rutoken_erase(struct sc_profile *profile, sc_card_t *card) -{ - int ret = SC_ERROR_CARD_CMD_FAILED; - SC_FUNC_CALLED(card->ctx, 1); - sc_apdu_t apdu = {0}; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x7a, 0x00, 0x00); - apdu.cla = 0x80; - if(sc_transmit_apdu(card, &apdu) >= 0) - ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); - - SC_FUNC_RETURN(card->ctx, 1, ret); - - return ret; -} - -/* create pkcs15 structure */ -int rutoken_init(sc_profile_t *profile, sc_card_t *card) -{ - return SC_ERROR_NOT_SUPPORTED; -} - - -static struct sc_pkcs15init_operations sc_pkcs15init_rutoken_operations = { - rutoken_erase, - rutoken_init, /* init_card */ - rutoken_create_dir, - NULL, /* create_domain */ - NULL/*rutoken_select_pin_reference*/, - NULL/*rutoken_create_pin*/, - rutoken_select_key_reference, - rutoken_create_key, - rutoken_store_key, - NULL, /* rutoken_generate_key, */ - rutoken_encode_private_key, - NULL, /* encode private/public key */ - rutoken_finalize_card, /* finalize_card */ - - NULL, NULL, NULL, - rutoken_new_file, - NULL, /* old style api */ - - rutoken_delete_object /* delete_object */ -}; - -struct sc_pkcs15init_operations * -sc_pkcs15init_get_rutoken_ops(void) -{ - return &sc_pkcs15init_rutoken_operations; -} - -static void set_string(char **strp, const char *value) -{ - if (*strp) free(*strp); - *strp = value ? strdup(value) : NULL; -} - -#define KEY_LABEL "GOST 28.147-89 KEY" -#define OBJ_LABEL "SE object" #define RUT_LABEL "ruToken card" - + +#define PrKDF_path "3F00FF000001" +#define PuKDF_path "3F00FF000002" +#define CDF_path "3F00FF000003" +#define DODF_path "3F00FF000004" +#define AODF_path "3F00FF00A0DF" + +static const struct +{ + char const* path; + unsigned int type; +} arr_profile_df[] = + { + { PrKDF_path, SC_PKCS15_PRKDF }, + { PuKDF_path, SC_PKCS15_PUKDF }, + { CDF_path, SC_PKCS15_CDF }, + { DODF_path, SC_PKCS15_DODF }, + { AODF_path, SC_PKCS15_AODF } + }; + static const struct { - int type, id, auth_id, min_length; - unsigned char reference; - const char *path; + int reference; const char *label; - int flags; + unsigned int flags; } pinlist[]= { - - {1, 2, 2, 1, 0x02, "3f0000000000", "User PIN", - SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_CASE_SENSITIVE /*| SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN*/}, - {0, 0, 0, 0, 0, NULL, NULL, 0} + { SC_RUTOKEN_DEF_ID_GCHV_USER, "User PIN", + SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + }, + { SC_RUTOKEN_DEF_ID_GCHV_ADMIN, "SO PIN", + SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_SO_PIN + } }; -int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); - -void add_predefined_pin(sc_pkcs15_card_t *p15card) +static int add_predefined_pin(sc_pkcs15_card_t *p15card, sc_path_t *adf_path) { - int i; - sc_path_t path; - sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) calloc(1, sizeof(*pin_info)); - sc_pkcs15_object_t *pin_obj = (sc_pkcs15_object_t *) calloc(1, sizeof(*pin_obj)); - for(i=0; pinlist[i].id; ++i) + size_t i; + sc_pkcs15_pin_info_t *pin_info; + sc_pkcs15_object_t *pin_obj; + + for (i = 0; i < sizeof(pinlist)/sizeof(pinlist[0]); ++i) { - sc_format_path(pinlist[i].path, &path); + pin_info = calloc(1, sizeof(*pin_info)); + pin_obj = calloc(1, sizeof(*pin_obj)); + if (!pin_info || !pin_obj) + { + free(pin_info); + free(pin_obj); + return SC_ERROR_OUT_OF_MEMORY; + } pin_info->auth_id.len = 1; - pin_info->auth_id.value[0] = 1; + pin_info->auth_id.value[0] = (u8)pinlist[i].reference; pin_info->reference = pinlist[i].reference; pin_info->flags = pinlist[i].flags; pin_info->type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; - pin_info->min_length = pinlist[i].min_length; + pin_info->min_length = 1; pin_info->stored_length = 16; pin_info->max_length = 16; pin_info->pad_char = -1; pin_info->tries_left = 1; - sc_format_path(pinlist[i].path, &pin_info->path); + pin_info->path = *adf_path; strncpy(pin_obj->label, pinlist[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE; @@ -788,15 +103,19 @@ void add_predefined_pin(sc_pkcs15_card_t *p15card) free(pin_obj); free(pin_info); } + return SC_SUCCESS; } -static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card) +static void set_string(char **strp, const char *value) +{ + if (*strp) free(*strp); + *strp = value ? strdup(value) : NULL; +} + +static int set_card_info(sc_pkcs15_card_t *p15card) { - int ret = SC_ERROR_WRONG_CARD; sc_card_t *card = p15card->card; sc_context_t *ctx = p15card->card->ctx; - sc_path_t path; - sc_file_t *odf; sc_serial_number_t serialnr; char serial[30] = {0}; u8 info[8]; @@ -804,76 +123,80 @@ static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card) /* get the card serial number */ if (sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr) < 0) { - sc_debug(ctx, "unable to get ICCSN"); - goto failed; + sc_debug(ctx, "Unable to get ICCSN\n"); + return SC_ERROR_WRONG_CARD; } sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0); set_string(&p15card->serial_number, serial); - /* ct_debug("serial_number = %s", serial); */ /* get ruToken information */ if (sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, info) < 0) { - sc_debug(ctx, "unable to get token information"); - goto failed; + sc_debug(ctx, "Unable to get token information\n"); + return SC_ERROR_WRONG_CARD; } set_string(&p15card->label, RUT_LABEL); p15card->version = (info[1] >> 4)*10 + (info[1] & 0x0f); sc_bin_to_hex(info + 3, 3 , serial, sizeof(serial), 0); set_string(&p15card->manufacturer_id, serial); - odf = sc_file_new(); - if(odf) - { - sc_format_path("3f0000000000dfff", &odf->path); - odf->id = 0xdfff; - odf->type = SC_FILE_TYPE_WORKING_EF; - odf->ef_structure = SC_FILE_EF_TRANSPARENT; - odf->size = 1024; - p15card->file_odf = odf; - } + return SC_SUCCESS; +} - add_predefined_pin(p15card); +static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card) +{ + sc_context_t *ctx; + sc_file_t *df; + sc_card_t *card; + sc_path_t path; + size_t i; + int r; + unsigned int added_pin = 0; - while (p15card->df_list) - sc_pkcs15_remove_df(p15card, p15card->df_list); + if (!p15card || !p15card->card || !p15card->card->ctx + || !p15card->card->ops + || !p15card->card->ops->select_file + ) + return SC_ERROR_INVALID_ARGUMENTS; + card = p15card->card; + ctx = card->ctx; - sc_file_t *df = sc_file_new(); - if (df) - { - df->id = DFsId; - df->size = 0; - df->type = SC_FILE_TYPE_WORKING_EF; - df->ef_structure = SC_FILE_EF_TRANSPARENT; - - - sc_format_path(PRK_DF, &path); - sc_append_file_id(&path, df->id); - if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + r = set_card_info(p15card); + if (r != SC_SUCCESS) { - df->path = path; - sc_pkcs15_add_df(p15card, SC_PKCS15_PRKDF, &path, df); + sc_error(ctx, "Unable to set card info: %s\n", sc_strerror(r)); + r = SC_SUCCESS; } - sc_format_path(PUK_DF, &path); - sc_append_file_id(&path, df->id); - if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + + for (i = 0; i < sizeof(arr_profile_df)/sizeof(arr_profile_df[0]); ++i) { - df->path = path; - sc_pkcs15_add_df(p15card, SC_PKCS15_PUKDF, &path, df); - } - sc_format_path(C_DF, &path); - sc_append_file_id(&path, df->id); - if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + df = NULL; + sc_format_path(arr_profile_df[i].path, &path); + if (card->ops->select_file(card, &path, &df) == SC_ERROR_FILE_NOT_FOUND) { - df->path = path; - sc_pkcs15_add_df(p15card, SC_PKCS15_CDF, &path, df); + sc_error(ctx, "File system mismatch\n"); + r = SC_ERROR_OBJECT_NOT_FOUND; } + if (r == SC_SUCCESS) + r = sc_pkcs15_add_df(p15card, arr_profile_df[i].type, &path, df); + if (df) sc_file_free(df); + + if (r != SC_SUCCESS) break; + + if (arr_profile_df[i].type == SC_PKCS15_AODF + && add_predefined_pin(p15card, &path) == SC_SUCCESS + ) + added_pin = 1; } - ret = SC_SUCCESS; -failed: - return ret; + if (!added_pin) + { + sc_debug(ctx, "Use formating token!\n"); + sc_format_path("", &path); + r = add_predefined_pin(p15card, &path); + } + return r; } int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card, diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index afdc5f42..3e86f9f3 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -2445,7 +2445,18 @@ static int pkcs15_dobj_get_value(struct sc_pkcs11_session *session, return rv; } +static CK_RV data_value_to_attr(CK_ATTRIBUTE_PTR attr, struct sc_pkcs15_data *data) +{ + if (!attr || !data) + return CKR_ATTRIBUTE_VALUE_INVALID; + sc_debug(context, "data %p\n", data); + sc_debug(context, "data_len %i\n", data->data_len); + + check_attribute_buffer(attr, data->data_len); + memcpy(attr->pValue, data->data, data->data_len); + return CKR_OK; +} static CK_RV pkcs15_dobj_get_attribute(struct sc_pkcs11_session *session, void *object, @@ -2503,16 +2514,14 @@ static CK_RV pkcs15_dobj_get_attribute(struct sc_pkcs11_session *session, struct sc_pkcs15_data *data = NULL; rv = pkcs15_dobj_get_value(session, dobj, &data); - if (rv!=CKR_OK) + if (rv == CKR_OK) + rv = data_value_to_attr(attr, data); + if (data) { + free(data->data); + free(data); + } + if (rv != CKR_OK) return rv; - else if (!data) - return CKR_ATTRIBUTE_VALUE_INVALID; - - sc_debug(context, "data %p\n", data); - sc_debug(context, "data_len %i\n", data->data_len); - check_attribute_buffer(attr, data->data_len); - memcpy(attr->pValue, data->data, data->data_len); - free(data); } break; default: diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index f04bb764..395af9a3 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -33,7 +33,7 @@ libpkcs15init_la_SOURCES = \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \ - pkcs15-muscle.c pkcs15-asepcos.c + pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ $(AM_LDFLAGS) diff --git a/src/pkcs15init/rutoken.profile b/src/pkcs15init/rutoken.profile index 7f369e05..9f4063d3 100644 --- a/src/pkcs15init/rutoken.profile +++ b/src/pkcs15init/rutoken.profile @@ -1,75 +1,88 @@ +# +# PKCS15 profile, generic information. +# This profile is loaded before any card specific profile. +# -cardinfo { - max-pin-length = 8; - pin-encoding = ascii-numeric; - pin-pad-char = 0x00; +# +# The following controls some aspects of the PKCS15 we put onto +# the card. +# +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; } -option default { + +# Default settings. +# This option block will always be processed. +option default_32k { macros { - so-pin-flags = initialized, needs-padding, soPin; - isf_acl = WRITE=$SOPIN; - df_acl = *=$SOPIN; + odf-size = 0; + aodf-size = 0; + dodf-size = 2048; + cdf-size = 2048; + prkdf-size = 2048; + pukdf-size = 2048; } } - -# Define reasonable limits for PINs and PUK -# We set the reference for SO pin+puk here, because -# those are hard-coded (if a PUK us assigned). -PIN so-pin { - reference = 0; +# This option is for cards with very little memory. +# It sets the size of various PKCS15 directory files +# to 128 or 256, respectively. +#option small { +option default { + macros { + odf-size = 0; + aodf-size = 0; + dodf-size = 512; + cdf-size = 512; + prkdf-size = 512; + pukdf-size = 512; + } } -PIN so-puk { - reference = 1; -} -PIN user-pin { - attempts = 15; -} -PIN user-puk { - attempts = 15; -} - -# Additional filesystem info. -# This is added to the file system info specified in the -# main profile. filesystem { DF MF { - DF { + path = 3F00; type = DF; - file-id = 0000; - acl = *=NONE; - DF { - type = DF; - file-id = 0000; - acl = *=NONE; - DF { + # Here comes the application DF + DF PKCS15-AppDF { type = DF; - file-id = 0000; - acl = *=NONE; + file-id = FF00; + + EF PKCS15-ODF { + file-id = 00DF; + size = $odf-size; } - DF { - type = DF; + EF PKCS15-AODF { + file-id = A0DF; + size = $aodf-size; + } + + EF PKCS15-PrKDF { file-id = 0001; - acl = *=NONE; + size = $prkdf-size; } - DF { - type = DF; + EF PKCS15-PuKDF { file-id = 0002; - acl = *=NONE; + size = $pukdf-size; } - } - DF { - type = DF; - file-id = 0001; - acl = *=NONE; + EF PKCS15-CDF { + file-id = 0003; + size = $cdf-size; } - + EF PKCS15-DODF { + file-id = 0004; + size = $dodf-size; + } } } } diff --git a/src/tools/rutoken-tool.c b/src/tools/rutoken-tool.c index dcb6387e..6aafc3e0 100644 --- a/src/tools/rutoken-tool.c +++ b/src/tools/rutoken-tool.c @@ -28,446 +28,505 @@ #include #endif #include -#include -#include #include -#include +#include +#include #include #include #include "util.h" -//#define _DEBUG -#ifdef _DEBUG -#define trace(fmt) printf("%s, %s line %d: " fmt, __FUNCTION__, __FILE__, __LINE__) -#define trace2(fmt,a) printf("%s, %s line %d: " fmt, __FUNCTION__, __FILE__, __LINE__, a) -#define trace2(fmt,a,b) printf("%s, %s line %d: " fmt, __FUNCTION__, __FILE__, __LINE__, a, b) -#else -#define trace(fmt) -#define trace2(fmt,a) -#define trace3(fmt,a,b) -#endif +#define IV_SIZE 8 +#define HASH_SIZE 4 - -/* globals */ -const char *app_name = "rutoken-tool"; -sc_context_t *g_ctx = NULL; +static const char *app_name = "rutoken-tool"; enum { OP_NONE, OP_GET_INFO, - OP_ENCIPHER, - OP_DECIPHER, - OP_SIGN, - OP_FORMAT + OP_GEN_KEY, + OP_ENCRYPT, + OP_DECRYPT, + OP_MAC }; -enum { - OPT_BASE = 0x100, - OPT_PIN, - OPT_SOPIN +static const struct option options[] = { + {"reader", 1, NULL, 'r'}, + {"wait", 0, NULL, 'w'}, + {"pin", 1, NULL, 'p'}, + {"key", 1, NULL, 'k'}, + {"IV", 1, NULL, 'I'}, + {"type", 1, NULL, 't'}, + {"input", 1, NULL, 'i'}, + {"output", 1, NULL, 'o'}, + {"info", 0, NULL, 's'}, + {"genkey", 0, NULL, 'g'}, + {"encrypt", 0, NULL, 'e'}, + {"decrypt", 0, NULL, 'd'}, + {"mac", 0, NULL, 'm'}, + {"verbose", 0, NULL, 'v'}, + {NULL, 0, NULL, 0 } }; -const struct option options[] = { - {"reader", 1, 0, 'r'}, - {"card-driver", 1, 0, 'c'}, - {"wait", 0, 0, 'w'}, - {"verbose", 0, 0, 'v'}, - {"getinfo", 0, 0, 'g'}, - {"encrypt", 0, 0, 'e'}, - {"decrypt", 0, 0, 'u'}, - {"sign", 0, 0, 's'}, - {"key", 1, 0, 'k'}, - {"i-vector",1, 0, 'I'}, - {"pin", 1, 0, OPT_PIN}, - {"so-pin", 1, 0, OPT_SOPIN}, - {"input", 1, 0, 'i'}, - {"output", 1, 0, 'o'}, - {"format", 0, 0, 'F'}, - {0, 0, 0, 0} -}; - -const char *option_help[] = { +static const char *option_help[] = { "Uses reader number [0]", - "Forces the use of driver [auto-detect]", "Wait for a card to be inserted", - "Verbose operation. Use several times to enable debug output.", - "Get RuToken info", - "GOST encrypt", - "GOST decrypt", - "sign", - "use GOST key", - "use initialization vector (synchro posylka)", - "user pin", - "admin pin", - "input file path", - "output file path", - "format card" + "Specify PIN", + "Selects the GOST key ID to use", + "Initialization vector of the encryption to use", + "Specify a new GOST key type: ECB (default), SM or CFB", + "Selects the input file to cipher", + "Selects the output file to cipher", + "Show ruToken information", + "Generate new GOST key", + "Performs GOST encryption operation", + "Performs GOST decryption operation", + "Performs MAC computation with GOST key", + "Verbose operation. Use several times to enable debug output." }; - /* Get ruToken device information */ -int rutoken_info(sc_card_t *card) + +static int rutoken_info(sc_card_t *card) { - trace("enter\n"); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - char szInfo[SC_MAX_APDU_BUFFER_SIZE*4]; + sc_serial_number_t serial; int r; - - r = card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, rbuf); + r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, rbuf); if (r) { - fprintf(stderr, "get info failed: %s\n", - sc_strerror(r)); - return 1; - } - sc_bin_to_hex(rbuf, 8, szInfo, sizeof(szInfo), 0); - - printf("Type: %d\n", *((char *)rbuf)); - printf("Version: %d.%d\n", (*((char *)rbuf+1))>>4, (*((char *)rbuf+1))&0x0F ); - printf("Memory: %d Kb\n", *((char *)rbuf+2)*8); - printf("Protocol version: %d\n", *((char *)rbuf+3)); - printf("Software version: %d\n", *((char *)rbuf+4)); - printf("Order: %d\n", *((char *)rbuf+5)); - - sc_serial_number_t serial; - r = card->ops->card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); - if (r) { - fprintf(stderr, "get serial failed: %s\n", - sc_strerror(r)); - return 1; - } - sc_bin_to_hex(serial.value, serial.len , szInfo, sizeof(szInfo), 0); - printf("Serial number : %s\n", szInfo); - return 0; -} - -/* Cryptografic routine */ - -/* Size of file */ - -off_t get_file_size(int fd) -{ - off_t cur_pos; - off_t file_size; - cur_pos = lseek(fd, 0, SEEK_CUR); - file_size = lseek(fd, 0, SEEK_END); - lseek(fd, cur_pos, SEEK_SET); - return file_size; -} - -/* Allocate buffer and read file, insert initialization vector if needIV - return buffer size */ - -int get_file(sc_card_t *card, const char *filepath, u8 **ppBuf, int needIV, u8 *IV) -{ - int file = open(filepath, O_RDONLY); - int ret = -1, size = -1; - - if(file > 0) - { - size = get_file_size(file); - trace2("size = %d\n", size); - if(size > 0) *ppBuf = realloc(*ppBuf, needIV ? size + 8 : size); - if(*ppBuf) - { - trace3("needIV %d, %p\n", needIV, IV); - if (needIV) - { - if (IV) - ret = memcpy(*ppBuf, IV, 8) != *ppBuf; - else - { - trace("get_challenge\n"); - ret = card->ops->get_challenge(card, *ppBuf, 8); - } - if (ret == SC_SUCCESS) - { - ret = read(file, *ppBuf + 8, size) + 8; - size += 8; - } - else - ret = -1; - } - else - ret = read(file, *ppBuf, size); - } - trace3("ret = %d, size = %d\n", ret, size); - if( ret != size) - { - printf("Read error!!!\n"); - free(*ppBuf); - ret = -1; - } - close(file); - } - else - printf("File %s not found\n", filepath); - return ret; -} - -/* Write buffer to a file - sync = NULL if not sync decrypt */ - -int write_file(const char *filepath, u8 *buff, size_t len) -{ - int file, r; - file = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); - if( file < 0 ) { - printf("File %s not found\n", filepath); + fprintf(stderr, "Error: Get info failed: %s\n", sc_strerror(r)); return -1; } - r = write(file, buff, len); - if( r < 0) { - printf("Write error!!!\n"); - return r; - }; - return r; + printf("Type: %d\n", rbuf[0]); + printf("Version: %d.%d\n", rbuf[1]>>4, rbuf[1] & 0x0F); + printf("Memory: %d Kb\n", rbuf[2]*8); + printf("Protocol version: %d\n", rbuf[3]); + printf("Software version: %d\n", rbuf[4]); + printf("Order: %d\n", rbuf[5]); + + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r) { + fprintf(stderr, "Error: Get serial failed: %s\n", sc_strerror(r)); + return -1; + } + printf("Serial number: "); + hex_dump(stdout, serial.value, serial.len, NULL); + putchar('\n'); + return 0; } - -/* Decrypt a buffer */ - -int rutoken_decipher(sc_card_t *card, u8 keyid, u8 *in, size_t inlen, u8 *out, size_t outlen, int oper) + +/* Cipher/Decipher a buffer on token (used GOST key chosen by ID) */ + +static int rutoken_cipher(sc_card_t *card, u8 keyid, + const u8 *in, size_t inlen, + u8 *out, size_t outlen, int oper) { - int r;/* - u8 buff[24] = {0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, - 0x4E, 0x4F, 0xEB, 0x69, 0x5B, 0xFF, 0x01, 0x20, 0xE1, 0xA9, 0x2D, 0xAE, 0x59, 0xD4, 0xD1, 0xCA}; - u8 outbuff[24] = {0};*/ + int r; struct sc_rutoken_decipherinfo inf = { in, inlen, out, outlen }; sc_security_env_t env; + int cmd = (oper == OP_ENCRYPT) ? + SC_CARDCTL_RUTOKEN_GOST_ENCIPHER : + SC_CARDCTL_RUTOKEN_GOST_DECIPHER; + + memset(&env, 0, sizeof(env)); env.key_ref[0] = keyid; env.key_ref_len = 1; env.algorithm = SC_ALGORITHM_GOST; - env.algorithm_flags = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM; env.operation = SC_SEC_OPERATION_DECIPHER; /* set security env */ - trace2("try to set SE key = %02X\n", keyid); - r = card->ops->set_security_env(card, &env, 0); + r = sc_set_security_env(card, &env, 0); if (r) { - fprintf(stderr, "decipher failed: %d : %s\n", - r, sc_strerror(r)); - return 1; - } - trace("set SE - ok\n"); - /* cipher */ - r = card->ops->card_ctl(card, oper, &inf); - if (r < 0) { - fprintf(stderr, "decipher failed: %s\n", + fprintf(stderr, "Error: Cipher failed (set security environment): %s\n", sc_strerror(r)); - return 1; + return -1; } - trace2("return %d\n", r); - return r; -} - -/* Decrypt a file */ - -int crypt_file(sc_card_t *card, u8 keyid, const char *szInFile, const char *szOutFile, int oper, u8* IV) -{ - int ret = SC_ERROR_CARD_CMD_FAILED; - int size = -1; - u8 *pBuf = NULL, *pOut = NULL; - - size = get_file(card, szInFile, &pBuf, oper == OP_ENCIPHER, IV); - trace3("size of %s is %d\n", szInFile, size); - if(size > 0) - { - pOut = malloc(size); - size = rutoken_decipher - (card, keyid, pBuf, size, pOut, size, - oper == OP_ENCIPHER ? SC_CARDCTL_RUTOKEN_GOST_ENCIPHER : SC_CARDCTL_RUTOKEN_GOST_DECIPHER); - if ((size > 0) && (write_file(szOutFile, pOut, size) == size)) ret = SC_SUCCESS; - free(pBuf); - free(pOut); + /* cipher */ + r = sc_card_ctl(card, cmd, &inf); + if (r) { + fprintf(stderr, "Error: Cipher failed: %s\n", sc_strerror(r)); + return -1; } - return ret; + return 0; } -/* external definitions */ -struct sc_profile_t; -extern int rutoken_erase(struct sc_profile_t *, sc_card_t *); -extern int rutoken_finalize_card(sc_card_t *); -extern int rutoken_init(struct sc_profile_t *, sc_card_t *); -/* Format and initialize file system */ -int format_card(sc_card_t *card) +/* Compute MAC a buffer on token (used GOST key chosen by ID) */ + +static int rutoken_mac(sc_card_t *card, u8 keyid, + const u8 *in, size_t inlen, + u8 *out, size_t outlen) { - int ret = SC_ERROR_CARD_CMD_FAILED; - trace("enter\n"); - if (( ret = (rutoken_erase(NULL, card)) == SC_SUCCESS) && - ( ret = (rutoken_init(NULL, card)) == SC_SUCCESS) - ) - ret = rutoken_finalize_card(card); - return ret; + int r; + sc_security_env_t env; + + memset(&env, 0, sizeof(env)); + env.key_ref[0] = keyid; + env.key_ref_len = 1; + env.algorithm = SC_ALGORITHM_GOST; + env.operation = SC_SEC_OPERATION_SIGN; + + /* set security env */ + r = sc_set_security_env(card, &env, 0); + if (r) { + fprintf(stderr, "Error: Computation signature (MAC) failed" + " (set security environment): %s\n", sc_strerror(r)); + return -1; + } + /* calculate hash */ + r = sc_compute_signature(card, in, inlen, out, outlen); + if (r) { + fprintf(stderr, "Error: Computation signature (MAC) failed: %s\n", + sc_strerror(r)); + return -1; + } + return 0; } -int main(int argc, char *const argv[]) +/* Encrypt/Decrupt infile to outfile */ + +static int do_crypt(sc_card_t *card, u8 keyid, + const char *path_infile, const char *path_outfile, + const u8 IV[IV_SIZE], int oper) { - int err = 0, r, c, long_optind = 0; - const char *opt_driver = NULL; - sc_context_param_t ctx_param; - int opt_reader = -1, opt_debug = 0, opt_wait = 0, opt_key = 0, opt_is_IV, - opt_is_pin = 0, opt_is_sopin = 0, opt_is_input = 0, opt_is_output = 0; - char opt_pin[100] = {0}, opt_input[PATH_MAX] = {0}, opt_output[PATH_MAX] = {0}, opt_IV[16] = {0}; - - int operation = 0; + int err; + int fd_in, fd_out; + struct stat st; + size_t insize, outsize, readsize; + u8 *inbuf = NULL, *outbuf = NULL, *p; + + fd_in = open(path_infile, O_RDONLY); + if (fd_in < 0) { + fprintf(stderr, "Error: Cannot open file '%s'\n", path_infile); + return -1; + } + err = fstat(fd_in, &st); + if (err || (oper == OP_DECRYPT && st.st_size < IV_SIZE)) { + fprintf(stderr, "Error: File '%s' is invalid\n", path_infile); + close(fd_in); + return -1; + } + insize = st.st_size; + if (oper == OP_ENCRYPT) + insize += IV_SIZE; + outsize = insize; + if (oper == OP_DECRYPT) /* !(stat.st_size < IV_SIZE) already true */ + outsize -= IV_SIZE; + + inbuf = malloc(insize); + outbuf = malloc(outsize); + if (!inbuf || !outbuf) { + fprintf(stderr, "Error: File '%s' is too big (allocate memory)\n", + path_infile); + err = -1; + } + if (err == 0) { + p = inbuf; + readsize = insize; + if (oper == OP_ENCRYPT) { + memcpy(inbuf, IV, IV_SIZE); /* Set IV in first bytes buf */ + /* insize >= IV_SIZE already true */ + p += IV_SIZE; + readsize -= IV_SIZE; + } + err = read(fd_in, p, readsize); + if (err < 0 || (size_t)err != readsize) { + fprintf(stderr, "Error: Read file '%s' failed\n", path_infile); + err = -1; + } + else + err = 0; + } + close(fd_in); + + if (err == 0) { + fd_out = open(path_outfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd_out < 0) { + fprintf(stderr, "Error: Cannot create file '%s'\n",path_outfile); + err = -1; + } + else { + err = rutoken_cipher(card, keyid, inbuf, insize, + outbuf, outsize, oper); + if (err == 0) { + err = write(fd_out, outbuf, outsize); + if (err < 0 || (size_t)err != outsize) { + fprintf(stderr,"Error: Write file '%s' failed\n", + path_outfile); + err = -1; + } + else + err = 0; + } + close(fd_out); + } + } + if (outbuf) + free(outbuf); + if (inbuf) + free(inbuf); + return err; +} + +/* Cipher/Decipher + (for cipher IV is parameters or random generated on token) */ + +static int gostchiper(sc_card_t *card, u8 keyid, + const char *path_infile, const char *path_outfile, + const char IV[IV_SIZE], int is_iv, int op_oper) +{ + int r; + u8 iv[IV_SIZE]; + + if (op_oper == OP_ENCRYPT) { + if (!is_iv) { + /* generated random on token */ + r = sc_get_challenge(card, iv, IV_SIZE); + if (r) { + fprintf(stderr, "Error: Generate IV" + " (get challenge) failed: %s\n", + sc_strerror(r)); + return -1; + } + } + else + memcpy(iv, IV, IV_SIZE); + } + return do_crypt(card, keyid, path_infile, path_outfile, iv, op_oper); +} + +/* Print MAC infile (used GOST key chosen by ID) */ + +static int gostmac(sc_card_t *card, u8 keyid, const char *path_infile) +{ + int err; + int fd; + struct stat st; + size_t insize; + u8 *inbuf = NULL; + u8 outbuf[HASH_SIZE]; + + fd = open(path_infile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error: Cannot open file '%s'\n", path_infile); + return -1; + } + err = fstat(fd, &st); + if (err) { + fprintf(stderr, "Error: File '%s' is invalid\n", path_infile); + close(fd); + return -1; + } + insize = st.st_size; + inbuf = malloc(insize); + if (!inbuf) { + fprintf(stderr, "Error: File '%s' is too big (allocate memory)\n", + path_infile); + err = -1; + } + if (err == 0) { + err = read(fd, inbuf, insize); + if (err < 0 || (size_t)err != insize) { + fprintf(stderr, "Error: Read file '%s' failed\n", path_infile); + err = -1; + } + else + err = rutoken_mac(card, keyid, inbuf, insize, + outbuf, sizeof(outbuf)); + } + if (err == 0) { + hex_dump(stdout, outbuf, sizeof(outbuf), NULL); + putchar('\n'); + } + if (inbuf) + free(inbuf); + close(fd); + return err; +} + +/* Generate GOST key on ruToken card */ + +static int generate_gostkey(sc_card_t *card, u8 keyid, u8 keyoptions) +{ + const sc_SecAttrV2_t gk_sec_attr = + {0x44, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 2}; + sc_DOHdrV2_t paramkey; + int r; + + memset(¶mkey, 0, sizeof(paramkey)); + paramkey.wDOBodyLen = SC_RUTOKEN_DEF_LEN_DO_GOST; + paramkey.OTID.byObjectType = SC_RUTOKEN_TYPE_KEY; + paramkey.OTID.byObjectID = keyid; + paramkey.OP.byObjectOptions = keyoptions; + + /* assert(sizeof(*gk_sec_attr)); */ + /* assert(sizeof(*paramkey.SA_V2)); */ + /* assert(sizeof(paramkey.SA_V2) == sizeof(gk_sec_attr)); */ + memcpy(paramkey.SA_V2, gk_sec_attr, sizeof(gk_sec_attr)); + + r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO, ¶mkey); + if (r) { + fprintf(stderr, "Error: Generate GOST key failed: %s\n", sc_strerror(r)); + return -1; + } + return 0; +} + +int main(int argc, char* argv[]) +{ + int opt_reader = -1; + int opt_wait = 0; + const char *opt_pin = NULL; + int opt_key = 0; + int opt_is_iv = 0; + u8 opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_PZ; + const char *opt_input = NULL; + const char *opt_output = NULL; + int opt_operation = OP_NONE; + int opt_debug = 0; + char IV[IV_SIZE]; + int err = 0; sc_context_t *ctx = NULL; + sc_context_param_t ctx_param; sc_card_t *card = NULL; + int c, long_optind, r, tries_left; - while (1) - { - c = getopt_long(argc, argv, "r:vc:wgeusk:i:o:p:I:F", options, - &long_optind); + while (1) { + c = getopt_long(argc, argv, "r:wp:k:I:t:i:o:sgedmv", + options, &long_optind); if (c == -1) break; switch (c) { - case 'h': case '?': print_usage_and_die(app_name, options, option_help); case 'r': opt_reader = atoi(optarg); break; - case 'v': - opt_debug++; - break; - case 'c': - opt_driver = optarg; - break; case 'w': opt_wait = 1; break; - case 'g': - operation = OP_GET_INFO; + case 'p': + opt_pin = optarg; break; case 'k': opt_key = atoi(optarg); - opt_key = (opt_key / 10) * 0x10 + opt_key % 10; + if (opt_key <= 0 || opt_key < SC_RUTOKEN_DO_ALL_MIN_ID + || opt_key > SC_RUTOKEN_DO_NOCHV_MAX_ID) { + fprintf(stderr, "Error: Key ID is invalid" + " (%d <= ID <= %d)\n", + SC_RUTOKEN_DO_ALL_MIN_ID > 0 ? + SC_RUTOKEN_DO_ALL_MIN_ID : 1, + SC_RUTOKEN_DO_NOCHV_MAX_ID); + return -1; + } break; case 'I': - opt_is_IV = 1; - strncpy(opt_IV, optarg, 8); + opt_is_iv = 1; + strncpy(IV, optarg, sizeof(IV)); break; - case 'u': - operation = OP_DECIPHER; - break; - case 'e': - operation = OP_ENCIPHER; - break; - case 's': - operation = OP_SIGN; - break; - case OPT_PIN: - if(opt_is_sopin || opt_is_pin) - { - fprintf(stderr, "You must specify only one pin\n"); - goto end; + case 't': + if (strcmp(optarg, "CFB") == 0) + opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMMOS; + else if (strcmp(optarg, "SM") == 0) + opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM; + else if (strcmp(optarg, "ECB") != 0) { + fprintf(stderr, "Error: Key type must be either" + " ECB, SM or CFB\n"); + return -1; } - opt_is_pin = 1; - strcpy(opt_pin, optarg); - break; - case OPT_SOPIN: - if(opt_is_sopin || opt_is_pin) - { - fprintf(stderr, "You must specify only one pin\n"); - goto end; - } - opt_is_sopin = 1; - strcpy(opt_pin, optarg); break; case 'i': - opt_is_input = 1; - strcpy(opt_input, optarg); + opt_input = optarg; break; case 'o': - opt_is_output = 1; - strcpy(opt_output, optarg); + opt_output = optarg; break; - case 'F': - operation = OP_FORMAT; + case 's': + opt_operation = OP_GET_INFO; + break; + case 'g': + opt_operation = OP_GEN_KEY; + break; + case 'e': + opt_operation = OP_ENCRYPT; + break; + case 'd': + opt_operation = OP_DECRYPT; + break; + case 'm': + opt_operation = OP_MAC; + break; + case 'v': + opt_debug++; break; } } - /* create sc_context_t object */ - trace("\n"); memset(&ctx_param, 0, sizeof(ctx_param)); - ctx_param.ver = 0; ctx_param.app_name = app_name; r = sc_context_create(&ctx, &ctx_param); if (r) { - fprintf(stderr, "Failed to establish context: %s\n", + fprintf(stderr, "Error: Failed to establish context: %s\n", sc_strerror(r)); - return 1; + return -1; } - if (opt_debug) ctx->debug = opt_debug; - if (opt_driver != NULL) { - err = sc_set_card_driver(ctx, opt_driver); - if (err) { - fprintf(stderr, "Driver '%s' not found!\n", - opt_driver); - err = -1; - goto end; - } - } - trace("\n"); - err = connect_card(ctx, &card, opt_reader, 0, opt_wait, opt_debug); - if (err) - goto end; + if (connect_card(ctx, &card, opt_reader, 0, opt_wait, opt_debug) != 0) + err = -1; - if(opt_is_pin || opt_is_sopin){ + if (err == 0 && opt_pin) { /* verify */ - int tries_left = 0; - err = sc_verify(card, SC_AC_CHV, opt_is_sopin ? 1 : 2 , (u8*)opt_pin, strlen(opt_pin), &tries_left); - if(err) - { - fprintf(stderr, "verify failed %d\n", err); - goto end; + r = sc_verify(card, SC_AC_CHV, SC_RUTOKEN_DEF_ID_GCHV_USER, + (u8*)opt_pin, strlen(opt_pin), &tries_left); + if (r) { + fprintf(stderr, "Error: PIN verification failed: %s", + sc_strerror(r)); + if (r == SC_ERROR_PIN_CODE_INCORRECT) + fprintf(stderr, " (tries left %d)\n", tries_left); + else + putc('\n', stderr); + err = 1; } - fprintf(stderr, "Verify ok\n"); } - switch(operation) - { + if (err == 0) { + err = -1; + switch (opt_operation) { case OP_GET_INFO: - if ((err = rutoken_info(card))) { - goto end; - } + err = rutoken_info(card); break; - case OP_DECIPHER: - case OP_ENCIPHER: - if(!opt_key) - { - fprintf(stderr, "Not key\n"); - err = -1; + case OP_DECRYPT: + case OP_ENCRYPT: + case OP_MAC: + if (!opt_input) { + fprintf(stderr, "Error: No input file specified\n"); break; } - if (!opt_is_input) - { - fprintf(stderr, "Not input file\n"); - err = -1; + if (opt_operation != OP_MAC && !opt_output) { + fprintf(stderr, "Error: No output file specified\n"); break; } - if (!opt_is_output) - { - fprintf(stderr, "Not output file\n"); - err = -1; + case OP_GEN_KEY: + if (opt_key == 0) { + fprintf(stderr, "Error: You must set key ID\n"); break; } - err = crypt_file(card, opt_key, opt_input, opt_output, operation, opt_is_IV ? (u8*)opt_IV : NULL); - break; - case OP_FORMAT: - trace("OP_FORMAT\n"); - err = format_card(card); - if(err != SC_SUCCESS) fprintf(stderr, "Initialization failed\n"); - + if (opt_operation == OP_GEN_KEY) + err = generate_gostkey(card, (u8)opt_key, opt_keytype); + else if (opt_operation == OP_MAC) + err = gostmac(card, (u8)opt_key, opt_input); + else + err = gostchiper(card, (u8)opt_key, opt_input,opt_output, + IV, opt_is_iv, opt_operation); break; default: - printf("No operation --help\n"); + fprintf(stderr, "Error: No operation specified\n"); break; } -end: + } if (card) { + /* sc_lock and sc_connect_card in connect_card */ sc_unlock(card); sc_disconnect_card(card, 0); } @@ -475,3 +534,4 @@ end: sc_release_context(ctx); return err; } +