add a test-decipher command
test-decipher will for rsa do public encrypt on a random string and let the key decrypt for ec it will generate a new ec key and do ecdh and confirm it gets the same answer back
This commit is contained in:
+32
-7
@@ -498,11 +498,10 @@ ykpiv_rc ykpiv_hex_decode(const char *hex_in, size_t in_len,
|
|||||||
return YKPIV_OK;
|
return YKPIV_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
|
static ykpiv_rc _general_authenticate(ykpiv_state *state,
|
||||||
const unsigned char *raw_in, size_t in_len,
|
const unsigned char *raw_in, size_t in_len,
|
||||||
unsigned char *sign_out, size_t *out_len,
|
unsigned char *out, size_t *out_len,
|
||||||
unsigned char algorithm, unsigned char key) {
|
unsigned char algorithm, unsigned char key, bool decipher) {
|
||||||
|
|
||||||
unsigned char indata[1024];
|
unsigned char indata[1024];
|
||||||
unsigned char *dataptr = indata;
|
unsigned char *dataptr = indata;
|
||||||
unsigned char data[1024];
|
unsigned char data[1024];
|
||||||
@@ -522,14 +521,23 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
|
|||||||
if(pad_len == 0) {
|
if(pad_len == 0) {
|
||||||
pad_len = 256;
|
pad_len = 256;
|
||||||
}
|
}
|
||||||
|
if(!decipher) {
|
||||||
if(in_len + RSA_PKCS1_PADDING_SIZE > pad_len) {
|
if(in_len + RSA_PKCS1_PADDING_SIZE > pad_len) {
|
||||||
return YKPIV_SIZE_ERROR;
|
return YKPIV_SIZE_ERROR;
|
||||||
}
|
}
|
||||||
RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len);
|
RSA_padding_add_PKCS1_type_1(sign_in, pad_len, raw_in, in_len);
|
||||||
in_len = pad_len;
|
in_len = pad_len;
|
||||||
|
} else {
|
||||||
|
if(in_len != pad_len) {
|
||||||
|
return YKPIV_SIZE_ERROR;
|
||||||
|
}
|
||||||
|
memcpy(sign_in, raw_in, in_len);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case YKPIV_ALGO_ECCP256:
|
case YKPIV_ALGO_ECCP256:
|
||||||
if(in_len > 32) {
|
if(!decipher && in_len > 32) {
|
||||||
|
return YKPIV_SIZE_ERROR;
|
||||||
|
} else if(decipher && in_len != 65) {
|
||||||
return YKPIV_SIZE_ERROR;
|
return YKPIV_SIZE_ERROR;
|
||||||
}
|
}
|
||||||
memcpy(sign_in, raw_in, in_len);
|
memcpy(sign_in, raw_in, in_len);
|
||||||
@@ -550,7 +558,7 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
|
|||||||
dataptr += set_length(dataptr, in_len + bytes + 3);
|
dataptr += set_length(dataptr, in_len + bytes + 3);
|
||||||
*dataptr++ = 0x82;
|
*dataptr++ = 0x82;
|
||||||
*dataptr++ = 0x00;
|
*dataptr++ = 0x00;
|
||||||
*dataptr++ = 0x81;
|
*dataptr++ = algorithm == YKPIV_ALGO_ECCP256 && decipher ? 0x85 : 0x81;
|
||||||
dataptr += set_length(dataptr, in_len);
|
dataptr += set_length(dataptr, in_len);
|
||||||
memcpy(dataptr, sign_in, (size_t)in_len);
|
memcpy(dataptr, sign_in, (size_t)in_len);
|
||||||
dataptr += in_len;
|
dataptr += in_len;
|
||||||
@@ -592,10 +600,27 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
|
|||||||
return YKPIV_SIZE_ERROR;
|
return YKPIV_SIZE_ERROR;
|
||||||
}
|
}
|
||||||
*out_len = len;
|
*out_len = len;
|
||||||
memcpy(sign_out, dataptr, len);
|
memcpy(out, dataptr, len);
|
||||||
return YKPIV_OK;
|
return YKPIV_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
|
||||||
|
const unsigned char *raw_in, size_t in_len,
|
||||||
|
unsigned char *sign_out, size_t *out_len,
|
||||||
|
unsigned char algorithm, unsigned char key) {
|
||||||
|
|
||||||
|
return _general_authenticate(state, raw_in, in_len, sign_out, out_len,
|
||||||
|
algorithm, key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *in,
|
||||||
|
size_t in_len, unsigned char *out, size_t *out_len,
|
||||||
|
unsigned char algorithm, unsigned char key) {
|
||||||
|
return _general_authenticate(state, in, in_len, out, out_len,
|
||||||
|
algorithm, key, true);
|
||||||
|
}
|
||||||
|
|
||||||
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
|
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
|
||||||
APDU apdu;
|
APDU apdu;
|
||||||
unsigned char data[0xff];
|
unsigned char data[0xff];
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ extern "C"
|
|||||||
ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in,
|
ykpiv_rc ykpiv_sign_data(ykpiv_state *state, const unsigned char *sign_in,
|
||||||
size_t in_len,unsigned char *sign_out, size_t *out_len,
|
size_t in_len,unsigned char *sign_out, size_t *out_len,
|
||||||
unsigned char algorithm, unsigned char key);
|
unsigned char algorithm, unsigned char key);
|
||||||
|
ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *enc_in,
|
||||||
|
size_t in_len, unsigned char *enc_out, size_t *out_len,
|
||||||
|
unsigned char algorithm, unsigned char key);
|
||||||
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len);
|
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len);
|
||||||
ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries);
|
ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries);
|
||||||
ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id,
|
ykpiv_rc ykpiv_fetch_object(ykpiv_state *state, int object_id,
|
||||||
|
|||||||
@@ -47,3 +47,9 @@ global:
|
|||||||
local:
|
local:
|
||||||
*;
|
*;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
YKPIV_0.2.0
|
||||||
|
{
|
||||||
|
global:
|
||||||
|
ykpiv_decipher_data;
|
||||||
|
} YKPIV_0.1.0;
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ option "action" a "Action to take" values="version","generate","set-mgm-key",
|
|||||||
"reset","pin-retries","import-key","import-certificate","set-chuid",
|
"reset","pin-retries","import-key","import-certificate","set-chuid",
|
||||||
"request-certificate","verify-pin","change-pin","change-puk","unblock-pin",
|
"request-certificate","verify-pin","change-pin","change-puk","unblock-pin",
|
||||||
"selfsign-certificate","delete-certificate","read-certificate","status",
|
"selfsign-certificate","delete-certificate","read-certificate","status",
|
||||||
"test-signature" enum multiple
|
"test-signature","test-decipher" enum multiple
|
||||||
text "
|
text "
|
||||||
Multiple actions may be given at once and will be executed in order
|
Multiple actions may be given at once and will be executed in order
|
||||||
for example --action=verify-pin --action=request-certificate\n"
|
for example --action=verify-pin --action=request-certificate\n"
|
||||||
|
|||||||
@@ -1484,6 +1484,137 @@ test_out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool test_decipher(ykpiv_state *state, enum enum_slot slot,
|
||||||
|
const char *input_file_name, enum enum_key_format cert_format, int verbose) {
|
||||||
|
bool ret = false;
|
||||||
|
X509 *x509 = NULL;
|
||||||
|
EVP_PKEY *pubkey;
|
||||||
|
EC_KEY *tmpkey = NULL;
|
||||||
|
FILE *input_file = open_file(input_file_name, INPUT);
|
||||||
|
|
||||||
|
if(!input_file) {
|
||||||
|
fprintf(stderr, "Failed opening input file %s.\n", input_file_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isatty(fileno(input_file))) {
|
||||||
|
fprintf(stderr, "Please paste the certificate to encrypt for...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cert_format == key_format_arg_PEM) {
|
||||||
|
x509 = PEM_read_X509(input_file, NULL, NULL, NULL);
|
||||||
|
} else if(cert_format == key_format_arg_DER) {
|
||||||
|
x509 = d2i_X509_fp(input_file, NULL);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Only PEM or DER format is supported for test-decipher.\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
if(!x509) {
|
||||||
|
fprintf(stderr, "Failed loading certificate for test-decipher.\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int key = 0;
|
||||||
|
unsigned char algorithm;
|
||||||
|
|
||||||
|
pubkey = X509_get_pubkey(x509);
|
||||||
|
if(!pubkey) {
|
||||||
|
fprintf(stderr, "Parse error.\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
algorithm = get_algorithm(pubkey);
|
||||||
|
if(algorithm == 0) {
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
sscanf(cmdline_parser_slot_values[slot], "%2x", &key);
|
||||||
|
if(algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) {
|
||||||
|
unsigned char secret[32];
|
||||||
|
unsigned char secret2[32];
|
||||||
|
unsigned char data[256];
|
||||||
|
int len;
|
||||||
|
size_t len2 = sizeof(data);
|
||||||
|
RSA *rsa = EVP_PKEY_get1_RSA(pubkey);
|
||||||
|
|
||||||
|
if(RAND_pseudo_bytes(secret, sizeof(secret)) == -1) {
|
||||||
|
fprintf(stderr, "error: no randomness.\n");
|
||||||
|
ret = false;
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSA_padding_add_PKCS1_type_1(data, RSA_size(rsa), secret, sizeof(secret));
|
||||||
|
len = RSA_public_encrypt(RSA_size(rsa), data, data, rsa, RSA_NO_PADDING);
|
||||||
|
if(len < 0) {
|
||||||
|
fprintf(stderr, "Failed performing RSA encryption!\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
if(ykpiv_decipher_data(state, data, (size_t)len, data, &len2, algorithm, key) != YKPIV_OK) {
|
||||||
|
fprintf(stderr, "RSA decrypt failed!\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
/* for some reason we have to give the padding check function data + 1 */
|
||||||
|
len = RSA_padding_check_PKCS1_type_1(secret2, sizeof(secret2), data + 1, len2 - 1, RSA_size(rsa));
|
||||||
|
if(len == sizeof(secret)) {
|
||||||
|
if(verbose) {
|
||||||
|
fprintf(stderr, "Generated nonce: ");
|
||||||
|
dump_hex(secret, sizeof(secret), stderr, true);
|
||||||
|
fprintf(stderr, "Decrypted nonce: ");
|
||||||
|
dump_hex(secret2, sizeof(secret2), stderr, true);
|
||||||
|
}
|
||||||
|
if(memcmp(secret, secret2, sizeof(secret)) == 0) {
|
||||||
|
fprintf(stderr, "Successfully performed RSA decryption!\n");
|
||||||
|
ret = true;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Failed performing RSA decryption!\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Failed unwrapping PKCS1 envelope.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned char secret[32];
|
||||||
|
unsigned char secret2[32];
|
||||||
|
unsigned char public_key[65];
|
||||||
|
unsigned char *ptr = public_key;
|
||||||
|
size_t len = sizeof(secret);
|
||||||
|
EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pubkey);
|
||||||
|
|
||||||
|
tmpkey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||||
|
EC_KEY_generate_key(tmpkey);
|
||||||
|
ECDH_compute_key(secret, len, EC_KEY_get0_public_key(ec), tmpkey, NULL);
|
||||||
|
|
||||||
|
i2o_ECPublicKey(tmpkey, &ptr);
|
||||||
|
if(ykpiv_decipher_data(state, public_key, sizeof(public_key), secret2, &len, algorithm, key) != YKPIV_OK) {
|
||||||
|
fprintf(stderr, "Failed ECDH exchange!\n");
|
||||||
|
goto decipher_out;
|
||||||
|
}
|
||||||
|
if(verbose) {
|
||||||
|
fprintf(stderr, "ECDH host generated: ");
|
||||||
|
dump_hex(secret, len, stderr, true);
|
||||||
|
fprintf(stderr, "ECDH card generated: ");
|
||||||
|
dump_hex(secret2, len, stderr, true);
|
||||||
|
}
|
||||||
|
if(memcmp(secret, secret2, 32) == 0) {
|
||||||
|
fprintf(stderr, "Successfully performed ECDH exchange with card.\n");
|
||||||
|
ret = true;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "ECDH exchange with card failed!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decipher_out:
|
||||||
|
if(tmpkey) {
|
||||||
|
EC_KEY_free(tmpkey);
|
||||||
|
}
|
||||||
|
if(x509) {
|
||||||
|
X509_free(x509);
|
||||||
|
}
|
||||||
|
if(input_file != stdin) {
|
||||||
|
fclose(input_file);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
struct gengetopt_args_info args_info;
|
struct gengetopt_args_info args_info;
|
||||||
ykpiv_state *state;
|
ykpiv_state *state;
|
||||||
@@ -1514,6 +1645,7 @@ int main(int argc, char *argv[]) {
|
|||||||
case action_arg_deleteMINUS_certificate:
|
case action_arg_deleteMINUS_certificate:
|
||||||
case action_arg_readMINUS_certificate:
|
case action_arg_readMINUS_certificate:
|
||||||
case action_arg_testMINUS_signature:
|
case action_arg_testMINUS_signature:
|
||||||
|
case action_arg_testMINUS_decipher:
|
||||||
if(args_info.slot_arg == slot__NULL) {
|
if(args_info.slot_arg == slot__NULL) {
|
||||||
fprintf(stderr, "The '%s' action needs a slot (-s) to operate on.\n",
|
fprintf(stderr, "The '%s' action needs a slot (-s) to operate on.\n",
|
||||||
cmdline_parser_action_values[action]);
|
cmdline_parser_action_values[action]);
|
||||||
@@ -1596,6 +1728,7 @@ int main(int argc, char *argv[]) {
|
|||||||
case action_arg_readMINUS_certificate:
|
case action_arg_readMINUS_certificate:
|
||||||
case action_arg_status:
|
case action_arg_status:
|
||||||
case action_arg_testMINUS_signature:
|
case action_arg_testMINUS_signature:
|
||||||
|
case action_arg_testMINUS_decipher:
|
||||||
case action__NULL:
|
case action__NULL:
|
||||||
default:
|
default:
|
||||||
if(verbosity) {
|
if(verbosity) {
|
||||||
@@ -1758,6 +1891,12 @@ int main(int argc, char *argv[]) {
|
|||||||
ret = EXIT_FAILURE;
|
ret = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case action_arg_testMINUS_decipher:
|
||||||
|
if(test_decipher(state, args_info.slot_arg, args_info.input_arg,
|
||||||
|
args_info.key_format_arg, verbosity) == false) {
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case action__NULL:
|
case action__NULL:
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Wrong action. %d.\n", action);
|
fprintf(stderr, "Wrong action. %d.\n", action);
|
||||||
|
|||||||
Reference in New Issue
Block a user