diff --git a/.gitignore b/.gitignore index 6312246..accae11 100644 --- a/.gitignore +++ b/.gitignore @@ -43,12 +43,19 @@ lib/tests/parse_key lib/tests/parse_key.log lib/tests/parse_key.o lib/tests/parse_key.trs +lib/tests/util +lib/tests/util.log +lib/tests/util.o +lib/tests/util.trs lib/tests/test-suite.log lib/error.lo lib/error.o lib/libykpiv.la lib/version.lo lib/version.o +lib/util.la +lib/util.lo +lib/util.o lib/ykpiv-version.h lib/ykpiv.lo lib/ykpiv.o @@ -97,3 +104,9 @@ ykcs11/tests/ykcs11_tests.o ykcs11/tests/ykcs11_tests.trs ykcs11/tests/test-suite.log yubico-piv-tool.1.txt + +*.plist +.libs +GPATH +GRTAGS +GTAGS diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am index 29627af..fb826a1 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -25,11 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@ -AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CFLAGS = $(WARN_CFLAGS) @CHECK_CFLAGS@ $(OPENSSL_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_builddir)/lib $(OPENSSL_CFLAGS) AM_LDFLAGS = -no-install @CHECK_LIBS@ -LDADD = ../libykpiv.la +LDADD = ../libykpiv.la $(OPENSSL_LIBS) check_PROGRAMS = basic parse_key util TESTS = $(check_PROGRAMS) diff --git a/lib/tests/util.c b/lib/tests/util.c index de9e756..11b2dd8 100644 --- a/lib/tests/util.c +++ b/lib/tests/util.c @@ -79,10 +79,32 @@ void teardown(void) { } START_TEST(test_devicemodel) { + ykpiv_rc res; ykpiv_devmodel model; + char version[256]; + char reader_buf[2048]; + size_t num_readers = sizeof(reader_buf); + + res = ykpiv_get_version(g_state, version, sizeof(version)); + ck_assert_int_eq(res, YKPIV_OK); + fprintf(stderr, "Version: %s\n", version); model = ykpiv_util_devicemodel(g_state); fprintf(stdout, "Model: %u\n", model); - ck_assert(model == DEVTYPE_YK4); + ck_assert(model == DEVTYPE_YK4 || model == DEVTYPE_NEOr3); + + res = ykpiv_list_readers(g_state, reader_buf, &num_readers); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_gt(num_readers, 0); + if (model == DEVTYPE_YK4) { + ck_assert_ptr_nonnull(strnstr(reader_buf, "Yubikey 4", strlen(reader_buf))); + ck_assert(version[0] == '4'); // Verify app version 4.x + ck_assert(version[1] == '.'); + } + else { + ck_assert_ptr_nonnull(strnstr(reader_buf, "Yubikey NEO", strlen(reader_buf))); + ck_assert(version[0] == '1'); // Verify app version 1.x + ck_assert(version[1] == '.'); + } } END_TEST @@ -162,6 +184,229 @@ START_TEST(test_read_write_list_delete_cert) { } END_TEST +#include +#include +#include +#include + +// RSA2048 private key, generated with: `openssl genrsa 2048 -out private.pem` +static const char *private_key_pem = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEAwVUwmVbc+ffOy2+RivxBpgleTVN6bUa0q7jNYB+AseFQYaYq\n" + "EGfa+VGdxSGo+8DV1KT9+fNEd5243gXn/tcjtMItKeB+oAQc64s9lIFlYuR8bpq1\n" + "ibr33iW2elnnv9mpecqohdCVwM2McWveoPyb7MwlwVuhqexOzJO29bqJcazLbtkf\n" + "ZETK0oBx53/ylA4Y6nE9Pa46jW2qhj+KShf1iBg+gAyt3eI+wI2Wmub1WxLLH8D2\n" + "w+kow8QhQOa8dHCkRRw771JxVO5+d+Y/Y+x9B1HgF4q0q9xUlhWLK2TR4ChBFzXe\n" + "47sAHsSqi/pl5JbwYrHPOE/VEBLukmjL8NFCSQIDAQABAoIBADmEyOK2DyRnb6Ti\n" + "2qBJEJb/boj+7wuX36S/ZIrWlIlXiXyj3RvoaiOG/rNpokbURknvlIhKsfIMgLW9\n" + "eBo/k6Xxp1IwMjwVPS1uzbFjFfDoHYUijiQd9iSnf7TDDsnrThqoCp9VQViNTt1n\n" + "xGKNBS7cRddTFbPiVEdVIzfUeZPR2oRrc4maBCRCrQgg8WNknawmc8zhkf2NiPj3\n" + "tWLQHMy1/MgW2W1LM9sgzllEtS5CZUnyGy2HbbhS2tbZ6j9kPzOp0pPxxTTzJmmV\n" + "fi1vkJcVW4+MdXjWmhALcPA4dO7Y2Ljiu6VxIxQORRO1DyiCjAs1AVMQxgPAAY41\n" + "YR4Q2EkCgYEA4zE0oytg97aVaBY9CKi7/PqR+NI/uEvfoQCnT+ddaJgp/qsspuXo\n" + "tJt94p13ANd8O7suqQTVNvbZq1rX10xQjJZ9nvlqQa6iHkN6Epq31XBK3Z+acjIV\n" + "A2rAgKBByjz9/CpKHqnOsrTWU1Y7x416IG4BZt42hHdrxRH98/wiDH8CgYEA2djj\n" + "AjwgK+MwDnshwT1NNgCSP/2ZHatBAykZ5BCs9BJ6MNYqqXVGYoqs5Z5kSkow+Db3\n" + "pipkEieo5w2Rd5zkolTThaVCvRkSe5wRiBpZhaeY+b0UFwavGCb6zU/MmJIMDPiI\n" + "2iRGeCXgQDvIS/icIqzbTtp6dZaoMgG7LdSR7TcCgYBtxGhaLas8A8tL7vKuLFgn\n" + "cij0vyBqOr5hW596y54l2t7vXGTGfm5gVIAN7WaB0ZsEgPuaTet2Eu44DDwcmZKR\n" + "WmR3Wqor8eQCGzfvpTEMvqRtT5+fbPMaI4m+m68ttyo/m28UQZbMYPLscM2RLJnE\n" + "8WFcAiD0/33iST8ZksggoQKBgQDE/7Yhsj+hkHxHzB+1QPtOp2uaBHnvc4uCESwB\n" + "qvbMbN0kxrejsJLqz98UcozdBYSNIiAHmvQN2uGJuCJhGXdEORNjGxRkLoUhVPwh\n" + "qTplfC8BQHQncnrqi21oNw6ctg3BuQsAwaccRZwqWiWCVhrT3J8iCr6NEaWeOySK\n" + "iF1CNwKBgQCRpkkZArlccwS0kMvkK+tQ1rG2xWm7c05G34gP/g6dHFRy0gPNMyvi\n" + "SkiLTJmQIEZSAEiq0FFgcVwM6o556ftvQZuwDp5rHUbwqnHCpMJKpD9aJpStvfPi\n" + "4p9JbYdaGqnq4eoNKemmGnbUof0dR9Zr0lGmcMTwwzBib+4E1d7soA==\n" + "-----END RSA PRIVATE KEY-----\n"; + +// Certificate signed with key above: +// `openssl req -x509 -key private.pem -out cert.pem -subj "/CN=bar/OU=test/O=example.com/" -new` +static const char *certificate_pem = + "-----BEGIN CERTIFICATE-----\n" + "MIIC5zCCAc+gAwIBAgIJAOq8A/cmpxF5MA0GCSqGSIb3DQEBCwUAMDMxDDAKBgNV\n" + "BAMMA2JhcjENMAsGA1UECwwEdGVzdDEUMBIGA1UECgwLZXhhbXBsZS5jb20wHhcN\n" + "MTcwODAzMTE1MDI2WhcNMTgwODAzMTE1MDI2WjAzMQwwCgYDVQQDDANiYXIxDTAL\n" + "BgNVBAsMBHRlc3QxFDASBgNVBAoMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B\n" + "AQEFAAOCAQ8AMIIBCgKCAQEAwVUwmVbc+ffOy2+RivxBpgleTVN6bUa0q7jNYB+A\n" + "seFQYaYqEGfa+VGdxSGo+8DV1KT9+fNEd5243gXn/tcjtMItKeB+oAQc64s9lIFl\n" + "YuR8bpq1ibr33iW2elnnv9mpecqohdCVwM2McWveoPyb7MwlwVuhqexOzJO29bqJ\n" + "cazLbtkfZETK0oBx53/ylA4Y6nE9Pa46jW2qhj+KShf1iBg+gAyt3eI+wI2Wmub1\n" + "WxLLH8D2w+kow8QhQOa8dHCkRRw771JxVO5+d+Y/Y+x9B1HgF4q0q9xUlhWLK2TR\n" + "4ChBFzXe47sAHsSqi/pl5JbwYrHPOE/VEBLukmjL8NFCSQIDAQABMA0GCSqGSIb3\n" + "DQEBCwUAA4IBAQCamrwdEhNmY2GCQWq6U90Q3XQT6w0HHW/JmtuGeF+BTpVr12gN\n" + "/UvEXTo9geWbGcCTjaMMURTa7mUjVUIttIWEVHZMKqBuvsUM1RcuOEX/vitaJJ8K\n" + "Sw4upjCNa3ZxUXmSA1FBixZgDzFqjEeSiaJjMU0yX5W2p1T4iNYtF3YqzMF5AWSI\n" + "qCO7gP5ezPyg5kDnrO3V7DBgnDiqawq7Pyn9DynKNULX/hc1yls/R+ebb2u8Z+h5\n" + "W4YXbzGZb8qdT27qIZaHD638tL6liLkI6UE4KCXH8X8e3fqdbmqvwrq403nOGmsP\n" + "cbJb2PEXibNEQG234riKxm7x7vNDLL79Jwtc\n" + "-----END CERTIFICATE-----\n"; +bool set_component(unsigned char *in_ptr, const BIGNUM *bn, int element_len) { + int real_len = BN_num_bytes(bn); + + if(real_len > element_len) { + return false; + } + memset(in_ptr, 0, (size_t)(element_len - real_len)); + in_ptr += element_len - real_len; + BN_bn2bin(bn, in_ptr); + + return true; +} +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_ALGOR algor; + ASN1_TYPE parameter; + ASN1_OCTET_STRING digest; + unsigned char data[1024]; + + memcpy(data, in, in_len); + + digestInfo.algor = &algor; + digestInfo.algor->algorithm = OBJ_nid2obj(nid); + digestInfo.algor->parameter = ¶meter; + digestInfo.algor->parameter->type = V_ASN1_NULL; + digestInfo.algor->parameter->value.ptr = NULL; + digestInfo.digest = &digest; + digestInfo.digest->data = data; + digestInfo.digest->length = (int)in_len; + *out_len = (unsigned int)i2d_X509_SIG(&digestInfo, &out); + return true; +} +START_TEST(test_import_key) { + ykpiv_rc res; + + { + unsigned char pp = YKPIV_PINPOLICY_DEFAULT; + 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 + { + BIO *bio = NULL; + X509 *cert = NULL; + RSA *rsa = NULL; + EVP_PKEY *pub_key = NULL; + const EVP_MD *md = EVP_sha256(); + EVP_MD_CTX *mdctx; + + unsigned char signature[1024]; + unsigned char encoded[1024]; + unsigned char data[1024]; + unsigned char signinput[1024]; + unsigned char rand[128]; + + size_t sig_len = sizeof(signature); + size_t padlen = 256; + unsigned int enc_len; + unsigned int data_len; + + 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(rand, 128), 0); + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, md, NULL); + EVP_DigestUpdate(mdctx, rand, 128); + EVP_DigestFinal_ex(mdctx, data, &data_len); + + 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); + res = ykpiv_sign_data(g_state, signinput, padlen, signature, &sig_len, YKPIV_ALGO_RSA2048, 0x9e); + 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); + + 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); + } +} +END_TEST + START_TEST(test_generate_key) { ykpiv_rc res; uint8_t *mod, *exp; @@ -241,17 +486,78 @@ START_TEST(test_authenticate) { } END_TEST -START_TEST(test_reset) { +START_TEST(test_change_pin) { + ykpiv_rc res; + + res = ykpiv_verify(g_state, "123456", NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_change_pin(g_state, "123456", 6, "ABCDEF", 6, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_verify(g_state, "123456", NULL); + ck_assert_int_eq(res, YKPIV_WRONG_PIN); + + res = ykpiv_verify(g_state, "ABCDEF", NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_change_pin(g_state, "ABCDEF", 6, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_verify(g_state, "ABCDEF", NULL); + ck_assert_int_eq(res, YKPIV_WRONG_PIN); + + res = ykpiv_verify(g_state, "123456", NULL); + ck_assert_int_eq(res, YKPIV_OK); +} +END_TEST + +START_TEST(test_change_puk) { + ykpiv_rc res; + + res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_change_puk(g_state, "12345678", 8, "ABCDEFGH", 8, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_WRONG_PIN); + + res = ykpiv_unblock_pin(g_state, "ABCDEFGH", 8, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_change_puk(g_state, "ABCDEFGH", 8, "12345678", 8, NULL); + ck_assert_int_eq(res, YKPIV_OK); + + res = ykpiv_unblock_pin(g_state, "ABCDEFGH", 8, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_WRONG_PIN); + + res = ykpiv_unblock_pin(g_state, "12345678", 8, "123456", 6, NULL); + ck_assert_int_eq(res, YKPIV_OK); +} +END_TEST + +static int block_and_reset() { ykpiv_rc res; int tries = 100; - int i; + int tries_until_blocked; + tries_until_blocked = 0; while (tries) { res = ykpiv_verify(g_state, "AAAAAA", &tries); if (res == YKPIV_PIN_LOCKED) break; ck_assert_int_eq(res, YKPIV_WRONG_PIN); + tries_until_blocked++; } + + // Verify no PIN retries remaining + tries = 100; + res = ykpiv_get_pin_retries(g_state, &tries); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_eq(tries, 0); + tries = 100; while (tries) { res = ykpiv_change_puk(g_state, "AAAAAAAA", 8, "AAAAAAAA", 8, &tries); @@ -261,6 +567,61 @@ START_TEST(test_reset) { } res = ykpiv_util_reset(g_state); ck_assert_int_eq(res, YKPIV_OK); + return tries_until_blocked; +} + +START_TEST(test_reset) { + ykpiv_rc res; + int tries = 100; + int tries_until_blocked; + + // Block and reset, with default PIN retries + tries_until_blocked = block_and_reset(); + ck_assert_int_eq(tries_until_blocked, 3); + + // Authenticate and increase PIN retries + test_authenticate(0); + res = ykpiv_verify(g_state, "123456", NULL); + ck_assert_int_eq(res, YKPIV_OK); + res = ykpiv_set_pin_retries(g_state, 8); + ck_assert_int_eq(res, YKPIV_OK); + + // Block and reset again, verifying increased PIN retries + tries_until_blocked = block_and_reset(); + ck_assert_int_eq(tries_until_blocked, 8); + // Note: defaults back to 3 retries after reset + + // Verify default (3) PIN retries remaining + tries = 0; + res = ykpiv_get_pin_retries(g_state, &tries); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_eq(tries, 3); + + // Verify still (3) PIN retries remaining + tries = 0; + res = ykpiv_get_pin_retries(g_state, &tries); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_eq(tries, 3); + + // Try wrong PIN + res = ykpiv_verify(g_state, "AAAAAA", &tries); + + // Verify 2 PIN retries remaining + tries = 0; + res = ykpiv_get_pin_retries(g_state, &tries); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_eq(tries, 2); + + // Verify correct PIN + tries = 100; + res = ykpiv_verify(g_state, "123456", &tries); + ck_assert_int_eq(res, YKPIV_OK); + + // Verify back to 3 PIN retries remaining + tries = 0; + res = ykpiv_get_pin_retries(g_state, &tries); + ck_assert_int_eq(res, YKPIV_OK); + ck_assert_int_eq(tries, 3); } END_TEST @@ -390,10 +751,13 @@ Suite *test_suite(void) { tcase_add_test(tc, test_authenticate); // Test util functionality + tcase_add_test(tc, test_change_pin); + tcase_add_test(tc, test_change_puk); tcase_add_test(tc, test_devicemodel); tcase_add_test(tc, test_get_set_cardid); tcase_add_test(tc, test_list_readers); tcase_add_test(tc, test_read_write_list_delete_cert); + tcase_add_test(tc, test_import_key); tcase_add_test(tc, test_generate_key); // Must be last: tear down and re-test with custom memory allocator diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 1d6b21f..0e58ee0 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -864,6 +864,7 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { } strcpy(state->pin, pin); } + if (tries) *tries = (sw & 0xf); return YKPIV_OK; } else if((sw >> 8) == 0x63) { if (tries) *tries = (sw & 0xf); @@ -876,6 +877,48 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { } } +ykpiv_rc ykpiv_get_pin_retries(ykpiv_state *state, int* tries) { + ykpiv_rc res; + ykpiv_rc ykrc; + if (NULL == state || NULL == tries) { + return YKPIV_ARGUMENT_ERROR; + } + res = _ykpiv_select_application(state); + ykrc = ykpiv_verify(state, NULL, tries); + // WRONG_PIN is expected on successful query. + return ykrc == YKPIV_WRONG_PIN ? YKPIV_OK : ykrc; +} + + +ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, const int tries) { + ykpiv_rc res = YKPIV_OK; + unsigned char templ[] = {0, YKPIV_INS_SET_PIN_RETRIES, (unsigned char)tries, YKPIV_RETRIES_DEFAULT}; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + + if (0 == tries) { + //zero value means no change in retry count according to minidriver spec + return YKPIV_OK; + } + if (tries > YKPIV_RETRIES_MAX || tries < 1) { + return YKPIV_RANGE_ERROR; + } + + res = ykpiv_transfer_data(state, templ, NULL, 0, data, &recv_len, &sw); + if (YKPIV_OK == res) { + if (SW_SUCCESS == sw) { + return YKPIV_OK; + } else if (sw == SW_ERR_AUTH_BLOCKED) { + return YKPIV_AUTHENTICATION_ERROR; + } else if (sw == SW_ERR_SECURITY_STATUS) { + return YKPIV_AUTHENTICATION_ERROR; + } + return YKPIV_GENERIC_ERROR; + } + return res; +} + #define CHREF_ACT_CHANGE_PIN 0 #define CHREF_ACT_UNBLOCK_PIN 1 #define CHREF_ACT_CHANGE_PUK 2 @@ -912,7 +955,7 @@ static ykpiv_rc change_pin_internal(ykpiv_state *state, int action, const char * return res; } else if(sw != SW_SUCCESS) { if((sw >> 8) == 0x63) { - *tries = sw & 0xf; + if (tries) *tries = sw & 0xf; return YKPIV_WRONG_PIN; } else if(sw == SW_ERR_AUTH_BLOCKED) { return YKPIV_PIN_LOCKED; @@ -1110,9 +1153,14 @@ ykpiv_rc ykpiv_import_private_key(ykpiv_state *state, const unsigned char key, u return YKPIV_ALGORITHM_ERROR; for (i = 0; i < n_params; i++) { + size_t remaining; *in_ptr++ = param_tag + i; in_ptr += _ykpiv_set_length(in_ptr, elem_len); padding = elem_len - lens[i]; + remaining = (uintptr_t)key_data + sizeof(key_data) - (uintptr_t)in_ptr; + if (padding > remaining) { + return YKPIV_ALGORITHM_ERROR; + } memset(in_ptr, 0, padding); in_ptr += padding; memcpy(in_ptr, params[i], lens[i]); diff --git a/lib/ykpiv.h b/lib/ykpiv.h index a6f5636..0af2e79 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -92,9 +92,7 @@ extern "C" 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, unsigned char algorithm, unsigned char key); - ykpiv_rc ykpiv_sign_data2(ykpiv_state *state, const unsigned char *sign_in, - size_t in_len, unsigned char *sign_out, size_t *out_len, - unsigned char algorithm, unsigned char key, int padding); // Allow not to add padding + // TODO (TREV): minidriver has ykpiv_sign_data2 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); @@ -123,6 +121,8 @@ extern "C" const unsigned char *qinv, size_t qinv_len, const unsigned char *ec_data, unsigned char ec_data_len, const unsigned char pin_policy, const unsigned char touch_policy); + ykpiv_rc ykpiv_get_pin_retries(ykpiv_state *state, int* tries); + ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, const int tries); #define YKPIV_ALGO_TAG 0x80 #define YKPIV_ALGO_3DES 0x03 @@ -234,7 +234,8 @@ extern "C" #define YKPIV_IS_EC(a) ((a == YKPIV_ALGO_ECCP256 || a == YKPIV_ALGO_ECCP384)) #define YKPIV_IS_RSA(a) ((a == YKPIV_ALGO_RSA1024 || a == YKPIV_ALGO_RSA2048)) - +#define YKPIV_RETRIES_DEFAULT 3 +#define YKPIV_RETRIES_MAX 0xff // @@ -310,7 +311,7 @@ extern "C" ykpiv_rc ykpiv_util_reset(ykpiv_state *state); /** - * Card identifier + * Card identifier */ typedef struct { uint8_t data[16]; @@ -318,7 +319,7 @@ extern "C" /** * Get card identifier - * + * * @param state state * @param cardid ykpiv_cardid return value * @@ -328,7 +329,7 @@ extern "C" /** * Set card identifier - * + * * The card must be authenticated to call this function. * * @param state state