Add integration test for PIN cache

This commit is contained in:
Trevor Bentley
2017-11-03 13:39:23 +01:00
parent 7818b49e7d
commit c6abe7ac6d
+107 -93
View File
@@ -249,7 +249,8 @@ static const char *certificate_pem =
"W4YXbzGZb8qdT27qIZaHD638tL6liLkI6UE4KCXH8X8e3fqdbmqvwrq403nOGmsP\n" "W4YXbzGZb8qdT27qIZaHD638tL6liLkI6UE4KCXH8X8e3fqdbmqvwrq403nOGmsP\n"
"cbJb2PEXibNEQG234riKxm7x7vNDLL79Jwtc\n" "cbJb2PEXibNEQG234riKxm7x7vNDLL79Jwtc\n"
"-----END CERTIFICATE-----\n"; "-----END CERTIFICATE-----\n";
bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
static bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
int real_len = BN_num_bytes(bn); int real_len = BN_num_bytes(bn);
if(real_len > element_len) { if(real_len > element_len) {
@@ -261,7 +262,8 @@ bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) {
return true; return true;
} }
bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigned char *out, unsigned int *out_len, int nid) {
static bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigned char *out, unsigned int *out_len, int nid) {
X509_SIG digestInfo; X509_SIG digestInfo;
X509_ALGOR algor; X509_ALGOR algor;
ASN1_TYPE parameter; ASN1_TYPE parameter;
@@ -282,11 +284,10 @@ bool prepare_rsa_signature(const unsigned char *in, unsigned int in_len, unsigne
return true; return true;
} }
START_TEST(test_import_key) { static void import_key(unsigned char slot, unsigned char pin_policy) {
ykpiv_rc res; ykpiv_rc res;
{ {
unsigned char pp = YKPIV_PINPOLICY_DEFAULT; unsigned char pp = pin_policy;
unsigned char tp = YKPIV_TOUCHPOLICY_DEFAULT; unsigned char tp = YKPIV_TOUCHPOLICY_DEFAULT;
EVP_PKEY *private_key = NULL; EVP_PKEY *private_key = NULL;
BIO *bio = NULL; BIO *bio = NULL;
@@ -314,7 +315,7 @@ START_TEST(test_import_key) {
// Try wrong algorithm, fail. // Try wrong algorithm, fail.
res = ykpiv_import_private_key(g_state, res = ykpiv_import_private_key(g_state,
0x9e, slot,
YKPIV_ALGO_RSA1024, YKPIV_ALGO_RSA1024,
p, element_len, p, element_len,
q, element_len, q, element_len,
@@ -327,7 +328,7 @@ START_TEST(test_import_key) {
// Try right algorithm // Try right algorithm
res = ykpiv_import_private_key(g_state, res = ykpiv_import_private_key(g_state,
0x9e, slot,
YKPIV_ALGO_RSA2048, YKPIV_ALGO_RSA2048,
p, element_len, p, element_len,
q, element_len, q, element_len,
@@ -340,6 +341,44 @@ START_TEST(test_import_key) {
EVP_PKEY_free(private_key); EVP_PKEY_free(private_key);
} }
// Use imported key to decrypt a thing. See that it works.
{
BIO *bio = NULL;
X509 *cert = NULL;
EVP_PKEY *pub_key = NULL;
unsigned char secret[32];
unsigned char secret2[32];
unsigned char data[256];
int len;
size_t len2 = sizeof(data);
RSA *rsa = NULL;
bio = BIO_new_mem_buf(certificate_pem, strlen(certificate_pem));
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(cert);
BIO_free(bio);
pub_key = X509_get_pubkey(cert);
ck_assert_ptr_nonnull(pub_key);
rsa = EVP_PKEY_get1_RSA(pub_key);
ck_assert_ptr_nonnull(rsa);
ck_assert_int_gt(RAND_pseudo_bytes(secret, sizeof(secret)), 0);
len = RSA_public_encrypt(sizeof(secret), secret, data, rsa, RSA_PKCS1_PADDING);
ck_assert_int_ge(len, 0);
res = ykpiv_verify(g_state, "123456", NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, slot);
ck_assert_int_eq(res, YKPIV_OK);
len = RSA_padding_check_PKCS1_type_2(secret2, sizeof(secret2), data + 1, len2 - 1, RSA_size(rsa));
ck_assert_int_eq(len, sizeof(secret));
ck_assert_int_eq(memcmp(secret, secret2, sizeof(secret)), 0);
X509_free(cert);
}
}
START_TEST(test_import_key) {
ykpiv_rc res;
import_key(0x9a, YKPIV_PINPOLICY_DEFAULT);
// Verify certificate // Verify certificate
{ {
BIO *bio = NULL; BIO *bio = NULL;
@@ -377,7 +416,7 @@ START_TEST(test_import_key) {
prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md)); prepare_rsa_signature(data, data_len, encoded, &enc_len, EVP_MD_type(md));
ck_assert_int_ne(RSA_padding_add_PKCS1_type_1(signinput, padlen, encoded, enc_len), 0); ck_assert_int_ne(RSA_padding_add_PKCS1_type_1(signinput, padlen, encoded, enc_len), 0);
res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e); res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK); ck_assert_int_eq(res, YKPIV_OK);
ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1); ck_assert_int_eq(RSA_verify(EVP_MD_type(md), data, data_len, signature, sig_len, rsa), 1);
@@ -385,43 +424,13 @@ START_TEST(test_import_key) {
X509_free(cert); X509_free(cert);
} }
// Use imported key to decrypt a thing. See that it works.
{
BIO *bio = NULL;
X509 *cert = NULL;
EVP_PKEY *pub_key = NULL;
unsigned char secret[32];
unsigned char secret2[32];
unsigned char data[256];
int len;
size_t len2 = sizeof(data);
RSA *rsa = NULL;
bio = BIO_new_mem_buf(certificate_pem, strlen(certificate_pem));
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(cert);
BIO_free(bio);
pub_key = X509_get_pubkey(cert);
ck_assert_ptr_nonnull(pub_key);
rsa = EVP_PKEY_get1_RSA(pub_key);
ck_assert_ptr_nonnull(rsa);
ck_assert_int_gt(RAND_pseudo_bytes(secret, sizeof(secret)), 0);
len = RSA_public_encrypt(sizeof(secret), secret, data, rsa, RSA_PKCS1_PADDING);
ck_assert_int_ge(len, 0);
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9e);
ck_assert_int_eq(res, YKPIV_OK);
len = RSA_padding_check_PKCS1_type_2(secret2, sizeof(secret2), data + 1, len2 - 1, RSA_size(rsa));
ck_assert_int_eq(len, sizeof(secret));
ck_assert_int_eq(memcmp(secret, secret2, sizeof(secret)), 0);
X509_free(cert);
}
// Verify that imported key can not be attested // Verify that imported key can not be attested
{ {
unsigned char attest[2048]; unsigned char attest[2048];
size_t attest_len = sizeof(attest); size_t attest_len = sizeof(attest);
ykpiv_devmodel model; ykpiv_devmodel model;
model = ykpiv_util_devicemodel(g_state); model = ykpiv_util_devicemodel(g_state);
res = ykpiv_attest(g_state, 0x9e, attest, &attest_len); res = ykpiv_attest(g_state, 0x9a, attest, &attest_len);
if (model == DEVTYPE_YK4) { if (model == DEVTYPE_YK4) {
ck_assert_int_eq(res, YKPIV_GENERIC_ERROR); ck_assert_int_eq(res, YKPIV_GENERIC_ERROR);
} }
@@ -445,60 +454,7 @@ START_TEST(test_pin_policy_always) {
} }
} }
{ import_key(0x9e, YKPIV_PINPOLICY_ALWAYS);
unsigned char pp = YKPIV_PINPOLICY_ALWAYS;
unsigned char tp = YKPIV_TOUCHPOLICY_DEFAULT;
EVP_PKEY *private_key = NULL;
BIO *bio = NULL;
RSA *rsa_private_key = NULL;
unsigned char e[4];
unsigned char p[128];
unsigned char q[128];
unsigned char dmp1[128];
unsigned char dmq1[128];
unsigned char iqmp[128];
int element_len = 128;
bio = BIO_new_mem_buf(private_key_pem, strlen(private_key_pem));
private_key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
ck_assert_ptr_nonnull(private_key);
BIO_free(bio);
rsa_private_key = EVP_PKEY_get1_RSA(private_key);
ck_assert_ptr_nonnull(rsa_private_key);
ck_assert(set_component(e, rsa_private_key->e, 3));
ck_assert(set_component(p, rsa_private_key->p, element_len));
ck_assert(set_component(q, rsa_private_key->q, element_len));
ck_assert(set_component(dmp1, rsa_private_key->dmp1, element_len));
ck_assert(set_component(dmq1, rsa_private_key->dmq1, element_len));
ck_assert(set_component(iqmp, rsa_private_key->iqmp, element_len));
// Try wrong algorithm, fail.
res = ykpiv_import_private_key(g_state,
0x9e,
YKPIV_ALGO_RSA1024,
p, element_len,
q, element_len,
dmp1, element_len,
dmq1, element_len,
iqmp, element_len,
NULL, 0,
pp, tp);
ck_assert_int_eq(res, YKPIV_ALGORITHM_ERROR);
// Try right algorithm
res = ykpiv_import_private_key(g_state,
0x9e,
YKPIV_ALGO_RSA2048,
p, element_len,
q, element_len,
dmp1, element_len,
dmq1, element_len,
iqmp, element_len,
NULL, 0,
pp, tp);
ck_assert_int_eq(res, YKPIV_OK);
EVP_PKEY_free(private_key);
}
// Verify certificate // Verify certificate
{ {
@@ -896,6 +852,63 @@ START_TEST(test_allocator) {
} }
END_TEST END_TEST
START_TEST(test_pin_cache) {
ykpiv_rc res;
ykpiv_state *local_state;
unsigned char data[256];
int len = sizeof(data);
size_t len2 = sizeof(data);
import_key(0x9a, YKPIV_PINPOLICY_DEFAULT);
// Disconnect and reconnect to device to guarantee it is not authed
res = ykpiv_disconnect(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(g_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_init(&g_state, true);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(g_state);
res = ykpiv_connect(g_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
// Verify decryption does not work without auth
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_AUTHENTICATION_ERROR);
// Verify decryption does work when authed
res = ykpiv_verify_select(g_state, "123456", 6, NULL, true);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
// Verify PIN policy allows continuing to decrypt without re-verifying
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
// Create a new ykpiv state, connect, and close it.
// This forces a card reset from another context, so the original global
// context will require a reconnect for its next transaction.
res = ykpiv_init(&local_state, true);
ck_assert_int_eq(res, YKPIV_OK);
ck_assert_ptr_nonnull(local_state);
res = ykpiv_connect(local_state, NULL);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_disconnect(local_state);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_done(local_state);
ck_assert_int_eq(res, YKPIV_OK);
// Verify we are still authenticated on the global context. This will
// require an automatic reconnect and re-verify with the cached PIN.
//
// Note that you can verify that this fails by rebuilding with
// DISABLE_PIN_CACHE set to 1.
res = ykpiv_decipher_data(g_state, data, (size_t)len, data, &len2, YKPIV_ALGO_RSA2048, 0x9a);
ck_assert_int_eq(res, YKPIV_OK);
}
END_TEST
int destruction_confirmed(void) { int destruction_confirmed(void) {
char *confirmed = getenv("YKPIV_ENV_HWTESTS_CONFIRMED"); char *confirmed = getenv("YKPIV_ENV_HWTESTS_CONFIRMED");
if (confirmed && confirmed[0] == '1') if (confirmed && confirmed[0] == '1')
@@ -930,6 +943,7 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_import_key); tcase_add_test(tc, test_import_key);
tcase_add_test(tc, test_pin_policy_always); tcase_add_test(tc, test_pin_policy_always);
tcase_add_test(tc, test_generate_key); tcase_add_test(tc, test_generate_key);
tcase_add_test(tc, test_pin_cache);
// Must be last: tear down and re-test with custom memory allocator // Must be last: tear down and re-test with custom memory allocator
tcase_add_test(tc, test_allocator); tcase_add_test(tc, test_allocator);