sc-hsm-tool: Added support for DKEK password sharing scheme

This commit is contained in:
Frank Thater 2013-02-06 16:47:08 +01:00 committed by Andreas Schwier
parent 99af6cd8ee
commit 0577f7d4f5
1 changed files with 607 additions and 56 deletions

View File

@ -35,7 +35,8 @@
#include <openssl/opensslconf.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/rand.h>
#include "libopensc/opensc.h"
#include "libopensc/cardctl.h"
@ -63,25 +64,29 @@ enum {
OPT_SO_PIN = 0x100,
OPT_PIN,
OPT_RETRY,
OPT_PASSWORD
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' },
{ "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 },
{ "key-reference", 1, NULL, 'i' },
{ "force", 0, NULL, 'f' },
{ "reader", 1, NULL, 'r' },
{ "wait", 0, NULL, 'w' },
{ "verbose", 0, NULL, 'v' },
{ "initialize", 0, NULL, 'X' },
{ "create-dkek-share", 1, NULL, 'C' },
{ "import-dkek-share", 1, NULL, 'I' },
{ "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' },
{ "force", 0, NULL, 'f' },
{ "reader", 1, NULL, 'r' },
{ "wait", 0, NULL, 'w' },
{ "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
@ -96,6 +101,8 @@ static const char *option_help[] = {
"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",
"Force replacement of key and certificate",
"Uses reader number <arg> [0]",
@ -103,6 +110,10 @@ static const char *option_help[] = {
"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;
@ -110,6 +121,311 @@ static sc_card_t *card = NULL;
/**
* Generate a prime number
*
* The internal CPRNG is seeded using the provided seed value.
* For the bit size of the generated prime the following condition holds:
*
* num_bits(prime) > max(2^r, num_bits(n + 1))
*
* r equals the number of bits needed to encode the secret.
*
* @param prime Pointer for storage of prime number
* @param s Secret to share
* @param n Maximum number of shares
* @param rngSeed Seed value for CPRNG
*
*/
static void generatePrime(BIGNUM *prime, const BIGNUM *s, const unsigned int n, char *rngSeed) {
int bits = 0;
// Seed the RNG
RAND_seed(rngSeed, sizeof(rngSeed));
// Determine minimum number of bits for prime >= max(2^r, n + 1)
bits = BN_num_bits_word(n + 1) > BN_num_bits(s) ? (BN_num_bits_word(n + 1)) : (BN_num_bits(s));
// Clear the prime value
BN_clear(prime);
// Generate random prime
BN_generate_prime(prime, bits, 1, NULL, NULL, NULL, NULL );
}
/**
* 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();
BN_CTX_init(ctx);
BN_init(&temp);
BN_init(&exponent);
// 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;
// Set the secret value as the constant part of the polynomial
pp = polynomial;
*pp = BN_new();
BN_init(*pp);
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_init(*pp);
BN_rand_range(*pp, &prime);
pp++;
}
sp = shares;
// Now calculate n secret shares
for (i = 1; i <= n; i++) {
BN_init(&(sp->x));
BN_init(&(sp->y));
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;
// Initialize
pbValue = bValue;
for (i = 0; i < t; i++) {
*pbValue = BN_new();
BN_init(*pbValue);
pbValue++;
}
BN_init(&numerator);
BN_init(&denominator);
BN_init(&temp);
// Create context for temporary variables of engine
ctx = BN_CTX_new();
BN_CTX_init(ctx);
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 ) {
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" )) system( "cls" );
}
void waitForEnterKeyPressed() {
char 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) {
@ -248,14 +564,103 @@ static void initialize(sc_card_t *card, const char *so_pin, const char *user_pin
static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *password)
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];
char bin[64];
int binlen = 0;
char *ip;
secret_share_t *shares = NULL;
secret_share_t *sp;
/*
* Initialize prime and secret
*/
BN_init(&prime);
BN_init(&secret);
// Allocate data buffer for the shares
shares = malloc(num_of_password_shares * sizeof(secret_share_t));
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));
fgets(inbuf, sizeof(inbuf), stdin);
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();
BN_init(&(sp->x));
BN_init(&(sp->y));
printf("Share %i of %i\n\n", i + 1, num_of_password_shares);
printf("Please enter share ID: ");
memset(inbuf, 0, sizeof(inbuf));
fgets(inbuf, sizeof(inbuf), stdin);
p = &(sp->x);
BN_hex2bn(&p, inbuf);
printf("Please enter share value: ");
memset(inbuf, 0, sizeof(inbuf));
fgets(inbuf, sizeof(inbuf), stdin);
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");
return r;
}
/*
* Encode the secret value
*/
ip = inbuf;
*pwdlen = BN_bn2bin(&secret, ip);
*pwd = calloc(1, *pwdlen);
memcpy(*pwd, ip, *pwdlen);
cleanUpShares(shares, num_of_password_shares);
BN_clear_free(&prime);
BN_clear_free(&secret);
return 0;
}
static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *password, int num_of_password_shares)
{
sc_cardctl_sc_hsm_dkek_t dkekinfo;
EVP_CIPHER_CTX ctx;
FILE *in = NULL;
u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64];
char *pwd = NULL;
int r, outlen;
int r, outlen, pwdlen;
in = fopen(inf, "rb");
@ -277,15 +682,26 @@ static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *
}
if (password == NULL) {
printf("Enter password to decrypt DKEK share : ");
util_getpass(&pwd, NULL, stdin);
printf("\n");
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;
}
}
} else {
pwd = password;
pwdlen = strlen(password);
}
printf("Deciphering DKEK share, please wait...\n");
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv);
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv);
OPENSSL_cleanse(pwd, strlen(pwd));
if (password == NULL) {
@ -316,6 +732,7 @@ static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *
EVP_CIPHER_CTX_cleanup(&ctx);
if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares
printf("Not supported by card or card not initialized for key share usage\n");
return;
}
@ -329,46 +746,172 @@ static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *
static void create_dkek_share(sc_card_t *card, const char *outf, int iter, char *password)
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;
char buf[64];
char hex[64];
int l;
secret_share_t *shares = NULL;
secret_share_t *sp;
u8 rngseed[16];
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, *pwd, 8);
if (r < 0) {
printf("Error generating random key failed with ", sc_strerror(r));
OPENSSL_cleanse(pwd, *pwdlen);
free(pwd);
return r;
}
**pwd |= 0x80;
/*
* Initialize prime and secret
*/
BN_init(&prime);
BN_init(&secret);
/*
* Encode the secret value
*/
BN_bin2bn(*pwd, *pwdlen, &secret);
/*
* Generate seed and calculate a prime depending on the size of the secret
*/
r = sc_get_challenge(card, rngseed, 16);
if (r < 0) {
printf("Error generating random seed failed with ", sc_strerror(r));
OPENSSL_cleanse(pwd, *pwdlen);
free(pwd);
return r;
}
generatePrime(&prime, &secret, password_shares_total, rngseed);
// 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 void create_dkek_share(sc_card_t *card, const char *outf, int iter, char *password, int password_shares_threshold, int password_shares_total)
{
EVP_CIPHER_CTX ctx;
FILE *out = NULL;
u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64];
u8 filebuff[64], key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64];
u8 dkek_share[32];
char *pwd = NULL;
int r, outlen;
int r = 0, outlen, pwdlen = 0;
if (password == NULL) {
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 save 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;
}
break;
if (password_shares_threshold == -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);
}
OPENSSL_cleanse(refpwd, strlen(refpwd));
free(refpwd);
} else {
pwd = password;
pwdlen = strlen(password);
}
if (r < 0) {
printf("Creating DKEK share failed");
return;
}
memcpy(filebuff, magic, sizeof(magic) - 1);
@ -380,10 +923,10 @@ static void create_dkek_share(sc_card_t *card, const char *outf, int iter, char
}
printf("Enciphering DKEK share, please wait...\n");
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv);
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv);
if (password == NULL) {
OPENSSL_cleanse(pwd, strlen(pwd));
OPENSSL_cleanse(pwd, pwdlen);
free(pwd);
}
@ -805,6 +1348,8 @@ int main(int argc, char * const argv[])
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;
@ -855,6 +1400,12 @@ int main(int argc, char * const argv[])
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;
@ -914,11 +1465,11 @@ int main(int argc, char * const argv[])
}
if (do_create_dkek_share) {
create_dkek_share(card, opt_filename, opt_iter, opt_password);
create_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_threshold, opt_password_shares_total);
}
if (do_import_dkek_share) {
import_dkek_share(card, opt_filename, opt_iter, opt_password);
import_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_total);
}
if (do_wrap_key) {