| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | #ifdef HAVE_CONFIG_H |
|---|
| 23 | #include <config.h> |
|---|
| 24 | #endif |
|---|
| 25 | #if defined(HAVE_INTTYPES_H) |
|---|
| 26 | #include <inttypes.h> |
|---|
| 27 | #elif defined(HAVE_STDINT_H) |
|---|
| 28 | #include <stdint.h> |
|---|
| 29 | #elif defined(_MSC_VER) |
|---|
| 30 | typedef unsigned __int32 uint32_t; |
|---|
| 31 | typedef unsigned __int16 uint16_t; |
|---|
| 32 | typedef __int8 int8_t; |
|---|
| 33 | #else |
|---|
| 34 | #warning no uint32_t type available, please contact opensc-devel@opensc-project.org |
|---|
| 35 | #endif |
|---|
| 36 | #include <sys/types.h> |
|---|
| 37 | #include <ctype.h> |
|---|
| 38 | #include <string.h> |
|---|
| 39 | #include <stdlib.h> |
|---|
| 40 | #include <stdio.h> |
|---|
| 41 | #include "opensc.h" |
|---|
| 42 | #include "pkcs15.h" |
|---|
| 43 | #include "internal.h" |
|---|
| 44 | #include "cardctl.h" |
|---|
| 45 | |
|---|
| 46 | #ifdef ENABLE_OPENSSL |
|---|
| 47 | #include <openssl/evp.h> |
|---|
| 48 | #include <openssl/rsa.h> |
|---|
| 49 | #include <openssl/dsa.h> |
|---|
| 50 | #include <opensc/asn1.h> |
|---|
| 51 | #include <openssl/x509.h> |
|---|
| 52 | #include <openssl/err.h> |
|---|
| 53 | #endif |
|---|
| 54 | |
|---|
| 55 | #define FDESCR_DF 0x38 |
|---|
| 56 | #define FDESCR_EF 0x01 |
|---|
| 57 | #define ID_RESERVED_CURDF 0x3FFF |
|---|
| 58 | |
|---|
| 59 | struct auth_senv { |
|---|
| 60 | unsigned int algorithm; |
|---|
| 61 | }; |
|---|
| 62 | typedef struct auth_senv auth_senv_t; |
|---|
| 63 | |
|---|
| 64 | static const sc_SecAttrV2_t default_sec_attr = { |
|---|
| 65 | 0x42, |
|---|
| 66 | 0, 1, 0, 0, 0, 0, 1, |
|---|
| 67 | 0, 2, 0, 0, 0, 0, 2 |
|---|
| 68 | }; |
|---|
| 69 | |
|---|
| 70 | static struct sc_card_operations rutoken_ops; |
|---|
| 71 | |
|---|
| 72 | static struct sc_card_driver rutoken_drv = { |
|---|
| 73 | "Rutoken driver", |
|---|
| 74 | "rutoken", |
|---|
| 75 | &rutoken_ops, |
|---|
| 76 | NULL, 0, NULL |
|---|
| 77 | }; |
|---|
| 78 | |
|---|
| 79 | static struct sc_atr_table rutoken_atrs[] = { |
|---|
| 80 | { "3b:6f:00:ff:00:56:72:75:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, |
|---|
| 81 | { NULL, NULL, NULL, 0, 0, NULL } |
|---|
| 82 | }; |
|---|
| 83 | |
|---|
| 84 | static const char *hexdump(const void *data, size_t len) |
|---|
| 85 | { |
|---|
| 86 | static char string[1024]; |
|---|
| 87 | unsigned char *d = (unsigned char *)data; |
|---|
| 88 | unsigned int i, left; |
|---|
| 89 | |
|---|
| 90 | string[0] = '\0'; |
|---|
| 91 | left = sizeof(string); |
|---|
| 92 | for (i = 0; len--; i += 3) { |
|---|
| 93 | if (i >= sizeof(string) - 4) |
|---|
| 94 | break; |
|---|
| 95 | snprintf(string + i, 4, " %02x", *d++); |
|---|
| 96 | } |
|---|
| 97 | return string; |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | static int rutoken_finish(sc_card_t *card) |
|---|
| 101 | { |
|---|
| 102 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 103 | free(card->drv_data); |
|---|
| 104 | SC_FUNC_RETURN(card->ctx, 1, SC_SUCCESS); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | static int rutoken_match_card(sc_card_t *card) |
|---|
| 108 | { |
|---|
| 109 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 110 | if (_sc_match_atr(card, rutoken_atrs, &card->type) >= 0) |
|---|
| 111 | { |
|---|
| 112 | sc_debug(card->ctx, "ATR recognized as Rutoken\n"); |
|---|
| 113 | SC_FUNC_RETURN(card->ctx, 1, 1); |
|---|
| 114 | } |
|---|
| 115 | SC_FUNC_RETURN(card->ctx, 1, 0); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | static int token_init(sc_card_t *card, const char *card_name) |
|---|
| 119 | { |
|---|
| 120 | unsigned int flags; |
|---|
| 121 | sc_algorithm_info_t info; |
|---|
| 122 | |
|---|
| 123 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 124 | |
|---|
| 125 | card->name = card_name; |
|---|
| 126 | card->caps |= SC_CARD_CAP_RSA_2048 | SC_CARD_CAP_NO_FCI | SC_CARD_CAP_RNG; |
|---|
| 127 | card->drv_data = calloc(1, sizeof(auth_senv_t)); |
|---|
| 128 | if (card->drv_data == NULL) |
|---|
| 129 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_OUT_OF_MEMORY); |
|---|
| 130 | |
|---|
| 131 | flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; |
|---|
| 132 | _sc_card_add_rsa_alg(card, 256, flags, 0); |
|---|
| 133 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
|---|
| 134 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
|---|
| 135 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
|---|
| 136 | _sc_card_add_rsa_alg(card, 2048, flags, 0); |
|---|
| 137 | |
|---|
| 138 | memset(&info, 0, sizeof(info)); |
|---|
| 139 | info.algorithm = SC_ALGORITHM_GOST; |
|---|
| 140 | info.flags = SC_ALGORITHM_GOST_CRYPT_PZ | SC_ALGORITHM_GOST_CRYPT_GAMM |
|---|
| 141 | | SC_ALGORITHM_GOST_CRYPT_GAMMOS; |
|---|
| 142 | info.key_length = 32; |
|---|
| 143 | _sc_card_add_algorithm(card, &info); |
|---|
| 144 | |
|---|
| 145 | SC_FUNC_RETURN(card->ctx, 3, SC_SUCCESS); |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | static int rutoken_init(sc_card_t *card) |
|---|
| 149 | { |
|---|
| 150 | int ret; |
|---|
| 151 | |
|---|
| 152 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 153 | ret = token_init(card, "Rutoken card"); |
|---|
| 154 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | static const struct sc_card_error rutoken_errors[] = { |
|---|
| 158 | |
|---|
| 159 | { 0x6300, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 160 | { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. One tries left"}, |
|---|
| 161 | { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"}, |
|---|
| 162 | { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 163 | { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 164 | { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 165 | { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 166 | { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 167 | { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 168 | { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 169 | { 0x63CA, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 170 | { 0x63CB, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 171 | { 0x63CC, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 172 | { 0x63CD, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 173 | { 0x63CE, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 174 | { 0x63CF, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed"}, |
|---|
| 175 | |
|---|
| 176 | { 0x6400, SC_ERROR_CARD_CMD_FAILED, "Aborting"}, |
|---|
| 177 | |
|---|
| 178 | { 0x6500, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, |
|---|
| 179 | { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, |
|---|
| 180 | |
|---|
| 181 | { 0x6700, SC_ERROR_WRONG_LENGTH, "Lc or Le invalid"}, |
|---|
| 182 | |
|---|
| 183 | { 0x6883, SC_ERROR_CARD_CMD_FAILED, "The finishing command of a chain is expected"}, |
|---|
| 184 | |
|---|
| 185 | { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Required access right not granted"}, |
|---|
| 186 | { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "DO blocked"}, |
|---|
| 187 | { 0x6985, SC_ERROR_CARD_CMD_FAILED, "Command not allowed (unsuitable conditions)"}, |
|---|
| 188 | { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"No current EF selected"}, |
|---|
| 189 | |
|---|
| 190 | { 0x6A80, SC_ERROR_INCORRECT_PARAMETERS,"Invalid parameters in data field"}, |
|---|
| 191 | { 0x6A81, SC_ERROR_NOT_SUPPORTED, "Function/mode not supported"}, |
|---|
| 192 | { 0x6A82, SC_ERROR_FILE_NOT_FOUND, "File (DO) not found"}, |
|---|
| 193 | { 0x6A84, SC_ERROR_CARD_CMD_FAILED, "Not enough memory space in the token"}, |
|---|
| 194 | { 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"P1 or P2 invalid"}, |
|---|
| 195 | { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File (DO) already exists"}, |
|---|
| 196 | |
|---|
| 197 | { 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Out of maximum file length"}, |
|---|
| 198 | |
|---|
| 199 | { 0x6C00, SC_ERROR_WRONG_LENGTH, "Le does not fit the data to be sent"}, |
|---|
| 200 | |
|---|
| 201 | { 0x6D00, SC_ERROR_INS_NOT_SUPPORTED, "Ins invalid (not supported)"}, |
|---|
| 202 | |
|---|
| 203 | |
|---|
| 204 | { 0x6F01, SC_ERROR_CARD_CMD_FAILED, "Rutoken has the exchange protocol which is not supported by the USB-driver (newer, than in the driver)"}, |
|---|
| 205 | { 0x6F83, SC_ERROR_CARD_CMD_FAILED, "Infringement of the exchange protocol with Rutoken is revealed"}, |
|---|
| 206 | { 0x6F84, SC_ERROR_CARD_CMD_FAILED, "Rutoken is busy by processing of other command"}, |
|---|
| 207 | { 0x6F85, SC_ERROR_CARD_CMD_FAILED, "In the current folder the maximum quantity of file system objects is already created"}, |
|---|
| 208 | { 0x6F86, SC_ERROR_CARD_CMD_FAILED, "Invalid access right. Already login"}, |
|---|
| 209 | |
|---|
| 210 | { 0x9000, SC_NO_ERROR, NULL} |
|---|
| 211 | }; |
|---|
| 212 | |
|---|
| 213 | static int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) |
|---|
| 214 | { |
|---|
| 215 | size_t i; |
|---|
| 216 | |
|---|
| 217 | for (i = 0; i < sizeof(rutoken_errors)/sizeof(rutoken_errors[0]); ++i) { |
|---|
| 218 | if (rutoken_errors[i].SWs == ((sw1 << 8) | sw2)) { |
|---|
| 219 | if ( rutoken_errors[i].errorstr ) |
|---|
| 220 | sc_error(card->ctx, "%s\n", rutoken_errors[i].errorstr); |
|---|
| 221 | sc_debug(card->ctx, "sw1 = %x, sw2 = %x", sw1, sw2); |
|---|
| 222 | return rutoken_errors[i].errorno; |
|---|
| 223 | } |
|---|
| 224 | } |
|---|
| 225 | sc_error(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); |
|---|
| 226 | return SC_ERROR_CARD_CMD_FAILED; |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | static int rutoken_dir_up(sc_card_t *card) |
|---|
| 230 | { |
|---|
| 231 | u8 rbuf[256]; |
|---|
| 232 | sc_apdu_t apdu; |
|---|
| 233 | int ret; |
|---|
| 234 | |
|---|
| 235 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 236 | |
|---|
| 237 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0x00); |
|---|
| 238 | apdu.cla = 0x00; |
|---|
| 239 | apdu.resplen = sizeof(rbuf); |
|---|
| 240 | apdu.resp = rbuf; |
|---|
| 241 | apdu.le = sizeof(rbuf); |
|---|
| 242 | |
|---|
| 243 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 244 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 245 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 246 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 247 | } |
|---|
| 248 | |
|---|
| 249 | static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) |
|---|
| 250 | { |
|---|
| 251 | u8 rbuf[256]; |
|---|
| 252 | u8 previd[2]; |
|---|
| 253 | sc_apdu_t apdu; |
|---|
| 254 | size_t len = 0; |
|---|
| 255 | int ret, first = 1; |
|---|
| 256 | |
|---|
| 257 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 258 | while (1) |
|---|
| 259 | { |
|---|
| 260 | if (first) |
|---|
| 261 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00); |
|---|
| 262 | else |
|---|
| 263 | { |
|---|
| 264 | |
|---|
| 265 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x02); |
|---|
| 266 | apdu.lc = sizeof(previd); |
|---|
| 267 | apdu.data = previd; |
|---|
| 268 | apdu.datalen = sizeof(previd); |
|---|
| 269 | } |
|---|
| 270 | apdu.cla = 0x00; |
|---|
| 271 | apdu.resplen = sizeof(rbuf); |
|---|
| 272 | apdu.resp = rbuf; |
|---|
| 273 | apdu.le = sizeof(rbuf); |
|---|
| 274 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 275 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 276 | |
|---|
| 277 | if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) |
|---|
| 278 | break; |
|---|
| 279 | |
|---|
| 280 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 281 | SC_TEST_RET(card->ctx, ret, "Get list files failed"); |
|---|
| 282 | |
|---|
| 283 | |
|---|
| 284 | if (len + 2 <= buflen) |
|---|
| 285 | { |
|---|
| 286 | buf[len++] = rbuf[6]; |
|---|
| 287 | buf[len++] = rbuf[7]; |
|---|
| 288 | } |
|---|
| 289 | memcpy(previd, rbuf+6, sizeof(previd)); |
|---|
| 290 | if (rbuf[4] == FDESCR_DF) |
|---|
| 291 | rutoken_dir_up(card); |
|---|
| 292 | first = 0; |
|---|
| 293 | } |
|---|
| 294 | SC_FUNC_RETURN(card->ctx, 1, len); |
|---|
| 295 | } |
|---|
| 296 | |
|---|
| 297 | static void rutoken_process_fcp(sc_card_t *card, u8 *pIn, sc_file_t *file) |
|---|
| 298 | { |
|---|
| 299 | file->size = pIn[3] + ((uint16_t)pIn[2])*256; |
|---|
| 300 | file->id = pIn[7] + ((uint16_t)pIn[6])*256; |
|---|
| 301 | |
|---|
| 302 | if (pIn[4] == FDESCR_DF) |
|---|
| 303 | { |
|---|
| 304 | file->type = SC_FILE_TYPE_DF; |
|---|
| 305 | } |
|---|
| 306 | else |
|---|
| 307 | { |
|---|
| 308 | file->type = SC_FILE_TYPE_WORKING_EF; |
|---|
| 309 | file->ef_structure = SC_FILE_EF_TRANSPARENT; |
|---|
| 310 | } |
|---|
| 311 | sc_file_set_sec_attr(file, pIn + 17, SEC_ATTR_SIZE); |
|---|
| 312 | |
|---|
| 313 | if (file->sec_attr && file->sec_attr_len == SEC_ATTR_SIZE |
|---|
| 314 | #ifndef SET_ACL_FOR_EF_LENLESS8_RUTOKEN |
|---|
| 315 | |
|---|
| 316 | |
|---|
| 317 | |
|---|
| 318 | |
|---|
| 319 | |
|---|
| 320 | |
|---|
| 321 | |
|---|
| 322 | |
|---|
| 323 | && (file->type != SC_FILE_TYPE_WORKING_EF || file->path.len >= 8) |
|---|
| 324 | #endif |
|---|
| 325 | ) |
|---|
| 326 | { |
|---|
| 327 | sc_file_add_acl_entry(file, SC_AC_OP_SELECT, |
|---|
| 328 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 329 | if (file->sec_attr[0] & 0x40) |
|---|
| 330 | { |
|---|
| 331 | sc_debug(card->ctx, "SC_AC_OP_DELETE %i %i", |
|---|
| 332 | (int)(*(int8_t*)&file->sec_attr[1 +6]), |
|---|
| 333 | file->sec_attr[1+7 +6]); |
|---|
| 334 | sc_file_add_acl_entry(file, SC_AC_OP_DELETE, |
|---|
| 335 | (int)(*(int8_t*)&file->sec_attr[1 +6]), |
|---|
| 336 | file->sec_attr[1+7 +6]); |
|---|
| 337 | } |
|---|
| 338 | if (file->sec_attr[0] & 0x01) |
|---|
| 339 | { |
|---|
| 340 | sc_debug(card->ctx, (file->type == SC_FILE_TYPE_DF) ? |
|---|
| 341 | "SC_AC_OP_CREATE %i %i" : "SC_AC_OP_READ %i %i", |
|---|
| 342 | (int)(*(int8_t*)&file->sec_attr[1 +0]), |
|---|
| 343 | file->sec_attr[1+7 +0]); |
|---|
| 344 | sc_file_add_acl_entry(file, |
|---|
| 345 | (file->type == SC_FILE_TYPE_DF) ? |
|---|
| 346 | SC_AC_OP_CREATE : SC_AC_OP_READ, |
|---|
| 347 | (int)(*(int8_t*)&file->sec_attr[1 +0]), |
|---|
| 348 | file->sec_attr[1+7 +0]); |
|---|
| 349 | } |
|---|
| 350 | if (file->type == SC_FILE_TYPE_DF) |
|---|
| 351 | { |
|---|
| 352 | sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, |
|---|
| 353 | SC_AC_NONE, SC_AC_KEY_REF_NONE); |
|---|
| 354 | } |
|---|
| 355 | else |
|---|
| 356 | if (file->sec_attr[0] & 0x02) |
|---|
| 357 | { |
|---|
| 358 | sc_debug(card->ctx, "SC_AC_OP_UPDATE %i %i", |
|---|
| 359 | (int)(*(int8_t*)&file->sec_attr[1 +1]), |
|---|
| 360 | file->sec_attr[1+7 +1]); |
|---|
| 361 | sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, |
|---|
| 362 | (int)(*(int8_t*)&file->sec_attr[1 +1]), |
|---|
| 363 | file->sec_attr[1+7 +1]); |
|---|
| 364 | sc_debug(card->ctx, "SC_AC_OP_WRITE %i %i", |
|---|
| 365 | (int)(*(int8_t*)&file->sec_attr[1 +1]), |
|---|
| 366 | file->sec_attr[1+7 +1]); |
|---|
| 367 | sc_file_add_acl_entry(file, SC_AC_OP_WRITE, |
|---|
| 368 | (int)(*(int8_t*)&file->sec_attr[1 +1]), |
|---|
| 369 | file->sec_attr[1+7 +1]); |
|---|
| 370 | } |
|---|
| 371 | } |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | static int rutoken_select_file(sc_card_t *card, |
|---|
| 375 | const sc_path_t *in_path, sc_file_t **file) |
|---|
| 376 | { |
|---|
| 377 | int ret; |
|---|
| 378 | u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; |
|---|
| 379 | int pathlen; |
|---|
| 380 | u8 buf[SC_MAX_APDU_BUFFER_SIZE]; |
|---|
| 381 | sc_apdu_t apdu; |
|---|
| 382 | |
|---|
| 383 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 384 | if (!in_path || in_path->len < 2) |
|---|
| 385 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 386 | |
|---|
| 387 | pathlen = in_path->len; |
|---|
| 388 | memcpy(path, in_path->value, pathlen); |
|---|
| 389 | |
|---|
| 390 | sc_debug(card->ctx, "\n\tpath = %s\n\ttype = %d", |
|---|
| 391 | hexdump(path, pathlen), in_path->type); |
|---|
| 392 | |
|---|
| 393 | ret = SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 394 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); |
|---|
| 395 | switch (in_path->type) |
|---|
| 396 | { |
|---|
| 397 | case SC_PATH_TYPE_FILE_ID: |
|---|
| 398 | if (pathlen == 2) |
|---|
| 399 | { |
|---|
| 400 | apdu.p1 = 2; |
|---|
| 401 | ret = SC_SUCCESS; |
|---|
| 402 | } |
|---|
| 403 | break; |
|---|
| 404 | case SC_PATH_TYPE_PATH: |
|---|
| 405 | apdu.p1 = 8; |
|---|
| 406 | if (path[0] == 0x3F && path[1] == 0x00) |
|---|
| 407 | { |
|---|
| 408 | if (pathlen == 2) |
|---|
| 409 | { |
|---|
| 410 | apdu.p1 = 0; |
|---|
| 411 | } |
|---|
| 412 | else |
|---|
| 413 | { |
|---|
| 414 | path += 2; |
|---|
| 415 | pathlen -= 2; |
|---|
| 416 | } |
|---|
| 417 | } |
|---|
| 418 | ret = SC_SUCCESS; |
|---|
| 419 | break; |
|---|
| 420 | default: |
|---|
| 421 | ret = SC_ERROR_NOT_SUPPORTED; |
|---|
| 422 | break; |
|---|
| 423 | } |
|---|
| 424 | if (ret == SC_SUCCESS) |
|---|
| 425 | { |
|---|
| 426 | apdu.lc = pathlen; |
|---|
| 427 | apdu.data = path; |
|---|
| 428 | apdu.datalen = pathlen; |
|---|
| 429 | |
|---|
| 430 | if (file != NULL) { |
|---|
| 431 | apdu.resp = buf; |
|---|
| 432 | apdu.resplen = sizeof(buf); |
|---|
| 433 | apdu.le = 256; |
|---|
| 434 | } else { |
|---|
| 435 | apdu.resplen = 0; |
|---|
| 436 | apdu.le = 0; |
|---|
| 437 | apdu.cse = SC_APDU_CASE_3_SHORT; |
|---|
| 438 | } |
|---|
| 439 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 440 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 441 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 442 | |
|---|
| 443 | if (file == NULL) |
|---|
| 444 | { |
|---|
| 445 | |
|---|
| 446 | if (apdu.sw1 == 0x61) |
|---|
| 447 | SC_FUNC_RETURN(card->ctx, 2, SC_NO_ERROR); |
|---|
| 448 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 449 | } |
|---|
| 450 | |
|---|
| 451 | if ((ret == SC_SUCCESS) && (apdu.resplen == 32)) |
|---|
| 452 | { |
|---|
| 453 | *file = sc_file_new(); |
|---|
| 454 | if (*file == NULL) |
|---|
| 455 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY); |
|---|
| 456 | (*file)->path = *in_path; |
|---|
| 457 | |
|---|
| 458 | |
|---|
| 459 | rutoken_process_fcp(card, buf, *file); |
|---|
| 460 | |
|---|
| 461 | sc_debug(card->ctx, |
|---|
| 462 | "nfile ID = %04X, path = %s, type = %02X, len = %d", |
|---|
| 463 | (*file)->id, hexdump((*file)->path.value, (*file)->path.len), |
|---|
| 464 | (*file)->type, (*file)->size); |
|---|
| 465 | sc_debug(card->ctx, "sec attr = %s", |
|---|
| 466 | hexdump((*file)->sec_attr, (*file)->sec_attr_len)); |
|---|
| 467 | } |
|---|
| 468 | } |
|---|
| 469 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 470 | } |
|---|
| 471 | |
|---|
| 472 | static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, u8 *out) |
|---|
| 473 | { |
|---|
| 474 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 475 | |
|---|
| 476 | if ((!file) || (file->id == ID_RESERVED_CURDF)) |
|---|
| 477 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 478 | memset(out, 0, 32); |
|---|
| 479 | switch (file->type) |
|---|
| 480 | { |
|---|
| 481 | case SC_FILE_TYPE_DF: |
|---|
| 482 | out[4] = 0x38; |
|---|
| 483 | out[0] = file->size / 256; |
|---|
| 484 | out[1] = file->size % 256; |
|---|
| 485 | break; |
|---|
| 486 | case SC_FILE_TYPE_WORKING_EF: |
|---|
| 487 | out[4] = 0x01; |
|---|
| 488 | |
|---|
| 489 | out[2] = file->size / 256; |
|---|
| 490 | out[3] = file->size % 256; |
|---|
| 491 | break; |
|---|
| 492 | case SC_FILE_TYPE_INTERNAL_EF: |
|---|
| 493 | default: |
|---|
| 494 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_NOT_SUPPORTED); |
|---|
| 495 | } |
|---|
| 496 | |
|---|
| 497 | out[6] = file->id / 256; |
|---|
| 498 | out[7] = file->id % 256; |
|---|
| 499 | |
|---|
| 500 | |
|---|
| 501 | if (file->sec_attr_len == SEC_ATTR_SIZE) |
|---|
| 502 | memcpy(out + 17, file->sec_attr, SEC_ATTR_SIZE); |
|---|
| 503 | else |
|---|
| 504 | memcpy(out + 17, &default_sec_attr, SEC_ATTR_SIZE); |
|---|
| 505 | |
|---|
| 506 | SC_FUNC_RETURN(card->ctx, 3, SC_NO_ERROR); |
|---|
| 507 | } |
|---|
| 508 | |
|---|
| 509 | static int rutoken_create_file(sc_card_t *card, sc_file_t *file) |
|---|
| 510 | { |
|---|
| 511 | int ret; |
|---|
| 512 | sc_apdu_t apdu; |
|---|
| 513 | u8 sbuf[32] = { 0 }; |
|---|
| 514 | |
|---|
| 515 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 516 | ret = rutoken_construct_fcp(card, file, sbuf); |
|---|
| 517 | if (ret == SC_NO_ERROR) |
|---|
| 518 | { |
|---|
| 519 | sc_debug(card->ctx, "fcp = %s", hexdump(sbuf, 32)); |
|---|
| 520 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); |
|---|
| 521 | apdu.data = sbuf; |
|---|
| 522 | apdu.datalen = 32; |
|---|
| 523 | apdu.lc = 32; |
|---|
| 524 | |
|---|
| 525 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 526 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 527 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 528 | } |
|---|
| 529 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) |
|---|
| 533 | { |
|---|
| 534 | u8 sbuf[2]; |
|---|
| 535 | sc_apdu_t apdu; |
|---|
| 536 | |
|---|
| 537 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 538 | if (!path || path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) |
|---|
| 539 | { |
|---|
| 540 | sc_error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); |
|---|
| 541 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 542 | } |
|---|
| 543 | if (path->len == sizeof(sbuf)) |
|---|
| 544 | { |
|---|
| 545 | sbuf[0] = path->value[0]; |
|---|
| 546 | sbuf[1] = path->value[1]; |
|---|
| 547 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); |
|---|
| 548 | apdu.lc = sizeof(sbuf); |
|---|
| 549 | apdu.datalen = sizeof(sbuf); |
|---|
| 550 | apdu.data = sbuf; |
|---|
| 551 | } |
|---|
| 552 | else |
|---|
| 553 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); |
|---|
| 554 | SC_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); |
|---|
| 555 | SC_FUNC_RETURN(card->ctx, 1, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
|---|
| 556 | } |
|---|
| 557 | |
|---|
| 558 | static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, |
|---|
| 559 | const u8 *data, size_t data_len, int *tries_left) |
|---|
| 560 | { |
|---|
| 561 | sc_apdu_t apdu; |
|---|
| 562 | int ret; |
|---|
| 563 | |
|---|
| 564 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 565 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); |
|---|
| 566 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 567 | if (ret == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) |
|---|
| 568 | { |
|---|
| 569 | |
|---|
| 570 | |
|---|
| 571 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); |
|---|
| 572 | apdu.cla = 0x80; |
|---|
| 573 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 574 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 575 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 576 | SC_TEST_RET(card->ctx, ret, "Reset access rights failed"); |
|---|
| 577 | } |
|---|
| 578 | |
|---|
| 579 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, ref_qualifier); |
|---|
| 580 | apdu.lc = data_len; |
|---|
| 581 | apdu.datalen = data_len; |
|---|
| 582 | apdu.data = data; |
|---|
| 583 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 584 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 585 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 586 | if (ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) |
|---|
| 587 | { |
|---|
| 588 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); |
|---|
| 589 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 590 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 591 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 592 | if (ret == SC_ERROR_PIN_CODE_INCORRECT) |
|---|
| 593 | *tries_left = (int)(apdu.sw2 & 0x0f); |
|---|
| 594 | } |
|---|
| 595 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | static int rutoken_logout(sc_card_t *card) |
|---|
| 599 | { |
|---|
| 600 | sc_apdu_t apdu; |
|---|
| 601 | sc_path_t path; |
|---|
| 602 | int ret; |
|---|
| 603 | |
|---|
| 604 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 605 | sc_format_path("3F00", &path); |
|---|
| 606 | ret = rutoken_select_file(card, &path, NULL); |
|---|
| 607 | SC_TEST_RET(card->ctx, ret, "Select MF failed"); |
|---|
| 608 | |
|---|
| 609 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); |
|---|
| 610 | apdu.cla = 0x80; |
|---|
| 611 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 612 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 613 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 614 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 615 | } |
|---|
| 616 | |
|---|
| 617 | static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, |
|---|
| 618 | int ref_qualifier, const u8 *old, size_t oldlen, |
|---|
| 619 | const u8 *newref, size_t newlen, int *tries_left) |
|---|
| 620 | { |
|---|
| 621 | sc_apdu_t apdu; |
|---|
| 622 | int ret; |
|---|
| 623 | |
|---|
| 624 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 625 | if (old && oldlen) |
|---|
| 626 | { |
|---|
| 627 | ret = rutoken_verify(card, type, ref_qualifier, old, oldlen, tries_left); |
|---|
| 628 | SC_TEST_RET(card->ctx, ret, "Invalid 'old' pass"); |
|---|
| 629 | } |
|---|
| 630 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); |
|---|
| 631 | apdu.lc = newlen; |
|---|
| 632 | apdu.datalen = newlen; |
|---|
| 633 | apdu.data = newref; |
|---|
| 634 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 635 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 636 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 637 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 638 | } |
|---|
| 639 | |
|---|
| 640 | static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, |
|---|
| 641 | int ref_qualifier, const u8 *puk, size_t puklen, |
|---|
| 642 | const u8 *newref, size_t newlen) |
|---|
| 643 | { |
|---|
| 644 | #ifdef FORCE_VERIFY_RUTOKEN |
|---|
| 645 | int left; |
|---|
| 646 | #endif |
|---|
| 647 | sc_apdu_t apdu; |
|---|
| 648 | int ret; |
|---|
| 649 | |
|---|
| 650 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 651 | #ifdef FORCE_VERIFY_RUTOKEN |
|---|
| 652 | if (puk && puklen) |
|---|
| 653 | { |
|---|
| 654 | ret = rutoken_verify(card, type, ref_qualifier, puk, puklen, &left); |
|---|
| 655 | sc_error(card->ctx, "Tries left: %i\n", left); |
|---|
| 656 | SC_TEST_RET(card->ctx, ret, "Invalid 'puk' pass"); |
|---|
| 657 | } |
|---|
| 658 | #endif |
|---|
| 659 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier); |
|---|
| 660 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 661 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 662 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 663 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 664 | } |
|---|
| 665 | |
|---|
| 666 | static int rutoken_restore_security_env(sc_card_t *card, int se_num) |
|---|
| 667 | { |
|---|
| 668 | sc_apdu_t apdu; |
|---|
| 669 | int ret; |
|---|
| 670 | |
|---|
| 671 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 672 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 3, se_num); |
|---|
| 673 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 674 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 675 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 676 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 677 | } |
|---|
| 678 | |
|---|
| 679 | static int rutoken_set_security_env(sc_card_t *card, |
|---|
| 680 | const sc_security_env_t *env, |
|---|
| 681 | int se_num) |
|---|
| 682 | { |
|---|
| 683 | sc_apdu_t apdu; |
|---|
| 684 | auth_senv_t *senv; |
|---|
| 685 | u8 data[3] = { 0x83, 0x01 }; |
|---|
| 686 | int ret; |
|---|
| 687 | |
|---|
| 688 | SC_FUNC_CALLED(card->ctx, 1); |
|---|
| 689 | if (!env) |
|---|
| 690 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 691 | senv = (auth_senv_t*)card->drv_data; |
|---|
| 692 | if (!senv) |
|---|
| 693 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL); |
|---|
| 694 | if (env->algorithm == SC_ALGORITHM_RSA) |
|---|
| 695 | { |
|---|
| 696 | senv->algorithm = SC_ALGORITHM_RSA_RAW; |
|---|
| 697 | SC_FUNC_RETURN(card->ctx, 1, SC_SUCCESS); |
|---|
| 698 | } |
|---|
| 699 | |
|---|
| 700 | senv->algorithm = SC_ALGORITHM_GOST; |
|---|
| 701 | if (env->key_ref_len != 1) |
|---|
| 702 | { |
|---|
| 703 | sc_error(card->ctx, "No or invalid key reference\n"); |
|---|
| 704 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 705 | } |
|---|
| 706 | data[2] = env->key_ref[0]; |
|---|
| 707 | |
|---|
| 708 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 1, 0); |
|---|
| 709 | apdu.lc = apdu.datalen = sizeof(data); |
|---|
| 710 | apdu.data = data; |
|---|
| 711 | switch (env->operation) |
|---|
| 712 | { |
|---|
| 713 | case SC_SEC_OPERATION_AUTHENTICATE: |
|---|
| 714 | apdu.p2 = 0xA4; |
|---|
| 715 | break; |
|---|
| 716 | case SC_SEC_OPERATION_DECIPHER: |
|---|
| 717 | apdu.p2 = 0xB8; |
|---|
| 718 | break; |
|---|
| 719 | case SC_SEC_OPERATION_SIGN: |
|---|
| 720 | apdu.p2 = 0xAA; |
|---|
| 721 | break; |
|---|
| 722 | default: |
|---|
| 723 | SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 724 | } |
|---|
| 725 | |
|---|
| 726 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 727 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 728 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 729 | SC_FUNC_RETURN(card->ctx, 1, ret); |
|---|
| 730 | } |
|---|
| 731 | |
|---|
| 732 | static void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) |
|---|
| 733 | { |
|---|
| 734 | if (data) |
|---|
| 735 | { |
|---|
| 736 | data[0] = (u8)(pHdr->wDOBodyLen / 0x100); |
|---|
| 737 | data[1] = (u8)(pHdr->wDOBodyLen % 0x100); |
|---|
| 738 | data[2] = (u8)(pHdr->OTID.byObjectType); |
|---|
| 739 | data[3] = (u8)(pHdr->OTID.byObjectID); |
|---|
| 740 | data[4] = (u8)(pHdr->OP.byObjectOptions); |
|---|
| 741 | data[5] = (u8)(pHdr->OP.byObjectFlags); |
|---|
| 742 | data[6] = (u8)(pHdr->OP.byObjectTry); |
|---|
| 743 | memcpy(data + 7, pHdr->dwReserv1, 4); |
|---|
| 744 | memcpy(data + 11, pHdr->abyReserv2, 6); |
|---|
| 745 | memcpy(data + 17, pHdr->SA_V2, SEC_ATTR_SIZE); |
|---|
| 746 | } |
|---|
| 747 | } |
|---|
| 748 | |
|---|
| 749 | static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) |
|---|
| 750 | { |
|---|
| 751 | u8 data[SC_RUTOKEN_DO_HDR_LEN]; |
|---|
| 752 | sc_apdu_t apdu; |
|---|
| 753 | int ret; |
|---|
| 754 | |
|---|
| 755 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 756 | if ( |
|---|
| 757 | (pHdr->wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST) || |
|---|
| 758 | (pHdr->OTID.byObjectType != SC_RUTOKEN_TYPE_KEY) || |
|---|
| 759 | (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) || |
|---|
| 760 | (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_FULL_OPEN_DO) || |
|---|
| 761 | (pHdr->OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || |
|---|
| 762 | (pHdr->OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) |
|---|
| 763 | ) |
|---|
| 764 | { |
|---|
| 765 | ret = SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 766 | } |
|---|
| 767 | else |
|---|
| 768 | { |
|---|
| 769 | pHdr->OP.byObjectTry = 0; |
|---|
| 770 | rutoken_set_do_hdr(data, pHdr); |
|---|
| 771 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x65); |
|---|
| 772 | apdu.data = data; |
|---|
| 773 | apdu.datalen = apdu.lc = sizeof(data); |
|---|
| 774 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 775 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 776 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 777 | } |
|---|
| 778 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 779 | } |
|---|
| 780 | |
|---|
| 781 | static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) |
|---|
| 782 | { |
|---|
| 783 | u8 data[SC_RUTOKEN_DO_HDR_LEN + SC_RUTOKEN_DO_PART_BODY_LEN]; |
|---|
| 784 | sc_apdu_t apdu; |
|---|
| 785 | int ret; |
|---|
| 786 | |
|---|
| 787 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 788 | if ( |
|---|
| 789 | ((pDO->HDR.OTID.byObjectType & SC_RUTOKEN_TYPE_CHV) && |
|---|
| 790 | (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_USER) && |
|---|
| 791 | (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_ADMIN)) || |
|---|
| 792 | ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_GOST) && |
|---|
| 793 | (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST)) || |
|---|
| 794 | ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_SE) && |
|---|
| 795 | (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_SE)) || |
|---|
| 796 | (pDO->HDR.OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || |
|---|
| 797 | (pDO->HDR.OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) || |
|---|
| 798 | ((pDO->HDR.OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) && |
|---|
| 799 | (pDO->HDR.wDOBodyLen > SC_RUTOKEN_COMPACT_DO_MAX_LEN)) || |
|---|
| 800 | (pDO->HDR.wDOBodyLen > SC_RUTOKEN_DO_PART_BODY_LEN) |
|---|
| 801 | ) |
|---|
| 802 | { |
|---|
| 803 | ret = SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 804 | } |
|---|
| 805 | else |
|---|
| 806 | { |
|---|
| 807 | rutoken_set_do_hdr(data, &pDO->HDR); |
|---|
| 808 | memcpy(data + SC_RUTOKEN_DO_HDR_LEN, pDO->abyDOBody, pDO->HDR.wDOBodyLen); |
|---|
| 809 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x62); |
|---|
| 810 | apdu.data = data; |
|---|
| 811 | apdu.datalen = apdu.lc = SC_RUTOKEN_DO_HDR_LEN + pDO->HDR.wDOBodyLen; |
|---|
| 812 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 813 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 814 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 815 | } |
|---|
| 816 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 817 | } |
|---|
| 818 | |
|---|
| 819 | static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) |
|---|
| 820 | { |
|---|
| 821 | u8 data[1]; |
|---|
| 822 | sc_apdu_t apdu; |
|---|
| 823 | int ret; |
|---|
| 824 | |
|---|
| 825 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 826 | if ((pInfo->SelType != select_first) && |
|---|
| 827 | ((pInfo->DoId < SC_RUTOKEN_DO_ALL_MIN_ID) || |
|---|
| 828 | (pInfo->DoId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2))) |
|---|
| 829 | { |
|---|
| 830 | ret = SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 831 | } |
|---|
| 832 | else |
|---|
| 833 | { |
|---|
| 834 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x30, 0x00, 0x00); |
|---|
| 835 | apdu.cla = 0x80; |
|---|
| 836 | apdu.resp = pInfo->pDoData; |
|---|
| 837 | apdu.resplen = sizeof(pInfo->pDoData); |
|---|
| 838 | apdu.le = 255; |
|---|
| 839 | memset(apdu.resp, 0, apdu.resplen); |
|---|
| 840 | switch(pInfo->SelType) |
|---|
| 841 | { |
|---|
| 842 | case select_first: |
|---|
| 843 | apdu.cse = SC_APDU_CASE_2_SHORT; |
|---|
| 844 | break; |
|---|
| 845 | case select_next: |
|---|
| 846 | apdu.p2 = 0x02; |
|---|
| 847 | case select_by_id: |
|---|
| 848 | data[0] = pInfo->DoId; |
|---|
| 849 | apdu.data = data; |
|---|
| 850 | apdu.datalen = sizeof(data); |
|---|
| 851 | apdu.lc = sizeof(data); |
|---|
| 852 | break; |
|---|
| 853 | default: |
|---|
| 854 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 855 | break; |
|---|
| 856 | } |
|---|
| 857 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 858 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 859 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 860 | } |
|---|
| 861 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 862 | } |
|---|
| 863 | |
|---|
| 864 | static int rutoken_delete_do(sc_card_t *card, u8 *pId) |
|---|
| 865 | { |
|---|
| 866 | u8 data[1]; |
|---|
| 867 | sc_apdu_t apdu; |
|---|
| 868 | int ret; |
|---|
| 869 | |
|---|
| 870 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 871 | if ((*pId < SC_RUTOKEN_DO_ALL_MIN_ID) || |
|---|
| 872 | (*pId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2)) |
|---|
| 873 | { |
|---|
| 874 | ret = SC_ERROR_INVALID_ARGUMENTS; |
|---|
| 875 | } |
|---|
| 876 | else |
|---|
| 877 | { |
|---|
| 878 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x64); |
|---|
| 879 | data[0] = *pId; |
|---|
| 880 | apdu.data = data; |
|---|
| 881 | apdu.datalen = sizeof(data); |
|---|
| 882 | apdu.lc = sizeof(data); |
|---|
| 883 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 884 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 885 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 886 | } |
|---|
| 887 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 888 | } |
|---|
| 889 | |
|---|
| 890 | |
|---|
| 891 | |
|---|
| 892 | static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_len, |
|---|
| 893 | u8 * out, size_t outlen, int p1, int p2, int isIV) |
|---|
| 894 | { |
|---|
| 895 | u8 buf[248]; |
|---|
| 896 | size_t len, outlen_tail = outlen; |
|---|
| 897 | int ret; |
|---|
| 898 | sc_apdu_t apdu; |
|---|
| 899 | |
|---|
| 900 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 901 | sc_debug(card->ctx, ": crgram_len %i; outlen %i", crgram_len, outlen); |
|---|
| 902 | |
|---|
| 903 | if (!out) |
|---|
| 904 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 905 | if (crgram_len < 16 || ((crgram_len) % 8)) |
|---|
| 906 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_WRONG_LENGTH); |
|---|
| 907 | |
|---|
| 908 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); |
|---|
| 909 | do |
|---|
| 910 | { |
|---|
| 911 | len = (crgram_len > sizeof(buf)) ? sizeof(buf) : crgram_len; |
|---|
| 912 | apdu.lc = len; |
|---|
| 913 | apdu.datalen = len; |
|---|
| 914 | apdu.data = crgram; |
|---|
| 915 | crgram += len; |
|---|
| 916 | crgram_len -= len; |
|---|
| 917 | |
|---|
| 918 | apdu.cla = (crgram_len == 0) ? 0x00 : 0x10; |
|---|
| 919 | apdu.le = len; |
|---|
| 920 | apdu.resplen = len; |
|---|
| 921 | apdu.resp = buf; |
|---|
| 922 | |
|---|
| 923 | ret = sc_transmit_apdu(card, &apdu); |
|---|
| 924 | SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); |
|---|
| 925 | ret = sc_check_sw(card, apdu.sw1, apdu.sw2); |
|---|
| 926 | if (ret == SC_NO_ERROR) |
|---|
| 927 | { |
|---|
| 928 | if (isIV) |
|---|
| 929 | { |
|---|
| 930 | apdu.resp += 8; |
|---|
| 931 | apdu.resplen -= 8; |
|---|
| 932 | isIV = 0; |
|---|
| 933 | } |
|---|
| 934 | if (apdu.resplen > outlen_tail) |
|---|
| 935 | ret = SC_ERROR_BUFFER_TOO_SMALL; |
|---|
| 936 | else |
|---|
| 937 | { |
|---|
| 938 | memcpy(out, apdu.resp, apdu.resplen); |
|---|
| 939 | out += apdu.resplen; |
|---|
| 940 | outlen_tail -= apdu.resplen; |
|---|
| 941 | } |
|---|
| 942 | } |
|---|
| 943 | } while (ret == SC_NO_ERROR && crgram_len != 0); |
|---|
| 944 | sc_debug(card->ctx, "len out cipher %d\n", outlen - outlen_tail); |
|---|
| 945 | if (ret == SC_NO_ERROR) |
|---|
| 946 | ret = (outlen_tail == 0) ? (int)outlen : SC_ERROR_WRONG_LENGTH; |
|---|
| 947 | SC_FUNC_RETURN(card->ctx, 3, ret); |
|---|
| 948 | } |
|---|
| 949 | |
|---|
| 950 | |
|---|
| 951 | |
|---|
| 952 | static int rutoken_cipher_gost(sc_card_t *card, |
|---|
| 953 | struct sc_rutoken_decipherinfo *ptr, char is_encipher) |
|---|
| 954 | { |
|---|
| 955 | int ret; |
|---|
| 956 | |
|---|
| 957 | if (is_encipher) |
|---|
| 958 | ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, |
|---|
| 959 | ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); |
|---|
| 960 | else |
|---|
| 961 | ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, |
|---|
| 962 | ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); |
|---|
| 963 | if (ret > 0) |
|---|
| 964 | { |
|---|
| 965 | if ((size_t)ret == ptr->outlen) |
|---|
| 966 | ret = SC_SUCCESS; |
|---|
| 967 | else |
|---|
| 968 | ret = SC_ERROR_INTERNAL; |
|---|
| 969 | } |
|---|
| 970 | return ret; |
|---|
| 971 | |
|---|
| 972 | } |
|---|
| 973 | |
|---|
| 974 | static int rutoken_compute_mac_gost(sc_card_t *card, |
|---|
| 975 | const u8 *in, size_t ilen, |
|---|
| 976 | u8 *out, size_t olen) |
|---|
| 977 | { |
|---|
| 978 | const size_t signing_chunk = 248; |
|---|
| 979 | size_t len; |
|---|
| 980 | int ret; |
|---|
| 981 | sc_apdu_t apdu; |
|---|
| 982 | |
|---|
| 983 | SC_FUNC_CALLED(card->ctx, 3); |
|---|
| 984 | if (!in || !out || olen != 4 || ilen == 0) |
|---|
| 985 | SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS); |
|---|
| 986 |
|---|