/* * sc-hsm-tool.c: SmartCard-HSM Management Tool * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2012 www.CardContact.de, Andreas Schwier, Minden, Germany * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include /* Requires openssl for dkek import */ #include #include #include #include #include #include #include #include "libopensc/sc-ossl-compat.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "libopensc/card-sc-hsm.h" #include "util.h" static const char *app_name = "sc-hsm-tool"; static const char magic[] = "Salted__"; static struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01 }, 11 }; static int opt_wait = 0; static char *opt_reader = NULL; static char *opt_label = NULL; static int verbose = 0; // Some reasonable maximums #define MAX_CERT 4096 #define MAX_PRKD 256 #define MAX_KEY 1500 #define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY) #define SEED_LENGTH 16 enum { OPT_SO_PIN = 0x100, OPT_PIN, OPT_RETRY, OPT_BIO1, OPT_BIO2, OPT_PASSWORD, OPT_PASSWORD_SHARES_THRESHOLD, OPT_PASSWORD_SHARES_TOTAL }; static const struct option options[] = { { "initialize", 0, NULL, 'X' }, { "create-dkek-share", 1, NULL, 'C' }, { "import-dkek-share", 1, NULL, 'I' }, #ifdef PRINT_DKEK_SHARE { "print-dkek-share", 1, NULL, 'P' }, #endif { "wrap-key", 1, NULL, 'W' }, { "unwrap-key", 1, NULL, 'U' }, { "dkek-shares", 1, NULL, 's' }, { "so-pin", 1, NULL, OPT_SO_PIN }, { "pin", 1, NULL, OPT_PIN }, { "pin-retry", 1, NULL, OPT_RETRY }, { "bio-server1", 1, NULL, OPT_BIO1 }, { "bio-server2", 1, NULL, OPT_BIO2 }, { "password", 1, NULL, OPT_PASSWORD }, { "pwd-shares-threshold", 1, NULL, OPT_PASSWORD_SHARES_THRESHOLD }, { "pwd-shares-total", 1, NULL, OPT_PASSWORD_SHARES_TOTAL }, { "key-reference", 1, NULL, 'i' }, { "label", 1, NULL, 'l' }, { "force", 0, NULL, 'f' }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Initialize token", "Create DKEK key share and save to ", "Import DKEK key share ", #ifdef PRINT_DKEK_SHARE "Print HEX of DKEK key share ", #endif "Wrap key and save to ", "Unwrap key read from ", "Number of DKEK shares [No DKEK]", "Define security officer PIN (SO-PIN)", "Define user PIN", "Define user PIN retry counter", "AID of biometric server for template 1 (hex)", "AID of biometric server for template 2 (hex)", "Define password for DKEK share", "Define threshold for number of password shares required for reconstruction", "Define number of password shares", "Key reference for key wrap/unwrap", "Token label for --initialize", "Force replacement of key and certificate", "Uses reader number [0]", "Wait for a card to be inserted", "Verbose operation. Use several times to enable debug output.", }; typedef struct { BIGNUM * x; BIGNUM * y; } secret_share_t; /** * Generate a prime number * * The internal CPRNG is seeded using the provided seed value. * * @param prime Pointer for storage of prime number * @param s Secret to share * @param bits Bit size of prime * @param rngSeed Seed value for CPRNG * @param rngSeedLength Length of Seed value for CPRNG * */ static int generatePrime(BIGNUM *prime, const BIGNUM *s, const int bits, unsigned char *rngSeed, const unsigned int rngSeedLength) { int max_rounds = 1000; // Seed the RNG RAND_seed(rngSeed, rngSeedLength); // Clear the prime value BN_clear(prime); do { // Generate random prime BN_generate_prime_ex(prime, bits, 1, NULL, NULL, NULL); } while ((BN_ucmp(prime, s) == -1) && (max_rounds-- > 0)); // If prime < s or not reached 1000 tries if (max_rounds > 0) return 0; else return -1; // We could not find a prime number } /** * Helper method to calculate the y-value * for a given x-value and a polynomial * * @param x X-value * @param polynomial The underlying polynomial * @param t Threshold (determines the degree of the polynomial) * @param prime Prime for finite field arithmetic * @param y Pointer for storage of calculated y-value */ static void calculatePolynomialValue(const BIGNUM *x, BIGNUM **polynomial, const unsigned char t, const BIGNUM *prime, BIGNUM *y) { BIGNUM **pp; BIGNUM *temp; BIGNUM *exponent; unsigned long exp; BN_CTX *bn_ctx; // Create context for temporary variables of OpenSSL engine bn_ctx = BN_CTX_new(); temp = BN_new(); exponent = BN_new(); // Set y to ZERO BN_zero(y); /* Initialize the result using the secret value at position 0 of the polynomial */ pp = polynomial; BN_copy(y, *pp); pp++; for (exp = 1; exp < t; exp++) { BN_copy(temp, x); BN_set_word(exponent, exp); // temp = x^exponent mod prime BN_mod_exp(temp, x, exponent, prime, bn_ctx); // exponent = temp * a = a * x^exponent mod prime BN_mod_mul(exponent, temp, *pp, prime, bn_ctx); // add the temp value from exponent to y BN_copy(temp, y); BN_mod_add(y, temp, exponent, prime, bn_ctx); pp++; } BN_clear_free(temp); BN_clear_free(exponent); BN_CTX_free(bn_ctx); } /** * Create shares depending on the provided parameters * * @param s Secret value to share * @param t Threshold needed to reconstruct the secret * @param n Total number of shares * @param prime Prime for finite field arithmetic * @param shares Pointer for storage of calculated shares (must be big enough to hold n shares) */ static int createShares(const BIGNUM *s, const unsigned char t, const unsigned char n, const BIGNUM *prime, secret_share_t *shares) { // Array representing the polynomial a(x) = s + a_1 * x + ... + a_n-1 * x^n-1 mod p BIGNUM **polynomial = malloc(n * sizeof(BIGNUM *)); BIGNUM **pp; unsigned long i; secret_share_t *sp; if (!polynomial) return -1; // Set the secret value as the constant part of the polynomial pp = polynomial; *pp = BN_new(); BN_copy(*pp, s); pp++; // Initialize and generate some random values for coefficients a_x in the remaining polynomial for (i = 1; i < t; i++) { *pp = BN_new(); BN_rand_range(*pp, prime); pp++; } sp = shares; // Now calculate n secret shares for (i = 1; i <= n; i++) { sp->x = BN_new(); sp->y = BN_new(); BN_set_word((sp->x), i); calculatePolynomialValue(sp->x, polynomial, t, prime, (sp->y)); sp++; } // Deallocate the resource of the polynomial pp = polynomial; for (i = 0; i < t; i++) { BN_clear_free(*pp); pp++; } free(polynomial); return 0; } /** * Reconstruct secret using the provided shares * * @param shares Shares used to reconstruct secret (should contain t entries) * @param t Threshold used to reconstruct the secret * @param prime Prime for finite field arithmetic * @param s Pointer for storage of calculated secret */ static int reconstructSecret(secret_share_t *shares, unsigned char t, const BIGNUM *prime, BIGNUM *s) { unsigned char i; unsigned char j; // Array representing the polynomial a(x) = s + a_1 * x + ... + a_n-1 * x^n-1 mod p BIGNUM **bValue = malloc(t * sizeof(BIGNUM *)); BIGNUM **pbValue; BIGNUM * numerator; BIGNUM * denominator; BIGNUM * temp; secret_share_t *sp_i; secret_share_t *sp_j; BN_CTX *ctx; if (!bValue) return -1; // Initialize pbValue = bValue; for (i = 0; i < t; i++) { *pbValue = BN_new(); pbValue++; } numerator = BN_new(); denominator = BN_new(); temp = BN_new(); // Create context for temporary variables of engine ctx = BN_CTX_new(); pbValue = bValue; sp_i = shares; for (i = 0; i < t; i++) { BN_one(numerator); BN_one(denominator); sp_j = shares; for (j = 0; j < t; j++) { if (i == j) { sp_j++; continue; } BN_mul(numerator, numerator, (sp_j->x), ctx); BN_sub(temp, (sp_j->x), (sp_i->x)); BN_mul(denominator, denominator, temp, ctx); sp_j++; } /* * Use the modular inverse value of the denominator for the * multiplication */ if (BN_mod_inverse(denominator, denominator, prime, ctx) == NULL ) { free(bValue); return -1; } BN_mod_mul(*pbValue, numerator, denominator, prime, ctx); pbValue++; sp_i++; } /* * Calculate the secret by multiplying all y-values with their * corresponding intermediate values */ pbValue = bValue; sp_i = shares; BN_zero(s); for (i = 0; i < t; i++) { BN_mul(temp, (sp_i->y), *pbValue, ctx); BN_add(s, s, temp); pbValue++; sp_i++; } // Perform modulo operation and copy result BN_nnmod(temp, s, prime, ctx); BN_copy(s, temp); BN_clear_free(numerator); BN_clear_free(denominator); BN_clear_free(temp); BN_CTX_free(ctx); // Deallocate the resource of the polynomial pbValue = bValue; for (i = 0; i < t; i++) { BN_clear_free(*pbValue); pbValue++; } free(bValue); return 0; } /** * Helper method to free allocated resources * * @param shares Shares to be freed * @param n Total number of shares to freed */ static int cleanUpShares(secret_share_t *shares, unsigned char n) { int i; secret_share_t *sp; sp = shares; for (i = 0; i < n; i++) { BN_clear_free((sp->x)); BN_clear_free((sp->y)); sp++; } free(shares); return 0; } void clearScreen() { if (system( "clear" )) { if (system( "cls" )) { fprintf(stderr, "Clearing the screen failed\n"); } } } void waitForEnterKeyPressed() { int c; fflush(stdout); while ((c = getchar()) != '\n' && c != EOF) { } } static void print_dkek_info(sc_cardctl_sc_hsm_dkek_t *dkekinfo) { printf("DKEK shares : %d\n", dkekinfo->dkek_shares); if (dkekinfo->outstanding_shares > 0) { printf("DKEK import pending, %d share(s) still missing\n",dkekinfo->outstanding_shares); } else { printf("DKEK key check value : "); util_hex_dump(stdout, dkekinfo->key_check_value, 8, NULL); printf("\n"); } } static void print_info(sc_card_t *card, sc_file_t *file) { int r, tries_left; struct sc_pin_cmd_data data; sc_cardctl_sc_hsm_dkek_t dkekinfo; u8 major, minor, opt; major = file->prop_attr[file->prop_attr_len - 2]; minor = file->prop_attr[file->prop_attr_len - 1]; printf("Version : %d.%d\n", (int)major, (int)minor); if (file->prop_attr_len > 2) { /* Version >= 2.0 */ opt = file->prop_attr[file->prop_attr_len - 4]; if (opt != 0) { printf("Config options :\n"); if (opt & INIT_RRC_ENABLED) { printf(" User PIN reset with SO-PIN enabled\n"); } if (opt & INIT_TRANSPORT_PIN) { printf(" Transport-PIN mode enabled\n"); } } /* Try to update SO-PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; data.pin_type = SC_AC_CHV; data.pin_reference = ID_SO_PIN; r = sc_pin_cmd(card, &data, &tries_left); if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { printf("SmartCard-HSM has never been initialized. Please use --initialize to set SO-PIN and user PIN.\n"); } else { if (tries_left == 0) { printf("SO-PIN locked\n"); } else { printf("SO-PIN tries left : %d\n", tries_left); } /* Try to update PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; r = sc_pin_cmd(card, &data, &tries_left); if (r == SC_ERROR_CARD_CMD_FAILED) { printf("Public key authentication active.\n"); } else if (r == SC_ERROR_REF_DATA_NOT_USABLE) { printf("Transport-PIN active. Please change to user selected PIN first.\n"); } else { if (tries_left == 0) { printf("User PIN locked\n"); } else { printf("User PIN tries left : %d\n", tries_left); } } } } else { /* Version < 2.0 */ /* Try to update PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; r = sc_pin_cmd(card, &data, &tries_left); if (r == SC_ERROR_REF_DATA_NOT_USABLE) { printf("SmartCard-HSM has never been initialized. Please use --initialize to set SO-PIN and user PIN.\n"); } else { if (tries_left == 0) { printf("User PIN locked\n"); } else { printf("User PIN tries left : %d\n", tries_left); } } } memset(&dkekinfo, 0, sizeof(dkekinfo)); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares return; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); } print_dkek_info(&dkekinfo); } static int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, int retry_counter, const char *bio1, const char *bio2, int dkek_shares, const char *label) { sc_cardctl_sc_hsm_init_param_t param; size_t len; char *_so_pin = NULL, *_user_pin = NULL; int r; if (so_pin == NULL) { printf("Enter SO-PIN (16 hexadecimal characters) : "); util_getpass(&_so_pin, NULL, stdin); printf("\n"); } else { _so_pin = (char *)so_pin; } len = sizeof(param.init_code); r = sc_hex_to_bin(_so_pin, param.init_code, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 8) { fprintf(stderr, "SO-PIN must be a hexadecimal string of 16 characters\n"); return -1; } if (user_pin == NULL) { printf("Enter initial User-PIN (6 - 16 characters) : "); util_getpass(&_user_pin, NULL, stdin); printf("\n"); } else { _user_pin = (char *)user_pin; } param.user_pin_len = strlen(_user_pin); if (param.user_pin_len < 6) { fprintf(stderr, "PIN must be at least 6 characters long\n"); return -1; } if (param.user_pin_len > 16) { fprintf(stderr, "PIN must not be longer than 16 characters\n"); return -1; } if ((param.user_pin_len == 6) && (retry_counter > 3)) { fprintf(stderr, "Retry counter must not exceed 3 for a 6 digit PIN. Use a longer PIN for a higher retry counter.\n"); return -1; } if ((param.user_pin_len == 7) && (retry_counter > 5)) { fprintf(stderr, "Retry counter must not exceed 5 for a 7 digit PIN. Use a longer PIN for a higher retry counter.\n"); return -1; } if (retry_counter > 10) { fprintf(stderr, "Retry counter must not exceed 10\n"); return -1; } param.user_pin = (u8 *)_user_pin; param.user_pin_retry_counter = (u8)retry_counter; if (bio1) { param.bio1.len = sizeof(param.bio1.value); r = sc_hex_to_bin(bio1, param.bio1.value, ¶m.bio1.len); if (r < 0) { fprintf(stderr, "Error decoding AID of biometric server for template 1 (%s)\n", sc_strerror(r)); return -1; } } else { param.bio1.len = 0; } if (bio2) { param.bio2.len = sizeof(param.bio2.value); r = sc_hex_to_bin(bio2, param.bio2.value, ¶m.bio2.len); if (r < 0) { fprintf(stderr, "Error decoding AID of biometric server for template 2 (%s)\n", sc_strerror(r)); return -1; } } else { param.bio2.len = 0; } param.options[0] = 0x00; param.options[1] = 0x01; /* RESET RETRY COUNTER enabled */ if (param.bio1.len || param.bio2.len) { param.options[1] |= 0x04; /* Session-PIN enabled with clear on reset */ } param.dkek_shares = (char)dkek_shares; param.label = (char *)label; r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_INITIALIZE, (void *)¶m); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_INITIALIZE, *) failed with %s\n", sc_strerror(r)); } return 0; } static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_password_shares) { int r, i; BIGNUM *prime; BIGNUM *secret; BIGNUM *p; char inbuf[64]; unsigned char bin[64]; size_t binlen = 0; unsigned char *ip; secret_share_t *shares = NULL; secret_share_t *sp; if (num_of_password_shares < 2) { fprintf(stderr, "--pwd-shares-total must 2 or larger\n"); return -1; } // Allocate data buffer for the shares shares = malloc(num_of_password_shares * sizeof(secret_share_t)); if (!shares) return -1; /* * Initialize prime and secret */ prime = BN_new(); secret = BN_new(); printf("\nDeciphering the DKEK for import into the SmartCard-HSM requires %i key custodians", num_of_password_shares); printf("\nto present their share. Only the first key custodian needs to enter the public prime."); printf("\nPlease remember to present the share id as well as the share value."); printf("\n\nPlease enter prime: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); BN_bin2bn(bin, binlen, prime); sp = shares; for (i = 0; i < num_of_password_shares; i++) { clearScreen(); printf("Press to enter share %i of %i\n\n", i + 1, num_of_password_shares); waitForEnterKeyPressed(); clearScreen(); sp->x = BN_new(); sp->y = BN_new(); printf("Share %i of %i\n\n", i + 1, num_of_password_shares); printf("Please enter share ID: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } p = (sp->x); BN_hex2bn(&p, inbuf); printf("Please enter share value: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); BN_bin2bn(bin, binlen, (sp->y)); sp++; } clearScreen(); r = reconstructSecret(shares, num_of_password_shares, prime, secret); if (r < 0) { printf("\nError during reconstruction of secret. Wrong shares?\n"); cleanUpShares(shares, num_of_password_shares); return r; } /* * Encode the secret value */ ip = (unsigned char *) inbuf; *pwdlen = BN_bn2bin(secret, ip); *pwd = calloc(1, *pwdlen); if (*pwd) { memcpy(*pwd, ip, *pwdlen); } cleanUpShares(shares, num_of_password_shares); BN_clear_free(prime); BN_clear_free(secret); return *pwd ? 0 : -1; } static int import_dkek_share(sc_card_t *card, const char *inf, int iter, const char *password, int num_of_password_shares) { sc_cardctl_sc_hsm_dkek_t dkekinfo; EVP_CIPHER_CTX *bn_ctx = NULL; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; int r, outlen, pwdlen; if (inf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) { perror(inf); fclose(in); return -1; } fclose(in); if (memcmp(filebuff, magic, sizeof(magic) - 1)) { fprintf(stderr, "File %s is not a DKEK share\n", inf); return -1; } if (password == NULL) { if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); pwdlen = strlen(pwd); printf("\n"); } else { r = recreate_password_from_shares(&pwd, &pwdlen, num_of_password_shares); if (r < 0) { return -1; } } } else { pwd = (char *) password; pwdlen = strlen(password); } printf("Deciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); OPENSSL_cleanse(pwd, strlen(pwd)); if (password == NULL) { free(pwd); } bn_ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(bn_ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_DecryptUpdate(bn_ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) { fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } if (!EVP_DecryptFinal_ex(bn_ctx, outbuff + outlen, &r)) { fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } memset(&dkekinfo, 0, sizeof(dkekinfo)); memcpy(dkekinfo.dkek_share, outbuff, sizeof(dkekinfo.dkek_share)); dkekinfo.importShare = 1; OPENSSL_cleanse(outbuff, sizeof(outbuff)); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); EVP_CIPHER_CTX_free(bn_ctx); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Not supported by card or card not initialized for key share usage\n"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); return -1; } printf("DKEK share imported\n"); print_dkek_info(&dkekinfo); return 0; } static int print_dkek_share(sc_card_t *card, const char *inf, int iter, const char *password, int num_of_password_shares) { // hex output can be used in the SCSH shell with the // decrypt_keyblob.js file sc_cardctl_sc_hsm_dkek_t dkekinfo; EVP_CIPHER_CTX *bn_ctx = NULL; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; int r, outlen, pwdlen; u8 i; if (inf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) { perror(inf); fclose(in); return -1; } fclose(in); if (memcmp(filebuff, magic, sizeof(magic) - 1)) { fprintf(stderr, "File %s is not a DKEK share\n", inf); return -1; } if (password == NULL) { if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); pwdlen = strlen(pwd); printf("\n"); } else { r = recreate_password_from_shares(&pwd, &pwdlen, num_of_password_shares); if (r < 0) { return -1; } } } else { pwd = (char *) password; pwdlen = strlen(password); } printf("Deciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); OPENSSL_cleanse(pwd, strlen(pwd)); if (password == NULL) { free(pwd); } bn_ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(bn_ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_DecryptUpdate(bn_ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) { fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } if (!EVP_DecryptFinal_ex(bn_ctx, outbuff + outlen, &r)) { fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n"); return -1; } memset(&dkekinfo, 0, sizeof(dkekinfo)); memcpy(dkekinfo.dkek_share, outbuff, sizeof(dkekinfo.dkek_share)); dkekinfo.importShare = 1; OPENSSL_cleanse(outbuff, sizeof(outbuff)); printf("DKEK Share HEX: \n\n"); for (i = 0; i < sizeof(dkekinfo.dkek_share); i++) { printf("%02X", dkekinfo.dkek_share[i]); } printf("\n\n"); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); EVP_CIPHER_CTX_free(bn_ctx); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Not supported by card or card not initialized for key share usage\n"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); return -1; } //printf("DKEK share imported\n"); //print_dkek_info(&dkekinfo); return 0; } static void ask_for_password(char **pwd, int *pwdlen) { char *refpwd = NULL; printf( "\nThe DKEK share will be enciphered using a key derived from a user supplied password.\n"); printf( "The security of the DKEK share relies on a well chosen and sufficiently long password.\n"); printf( "The recommended length is more than 10 characters, which are mixed letters, numbers and\n"); printf("symbols.\n\n"); printf( "Please keep the generated DKEK share file in a safe location. We also recommend to keep a\n"); printf( "paper printout, in case the electronic version becomes unavailable. A printable version\n"); printf( "of the file can be generated using \"openssl base64 -in \".\n"); while (1) { printf("Enter password to encrypt DKEK share : "); util_getpass(pwd, NULL, stdin); printf("\n"); if (strlen(*pwd) < 6) { printf("Password way to short. Please retry.\n"); continue; } printf("Please retype password to confirm : "); util_getpass(&refpwd, NULL, stdin); printf("\n"); if (strcmp(*pwd, refpwd)) { printf("Passwords do not match. Please retry.\n"); continue; } *pwdlen = strlen(*pwd); break; } OPENSSL_cleanse(refpwd, strlen(refpwd)); free(refpwd); } static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int password_shares_threshold, int password_shares_total) { int r, i; BIGNUM *prime; BIGNUM *secret; unsigned char buf[64]; char hex[64]; int l; secret_share_t *shares = NULL; secret_share_t *sp; u8 rngseed[16]; if ((password_shares_threshold == -1) || (password_shares_total == -1)) { fprintf(stderr, "Must specify both, --pwd-shares-total and --pwd-shares-threshold\n"); return -1; } if (password_shares_total < 3) { fprintf(stderr, "--pwd-shares-total must be 3 or larger\n"); return -1; } if (password_shares_threshold < 2) { fprintf(stderr, "--pwd-shares-threshold must 2 or larger\n"); return -1; } if (password_shares_threshold > password_shares_total) { fprintf(stderr, "--pwd-shares-threshold must be smaller or equal to --pwd-shares-total\n"); return -1; } printf( "\nThe DKEK will be enciphered using a randomly generated 64 bit password.\n"); printf( "This password is split using a (%i-of-%i) threshold scheme.\n\n", password_shares_threshold, password_shares_total); printf( "Please keep the generated and encrypted DKEK file in a safe location. We also recommend \n"); printf( "to keep a paper printout, in case the electronic version becomes unavailable. A printable version\n"); printf( "of the file can be generated using \"openssl base64 -in \".\n"); printf("\n\nPress to continue"); waitForEnterKeyPressed(); *pwd = calloc(1, 8); *pwdlen = 8; r = sc_get_challenge(card, (unsigned char *)*pwd, 8); if (r < 0) { printf("Error generating random key failed with %s", sc_strerror(r)); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); return r; } **pwd &= 0x7F; // Make sure the bit size of the secret is not bigger than 63 bits /* * Initialize prime and secret */ prime = BN_new(); secret = BN_new(); /* * Encode the secret value */ BN_bin2bn((unsigned char *)*pwd, *pwdlen, secret); /* * Generate seed and calculate a prime depending on the size of the secret */ r = sc_get_challenge(card, rngseed, SEED_LENGTH); if (r < 0) { printf("Error generating random seed failed with %s", sc_strerror(r)); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); return r; } r = generatePrime(prime, secret, 64, rngseed, SEED_LENGTH); if (r < 0) { printf("Error generating valid prime number. Please try again."); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); return r; } // Allocate data buffer for the generated shares shares = malloc(password_shares_total * sizeof(secret_share_t)); if (!shares || 0 > createShares(secret, password_shares_threshold, password_shares_total, prime, shares)) { printf("Error generating Shares. Please try again."); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); free(shares); return -1; } sp = shares; for (i = 0; i < password_shares_total; i++) { clearScreen(); printf("Press to display key share %i of %i\n\n", i + 1, password_shares_total); waitForEnterKeyPressed(); clearScreen(); printf("Share %i of %i\n\n", i + 1, password_shares_total); l = BN_bn2bin(prime, buf); sc_bin_to_hex(buf, l, hex, 64, ':'); printf("\nPrime : %s\n", hex); printf("Share ID : %s\n", BN_bn2dec((sp->x))); l = BN_bn2bin((sp->y), buf); sc_bin_to_hex(buf, l, hex, 64, ':'); printf("Share value : %s\n", hex); printf("\n\nPlease note ALL values above and press when finished"); waitForEnterKeyPressed(); sp++; } clearScreen(); cleanUpShares(shares, password_shares_total); BN_clear_free(prime); BN_clear_free(secret); return 0; } static int create_dkek_share(sc_card_t *card, const char *outf, int iter, const char *password, int password_shares_threshold, int password_shares_total) { EVP_CIPHER_CTX *c_ctx = NULL; FILE *out = NULL; u8 filebuff[64], key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; u8 dkek_share[32]; char *pwd = NULL; int r = 0, outlen, pwdlen = 0; if (outf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } if (password == NULL) { if ((password_shares_threshold == -1) && (password_shares_total == -1)) { ask_for_password(&pwd, &pwdlen); } else { // create password using threshold scheme r = generate_pwd_shares(card, &pwd, &pwdlen, password_shares_threshold, password_shares_total); } } else { pwd = (char *) password; pwdlen = strlen(password); } if (r < 0) { fprintf(stderr, "Creating DKEK share failed\n"); return -1; } memcpy(filebuff, magic, sizeof(magic) - 1); r = sc_get_challenge(card, filebuff + 8, 8); if (r < 0) { fprintf(stderr, "Error generating random number failed with %s\n", sc_strerror(r)); return -1; } printf("Enciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); if (password == NULL) { OPENSSL_cleanse(pwd, pwdlen); free(pwd); } r = sc_get_challenge(card, dkek_share, sizeof(dkek_share)); if (r < 0) { fprintf(stderr, "Error generating random number failed with %s\n", sc_strerror(r)); return -1; } c_ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(c_ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_EncryptUpdate(c_ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share))) { fprintf(stderr, "Error encrypting DKEK share\n"); return -1; } if (!EVP_EncryptFinal_ex(c_ctx, filebuff + 16 + outlen, &r)) { fprintf(stderr, "Error encrypting DKEK share\n"); return -1; } out = fopen(outf, "wb"); if (out == NULL) { perror(outf); return -1; } if (fwrite(filebuff, 1, sizeof(filebuff), out) != sizeof(filebuff)) { perror(outf); fclose(out); return -1; } fclose(out); OPENSSL_cleanse(filebuff, sizeof(filebuff)); EVP_CIPHER_CTX_free(c_ctx); printf("DKEK share created and saved to %s\n", outf); return 0; } static size_t determineLength(const u8 *tlv, size_t buflen) { const u8 *ptr = tlv; unsigned int cla,tag; size_t len; if (sc_asn1_read_tag(&ptr, buflen, &cla, &tag, &len) != SC_SUCCESS || ptr == NULL) { return 0; } return len + (ptr - tlv); } /** * Encapsulate data object as TLV object * * @param tag the one byte tag * @param indata the value field * @param inlen the length of the value field * @param outdata pointer to the allocated memory buffer * @param outlen the size of the TLV object */ static int wrap_with_tag(u8 tag, u8 *indata, size_t inlen, u8 **outdata, size_t *outlen) { int r = sc_asn1_put_tag(tag, indata, inlen, NULL, 0, NULL); if (r < 0) return r; if (r == 0) return SC_ERROR_INVALID_ASN1_OBJECT; u8 *ptr = calloc(r, sizeof *ptr); if (ptr == NULL) { return SC_ERROR_OUT_OF_MEMORY; } *outdata = ptr; *outlen = r; return sc_asn1_put_tag(tag, indata, inlen, *outdata, *outlen, NULL); } static int wrap_key(sc_context_t *ctx, sc_card_t *card, int keyid, const char *outf, const char *pin) { sc_cardctl_sc_hsm_wrapped_key_t wrapped_key; struct sc_pin_cmd_data data; sc_path_t path; FILE *out = NULL; u8 fid[2]; u8 ef_prkd[MAX_PRKD]; u8 ef_cert[MAX_CERT]; u8 wrapped_key_buff[MAX_KEY]; u8 keyblob[MAX_WRAPPED_KEY]; u8 *key; u8 *ptr; char *lpin = NULL; size_t key_len; int r, ef_prkd_len, ef_cert_len; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); return -1; } if (outf == NULL) { fprintf(stderr, "No file name specified for wrapped key\n"); return -1; } if (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (char *)pin; } memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; data.pin1.data = (unsigned char *)lpin; data.pin1.len = strlen(lpin); r = sc_pin_cmd(card, &data, NULL); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); return -1; } if (pin == NULL) { free(lpin); } wrapped_key.key_id = keyid; wrapped_key.wrapped_key = wrapped_key_buff; wrapped_key.wrapped_key_length = sizeof(wrapped_key_buff); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_WRAP_KEY, (void *)&wrapped_key); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Card not initialized for key wrap\n"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_WRAP_KEY, *) failed with %s\n", sc_strerror(r)); return -1; } fid[0] = PRKD_PREFIX; fid[1] = (unsigned char)keyid; ef_prkd_len = 0; /* Try to select a related EF containing the PKCS#15 description of the key */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) { ef_prkd_len = sc_read_binary(card, 0, ef_prkd, sizeof(ef_prkd), 0); if (ef_prkd_len < 0) { fprintf(stderr, "Error reading PRKD file %s. Skipping.\n", sc_strerror(ef_prkd_len)); ef_prkd_len = 0; } else { ef_prkd_len = determineLength(ef_prkd, ef_prkd_len); } } fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = (unsigned char)keyid; ef_cert_len = 0; /* Try to select a related EF containing the certificate for the key */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) { ef_cert_len = sc_read_binary(card, 0, ef_cert, sizeof(ef_cert), 0); if (ef_cert_len < 0) { fprintf(stderr, "Error reading certificate %s. Skipping\n", sc_strerror(ef_cert_len)); ef_cert_len = 0; } else { ef_cert_len = determineLength(ef_cert, ef_cert_len); } } ptr = keyblob; // Encode key in octet string object key_len = 0; r = wrap_with_tag(0x04, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length, &key, &key_len); LOG_TEST_RET(ctx, r, "Out of memory"); memcpy(ptr, key, key_len); ptr += key_len; free(key); key = NULL; key_len = 0; // Add private key description if (ef_prkd_len > 0) { memcpy(ptr, ef_prkd, ef_prkd_len); ptr += ef_prkd_len; } // Add certificate if (ef_cert_len > 0) { memcpy(ptr, ef_cert, ef_cert_len); ptr += ef_cert_len; } // Encode key, key decription and certificate object in sequence r = wrap_with_tag(0x30, keyblob, ptr - keyblob, &key, &key_len); LOG_TEST_RET(ctx, r, "Out of memory"); out = fopen(outf, "wb"); if (out == NULL) { perror(outf); free(key); return -1; } if (fwrite(key, 1, key_len, out) != key_len) { perror(outf); free(key); fclose(out); return -1; } free(key); fclose(out); return 0; } static int update_ef(sc_card_t *card, u8 prefix, u8 id, int erase, const u8 *buf, size_t buflen) { sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; int r; fid[0] = prefix; fid[1] = id; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); r = sc_select_file(card, &path, NULL); if ((r == SC_SUCCESS) && erase) { sc_delete_file(card, &path); r = SC_ERROR_FILE_NOT_FOUND; } if (r == SC_ERROR_FILE_NOT_FOUND) { file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) 0; file->status = SC_FILE_STATUS_ACTIVATED; r = sc_create_file(card, file); sc_file_free(file); if (r < 0) { return r; } } r = sc_update_binary(card, 0, buf, buflen, 0); return r; } static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *pin, int force) { sc_cardctl_sc_hsm_wrapped_key_t wrapped_key; struct sc_pin_cmd_data data; u8 keyblob[MAX_WRAPPED_KEY]; const u8 *ptr,*prkd,*cert; FILE *in = NULL; sc_path_t path; u8 fid[2]; char *lpin = NULL; unsigned int cla, tag; int r, keybloblen; size_t len, olen, prkd_len, cert_len; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); return -1; } if (inf == NULL) { fprintf(stderr, "No file name specified for wrapped key\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } keybloblen = fread(keyblob, 1, sizeof(keyblob), in); fclose(in); if (keybloblen < 0) { perror(inf); return -1; } ptr = keyblob; if ((sc_asn1_read_tag(&ptr, keybloblen, &cla, &tag, &len) != SC_SUCCESS) || ((cla & SC_ASN1_TAG_CONSTRUCTED) != SC_ASN1_TAG_CONSTRUCTED) || (tag != SC_ASN1_TAG_SEQUENCE) ){ fprintf(stderr, "Invalid wrapped key format (Outer sequence).\n"); return -1; } if ((sc_asn1_read_tag(&ptr, len, &cla, &tag, &olen) != SC_SUCCESS) || ((cla & SC_ASN1_TAG_CONSTRUCTED) == SC_ASN1_TAG_CONSTRUCTED) || (tag != SC_ASN1_TAG_OCTET_STRING) ){ fprintf(stderr, "Invalid wrapped key format (Key binary).\n"); return -1; } wrapped_key.wrapped_key = (u8 *)ptr; wrapped_key.wrapped_key_length = olen; ptr += olen; prkd = ptr; prkd_len = determineLength(ptr, keybloblen - (ptr - keyblob)); ptr += prkd_len; cert = ptr; cert_len = determineLength(ptr, keybloblen - (ptr - keyblob)); printf("Wrapped key contains:\n"); printf(" Key blob\n"); if (prkd_len > 0) { printf(" Private Key Description (PRKD)\n"); } if (cert_len > 0) { printf(" Certificate\n"); } if ((prkd_len > 0) && !force) { fid[0] = PRKD_PREFIX; fid[1] = (unsigned char)keyid; /* Try to select a related EF containing the PKCS#15 description of the key */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) { fprintf(stderr, "Found existing private key description in EF with fid %02x%02x. Please remove key first, select unused key reference or use --force.\n", fid[0], fid[1]); return -1; } } if ((cert_len > 0) && !force) { fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = (unsigned char)keyid; /* Try to select a related EF containing the certificate */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) { fprintf(stderr, "Found existing certificate in EF with fid %02x%02x. Please remove certificate first, select unused key reference or use --force.\n", fid[0], fid[1]); return -1; } } if (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (char *)pin; } memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; data.pin1.data = (u8 *)lpin; data.pin1.len = strlen(lpin); r = sc_pin_cmd(card, &data, NULL); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); return -1; } if (pin == NULL) { free(lpin); } if (force) { fid[0] = KEY_PREFIX; fid[1] = keyid; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); sc_delete_file(card, &path); } wrapped_key.key_id = keyid; r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_UNWRAP_KEY, (void *)&wrapped_key); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares fprintf(stderr, "Card not initialized for key wrap\n"); return -1; } if (r == SC_ERROR_INCORRECT_PARAMETERS) { // Not supported or not initialized for key shares fprintf(stderr, "Wrapped key does not match DKEK\n"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_UNWRAP_KEY, *) failed with %s\n", sc_strerror(r)); return -1; } if (prkd_len > 0) { r = update_ef(card, PRKD_PREFIX, keyid, force, prkd, prkd_len); if (r < 0) { fprintf(stderr, "Updating private key description failed with %s\n", sc_strerror(r)); return -1; } } if (cert_len > 0) { r = update_ef(card, EE_CERTIFICATE_PREFIX, keyid, force, cert, cert_len); if (r < 0) { fprintf(stderr, "Updating certificate failed with %s\n", sc_strerror(r)); return -1; } } printf("Key successfully imported\n"); return 0; } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; int do_initialize = 0; int do_import_dkek_share = 0; int do_print_dkek_share = 0; int do_create_dkek_share = 0; int do_wrap_key = 0; int do_unwrap_key = 0; sc_path_t path; sc_file_t *file = NULL; const char *opt_so_pin = NULL; const char *opt_pin = NULL; const char *opt_filename = NULL; const char *opt_password = NULL; const char *opt_bio1 = NULL; const char *opt_bio2 = NULL; int opt_retry_counter = 3; int opt_dkek_shares = -1; int opt_key_reference = -1; int opt_password_shares_threshold = -1; int opt_password_shares_total = -1; int opt_force = 0; int opt_iter = 10000000; sc_context_param_t ctx_param; sc_context_t *ctx = NULL; sc_card_t *card = NULL; while (1) { c = getopt_long(argc, argv, "XC:I:P:W:U:s:i:fr:wv", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'X': do_initialize = 1; action_count++; break; case 'C': do_create_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'I': do_import_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'P': do_print_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'W': do_wrap_key = 1; opt_filename = optarg; action_count++; break; case 'U': do_unwrap_key = 1; opt_filename = optarg; action_count++; break; case OPT_PASSWORD: util_get_pin(optarg, &opt_password); break; case OPT_SO_PIN: util_get_pin(optarg, &opt_so_pin); break; case OPT_PIN: util_get_pin(optarg, &opt_pin); break; case OPT_RETRY: opt_retry_counter = atol(optarg); break; case OPT_BIO1: opt_bio1 = optarg; break; case OPT_BIO2: opt_bio2 = optarg; break; case OPT_PASSWORD_SHARES_THRESHOLD: opt_password_shares_threshold = atol(optarg); break; case OPT_PASSWORD_SHARES_TOTAL: opt_password_shares_total = atol(optarg); break; case 's': opt_dkek_shares = atol(optarg); break; case 'f': opt_force = 1; break; case 'i': opt_key_reference = atol(optarg); break; case 'r': opt_reader = optarg; break; case 'l': opt_label = optarg; break; case 'v': verbose++; break; case 'w': opt_wait = 1; break; } } #if OPENSSL_VERSION_NUMBER >= 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20700000L) OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); #else CRYPTO_malloc_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); #endif memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.app_name = app_name; r = sc_context_create(&ctx, &ctx_param); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); exit(1); } r = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0, verbose); if (r != SC_SUCCESS) { if (r < 0) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(err)); } goto end; } sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to select application: %s\n", sc_strerror(r)); goto fail; } if (do_initialize && initialize(card, opt_so_pin, opt_pin, opt_retry_counter, opt_bio1, opt_bio2, opt_dkek_shares, opt_label)) goto fail; if (do_create_dkek_share && create_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_threshold, opt_password_shares_total)) goto fail; if (do_import_dkek_share && import_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_total)) goto fail; if (do_print_dkek_share && print_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_total)) goto fail; if (do_wrap_key && wrap_key(ctx, card, opt_key_reference, opt_filename, opt_pin)) goto fail; if (do_unwrap_key && unwrap_key(card, opt_key_reference, opt_filename, opt_pin, opt_force)) goto fail; if (action_count == 0) { print_info(card, file); } err = 0; goto end; fail: err = 1; end: sc_disconnect_card(card); sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; }