6bfb39454b
* libopensc: handle allocation errors * handle more faults during memory allocation fixes several situations that cause segmentation fault
1838 lines
44 KiB
C
1838 lines
44 KiB
C
/*
|
|
* sc-hsm-tool.c: SmartCard-HSM Management Tool
|
|
*
|
|
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* Requires openssl for dkek import */
|
|
#include <openssl/opensslv.h>
|
|
|
|
#include <openssl/opensslconf.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include "libopensc/sc-ossl-compat.h"
|
|
#include "libopensc/opensc.h"
|
|
#include "libopensc/cardctl.h"
|
|
#include "libopensc/asn1.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 1024
|
|
#define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY)
|
|
|
|
#define SEED_LENGTH 16
|
|
|
|
enum {
|
|
OPT_SO_PIN = 0x100,
|
|
OPT_PIN,
|
|
OPT_RETRY,
|
|
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 },
|
|
{ "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 <filename>",
|
|
"Import DKEK key share <filename>",
|
|
#ifdef PRINT_DKEK_SHARE
|
|
"Print HEX of DKEK key share <filename>",
|
|
#endif
|
|
"Wrap key and save to <filename>",
|
|
"Unwrap key read from <filename>",
|
|
"Number of DKEK shares [No DKEK]",
|
|
"Define security officer PIN (SO-PIN)",
|
|
"Define user PIN",
|
|
"Define user PIN retry counter",
|
|
"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 <arg> [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;
|
|
|
|
static sc_context_t *ctx = NULL;
|
|
static sc_card_t *card = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
* 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
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00908000L /* last parm is BN_GENCB which is null in our case */
|
|
BN_generate_prime_ex(prime, bits, 1, NULL, NULL, NULL);
|
|
#else
|
|
BN_generate_prime(prime, bits, 1, NULL, NULL, NULL, NULL );
|
|
#endif
|
|
|
|
} 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 *ctx;
|
|
|
|
// Create context for temporary variables of OpenSSL engine
|
|
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, ctx);
|
|
// exponent = temp * a = a * x^exponent mod prime
|
|
BN_mod_mul(exponent, temp, *pp, prime, ctx);
|
|
// add the temp value from exponent to y
|
|
BN_copy(temp, y);
|
|
BN_mod_add(y, temp, exponent, prime, ctx);
|
|
pp++;
|
|
}
|
|
|
|
BN_clear_free(temp);
|
|
BN_clear_free(exponent);
|
|
|
|
BN_CTX_free(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 secred
|
|
*/
|
|
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, 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;
|
|
|
|
param.options[0] = 0x00;
|
|
param.options[1] = 0x01;
|
|
|
|
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 <enter> 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 *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);
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
|
if (!EVP_DecryptUpdate(ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) {
|
|
fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!EVP_DecryptFinal_ex(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(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 *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);
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
|
if (!EVP_DecryptUpdate(ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) {
|
|
fprintf(stderr, "Error decrypting DKEK share. Password correct ?\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!EVP_DecryptFinal_ex(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(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 <filename>\".\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 <filename>\".\n");
|
|
|
|
printf("\n\nPress <enter> 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));
|
|
|
|
createShares(secret, password_shares_threshold, password_shares_total, prime, shares);
|
|
|
|
sp = shares;
|
|
for (i = 0; i < password_shares_total; i++) {
|
|
clearScreen();
|
|
|
|
printf("Press <enter> 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 <enter> 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 *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;
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
|
if (!EVP_EncryptUpdate(ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share))) {
|
|
fprintf(stderr, "Error encrypting DKEK share\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!EVP_EncryptFinal_ex(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(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) {
|
|
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 nlc = 0;
|
|
u8 *ptr;
|
|
|
|
if (inlen > 127) {
|
|
do {
|
|
nlc++;
|
|
} while (inlen >= (unsigned)(1 << (nlc << 3)));
|
|
}
|
|
|
|
*outlen = 2 + nlc + inlen;
|
|
ptr = malloc(*outlen);
|
|
if (ptr == NULL) {
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
*outdata = ptr;
|
|
*ptr++ = tag;
|
|
|
|
if (nlc) {
|
|
*ptr++ = 0x80 | nlc;
|
|
while (nlc--) {
|
|
*ptr++ = (inlen >> (nlc << 3)) & 0xFF;
|
|
}
|
|
} else {
|
|
*ptr++ = inlen & 0x7F;
|
|
}
|
|
|
|
memcpy(ptr, indata, inlen);
|
|
return SC_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static int wrap_key(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;
|
|
wrap_with_tag(0x04, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length,
|
|
&key, &key_len);
|
|
|
|
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
|
|
wrap_with_tag(0x30, keyblob, ptr - keyblob, &key, &key_len);
|
|
|
|
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) {
|
|
r = 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;
|
|
}
|
|
|
|
if ((keybloblen = fread(keyblob, 1, sizeof(keyblob), in)) < 0) {
|
|
perror(inf);
|
|
return -1;
|
|
}
|
|
|
|
fclose(in);
|
|
|
|
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) ||
|
|
((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 * const 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;
|
|
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;
|
|
|
|
setbuf(stderr, NULL);
|
|
setbuf(stdout, 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_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)
|
|
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);
|
|
}
|
|
|
|
/* Only change if not in opensc.conf */
|
|
if (verbose > 1 && ctx->debug == 0) {
|
|
ctx->debug = verbose;
|
|
sc_ctx_log_to_file(ctx, "stderr");
|
|
}
|
|
|
|
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_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(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:
|
|
if (card) {
|
|
sc_disconnect_card(card);
|
|
}
|
|
if (ctx)
|
|
sc_release_context(ctx);
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
return err;
|
|
}
|