From c9099836f080c8bb821264050f5f6d59a1ceddba Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 9 Dec 2008 00:08:01 +0000 Subject: Add tests for X509 certificates. --- src/Makefile.am | 1 + src/certificate.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/check.c | 33 +++++ src/msg.c | 14 ++- src/p11-tests.c | 7 ++ src/p11-tests.h | 39 ++++-- src/rsa.c | 1 - 7 files changed, 434 insertions(+), 16 deletions(-) create mode 100644 src/certificate.c diff --git a/src/Makefile.am b/src/Makefile.am index 5139896..33abf3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,6 +4,7 @@ bin_PROGRAMS = p11-tests p11_tests_LDADD = -ldl -lpthread p11_tests_SOURCES = \ + certificate.c \ check.c \ config.c \ key.c \ diff --git a/src/certificate.c b/src/certificate.c new file mode 100644 index 0000000..ce4cac3 --- /dev/null +++ b/src/certificate.c @@ -0,0 +1,355 @@ + +#include "p11-tests.h" + +#include +#include + +#include + +/* ---------------------------------------------------------------------------------- + * TESTS + */ + +static const char* +test_x509_name(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE attr_type, const char *attr_name, + X509_NAME* compare) +{ + CK_BYTE_PTR ptr, encoded; + CK_ATTRIBUTE attr; + X509_NAME* name; + CK_RV rv; + int len; + + /* + * We return any messages, so this function can be called for + * different X509 dns. + */ + + attr.type = attr_type; + attr.pValue = NULL; + attr.ulValueLen = 0; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + if(rv != CKR_OK) + return p11t_msg_rv(rv); + + attr.pValue = malloc(attr.ulValueLen); + assert(attr.pValue); + + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + if(rv != CKR_OK) + return p11t_msg_rv(rv); + + /* Let openssl parse it */ + ptr = attr.pValue; + name = d2i_X509_NAME(NULL, (const unsigned char**)&ptr, attr.ulValueLen); + if(name == NULL) + return p11t_msg_openssl(); + if(ptr - (CK_BYTE_PTR)attr.pValue != attr.ulValueLen) + return "Extra trailing bytes"; + + /* Serialize the compare one */ + len = i2d_X509_NAME(compare, NULL); + assert(len >= 0); + encoded = malloc(len); + assert(encoded); + ptr = encoded; + len = i2d_X509_NAME(compare, &ptr); + assert(len >= 0); + + if(len != attr.ulValueLen || memcmp(encoded, attr.pValue, len) != 0) + return "Encoding of the DN didn't match what was in certificate"; + + free(attr.pValue); + free(encoded); + + X509_NAME_free(name); + + return NULL; +} + +static int +test_x509_cross_search(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE one, CK_ATTRIBUTE_TYPE two, + const char *description) +{ + CK_OBJECT_HANDLE_PTR objects; + CK_ULONG n_objects; + CK_ATTRIBUTE attrs[2]; + CK_ULONG i, n_attrs = 0; + CK_BBOOL success = CK_FALSE; + CK_RV rv; + + P11T_SECTION("CKC_X_509"); + + if(one != CK_INVALID) + { + attrs[n_attrs].type = one; + attrs[n_attrs].pValue = NULL; + attrs[n_attrs].ulValueLen = 0; + ++n_attrs; + } + + if(two != CK_INVALID) + { + attrs[n_attrs].type = two; + attrs[n_attrs].pValue = NULL; + attrs[n_attrs].ulValueLen = 0; + ++n_attrs; + } + + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, attrs, n_attrs); + P11T_CHECK_RV("Cross find certificates by attributes", rv, CKR_OK); + + for(i = 0; i < n_attrs; ++i) + { + attrs[i].pValue = malloc(attrs[i].ulValueLen); + assert(attrs[i].pValue); + } + + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, attrs, n_attrs); + P11T_CHECK_RV("Cross find certificates by attributes", rv, CKR_OK); + + /* Now find all the objects with the same */ + objects = p11t_object_find(session, attrs, n_attrs, &n_objects); + for(i = 0; objects && i < n_objects; ++i) + { + if(objects[i] == object) + { + success = CK_TRUE; + break; + } + } + + for(i = 0; i < n_attrs; ++i) + free(attrs[i].pValue); + + free(objects); + + if(!success) + { + p11t_check_fail("Couldn't find certificates by attributes %s", description); + return STOP; + } + + return CONTINUE; +} + +static int +test_x509_certificate(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) +{ + CK_BYTE check[4096]; + CK_BYTE buffer[4096]; + CK_ATTRIBUTE attr; + CK_BYTE_PTR der_value; + CK_BYTE_PTR ptr; + CK_ULONG n_der_value; + CK_ULONG java_midp; + const char* msg; + X509* cert; + CK_RV rv; + int len; + + P11T_SECTION("CKC_X_509"); + + /* Get the certificate DER */ + attr.type = CKA_VALUE; + attr.ulValueLen = 0; + attr.pValue = NULL; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_VALUE", rv, CKR_OK); + attr.pValue = der_value = malloc(attr.ulValueLen); + assert(der_value); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_VALUE", rv, CKR_OK); + n_der_value = attr.ulValueLen; + + /* Have OpenSSL parse it */ + ptr = der_value; + cert = d2i_X509(NULL, (const unsigned char**)&ptr, n_der_value); + if(cert == NULL) + P11T_CHECK_FAIL_MSG("CKA_VALUE", p11t_msg_openssl()); + if(ptr - der_value != n_der_value) + P11T_CHECK_FAIL_MSG("CKA_VALUE", "Extra trailing bytes"); + + + /* Cross check the certificate with the CKA_CHECK_VALUE */ + if(p11t_test_unexpected) + { + attr.type = CKA_CHECK_VALUE; + attr.pValue = buffer; + attr.ulValueLen = sizeof(buffer); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_CHECK_VALUE", rv, CKR_OK); + SHA1(der_value, n_der_value, check); + if(memcmp(check, buffer, 3) != 0) + P11T_CHECK_FAIL_MSG("CKA_CHECK_VALUE", "not equal to first 3 bytes of SHA1 hash of CKA_VALUE"); + } + + /* CKA_SUBJECT */ + msg = test_x509_name(session, object, CKA_SUBJECT, "CKA_SUBJECT", cert->cert_info->subject); + if(msg != NULL) + P11T_CHECK_FAIL_MSG("CKA_SUBJECT", msg); + + /* CKA_ISSUER */ + msg = test_x509_name(session, object, CKA_ISSUER, "CKA_ISSUER", cert->cert_info->issuer); + if(msg != NULL) + P11T_CHECK_FAIL_MSG("CKA_ISSUER", msg); + + /* CKA_SERIAL_NUMBER */ + attr.type = CKA_SERIAL_NUMBER; + attr.pValue = check; + attr.ulValueLen = sizeof(check); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_SERIAL_NUMBER", rv, CKR_OK); + ptr = buffer; + len = i2d_ASN1_INTEGER(cert->cert_info->serialNumber, &ptr); + assert(len >= 0); + if(attr.ulValueLen != len || memcmp(check, buffer, len) != 0) + P11T_CHECK_FAIL_MSG("CKA_SERIAL_NUMBER", "serial number does not match one encoded in CKA_VALUE"); + + if(p11t_test_unexpected) + { + /* CKA_URL */ + attr.type = CKA_URL; + attr.pValue = check; + attr.ulValueLen = sizeof(check); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_URL", rv, CKR_OK); + + /* CKA_HASH_OF_SUBJECT_PUBLIC_KEY */ + attr.type = CKA_HASH_OF_SUBJECT_PUBLIC_KEY; + attr.pValue = check; + attr.ulValueLen = sizeof(check); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_HASH_OF_SUBJECT_PUBLIC_KEY", rv, CKR_OK); + + /* CKA_HASH_OF_ISSUER_PUBLIC_KEY */ + attr.type = CKA_HASH_OF_ISSUER_PUBLIC_KEY; + attr.pValue = check; + attr.ulValueLen = sizeof(check); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_HASH_OF_ISSUER_PUBLIC_KEY", rv, CKR_OK); + + /* CKA_JAVA_MIDP_SECURITY_DOMAIN */ + attr.type = CKA_JAVA_MIDP_SECURITY_DOMAIN; + attr.pValue = &java_midp; + attr.ulValueLen = sizeof(java_midp); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_JAVA_MIDP_SECURITY_DOMAIN", rv, CKR_OK); + if(java_midp != 0 || java_midp != 1 || java_midp != 2 || java_midp != 3) + P11T_CHECK_FAIL_MSG("CKA_JAVA_MIDP_SECURITY_DOMAIN", "Unrecognized value"); + + /* CKA_ID */ + attr.type = CKA_ID; + attr.pValue = check; + attr.ulValueLen = sizeof(check); + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_ID", rv, CKR_OK); + if(attr.ulValueLen == 0) + P11T_CHECK_FAIL_MSG("CKA_ID", "zero length CKA_ID"); + } + + test_x509_cross_search(session, object, CKA_ID, CK_INVALID, "CKA_ID"); + test_x509_cross_search(session, object, CKA_ID, CK_INVALID, "CKA_SERIAL_NUMBER, CKA_ISSUER"); + + X509_free(cert); + free(der_value); + + return CONTINUE; +} + +static int +test_certificate_object(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) +{ + CK_CERTIFICATE_TYPE cert_type; + CK_BYTE check_value[256]; + CK_ATTRIBUTE attr; + CK_ULONG category; + CK_BBOOL trusted; + CK_DATE start_date; + CK_DATE end_date; + CK_RV rv; + + P11T_SECTION("CKO_CERTIFICATE"); + + attr.type = CKA_CERTIFICATE_TYPE; + attr.ulValueLen = sizeof(cert_type); + attr.pValue = &cert_type; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_CERTIFICATE_TYPE", rv, CKR_OK); + + if(p11t_test_unexpected) + { + attr.type = CKA_TRUSTED; + attr.ulValueLen = sizeof(trusted); + attr.pValue = &trusted; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_TRUSTED", rv, CKR_OK); + P11T_CHECK_BOOL("CKA_TRUSTED", trusted); + + attr.type = CKA_CERTIFICATE_CATEGORY; + attr.ulValueLen = sizeof(category); + attr.pValue = &category; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_CERTIFICATE_CATEGORY", rv, CKR_OK); + if(category != 1 && category != 2 && category != 3) + P11T_CHECK_FAIL_MSG("CKA_CERTIFICATE_CATEGORY", "invalid value"); + + attr.type = CKA_CHECK_VALUE; + attr.ulValueLen = sizeof(check_value); + attr.pValue = check_value; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_CHECK_VALUE", rv, CKR_OK); + if(attr.ulValueLen != 3) + P11T_CHECK_FAIL_MSG("CKA_CHECK_VALUE", "length must be 3 bytes"); + + attr.type = CKA_START_DATE; + attr.ulValueLen = sizeof(start_date); + attr.pValue = &start_date; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_START_DATE", rv, CKR_OK); + P11T_CHECK_DATE("CKA_START_DATE", &start_date); + + attr.type = CKA_END_DATE; + attr.ulValueLen = sizeof(end_date); + attr.pValue = &end_date; + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, &attr, 1); + P11T_CHECK_RV("CKA_END_DATE", rv, CKR_OK); + P11T_CHECK_DATE("CKA_END_DATE", &end_date); + } + + test_x509_certificate(session, object); + + return CONTINUE; +} + +void +p11t_certificate_tests(void) +{ + CK_OBJECT_CLASS klass = CKO_CERTIFICATE; + CK_OBJECT_HANDLE_PTR objects; + CK_SESSION_HANDLE session; + CK_ATTRIBUTE attrs[1]; + CK_ULONG j, i, n_objects; + CK_SLOT_ID slot; + + for(j = 0; j < p11t_slot_count; ++j) + { + slot = p11t_slot_get_id(j); + session = p11t_session_open(slot, 0); + if(!session) + continue; + + attrs[0].type = CKA_CLASS; + attrs[0].ulValueLen = sizeof(klass); + attrs[0].pValue = &klass; + + objects = p11t_object_find(session, attrs, 1, &n_objects); + for(i = 0; objects && i < n_objects; ++i) + test_certificate_object(session, objects[i]); + free(objects); + + p11t_session_close(session); + } +} diff --git a/src/check.c b/src/check.c index 4c71edd..cf12dd4 100644 --- a/src/check.c +++ b/src/check.c @@ -104,6 +104,39 @@ _p11t_check_bool(const char *what, CK_BBOOL value) return CONTINUE; } +static int +atoin (const char *p, int digits) +{ + int ret = 0, base = 1; + while(--digits >= 0) { + if (p[digits] < '0' || p[digits] > '9') + return -1; + ret += (p[digits] - '0') * base; + base *= 10; + } + return ret; +} + +int +_p11t_check_date(const char *what, CK_DATE* value) +{ + int year, month, day; + + year = atoin((const char*)value->year, 4); + month = atoin((const char*)value->month, 2); + day = atoin((const char*)value->day, 2); + + if(year < 0 || year > 9999 || + month < 1 || month > 12 || + day < 1 || day > 31) + { + p11t_check_fail("%s: invalid date", what); + return STOP; + } + + return CONTINUE; +} + int _p11t_check_string(const char *what, CK_UTF8CHAR_PTR value, CK_ULONG length) { diff --git a/src/msg.c b/src/msg.c index 6ad4fa3..be2fdcd 100644 --- a/src/msg.c +++ b/src/msg.c @@ -13,6 +13,8 @@ #include #endif +#include + static const char *the_prefix = NULL; const char* @@ -118,9 +120,9 @@ p11t_msg_rv(CK_RV rv) static char last_error[1024]; const char* -p11t_msg_lasterr(void) +p11t_msg_os(void) { - LPVOID lpMsgBuf; + LPVOID lpMsgBuf; DWORD error = GetLastError(); DWORD dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, error, @@ -142,13 +144,19 @@ p11t_msg_lasterr(void) #else /* _WIN32 */ const char* -p11t_msg_lasterr(void) +p11t_msg_os(void) { return strerror(errno); } #endif /* _WIN32 */ +const char* +p11t_msg_openssl(void) +{ + return ERR_error_string(ERR_get_error(), NULL); +} + void p11t_msg_va(const char *message, va_list va) { diff --git a/src/p11-tests.c b/src/p11-tests.c index 0e7975c..e4e4110 100644 --- a/src/p11-tests.c +++ b/src/p11-tests.c @@ -6,6 +6,9 @@ #include #include +#include +#include + int p11t_test_unexpected = 1; static void @@ -50,6 +53,9 @@ main(int argc, char* argv[]) if(config) p11t_config_parse(config); + ERR_load_crypto_strings(); + OpenSSL_add_all_digests(); + /* Basic module tests */ p11t_module_load(argv[0]); p11t_module_initialize(); @@ -58,6 +64,7 @@ main(int argc, char* argv[]) p11t_session_tests(); p11t_object_tests(); p11t_rsa_tests(); + p11t_certificate_tests(); /* Remaining module tests */ p11t_module_finalize(); diff --git a/src/p11-tests.h b/src/p11-tests.h index 82bc691..efacace 100644 --- a/src/p11-tests.h +++ b/src/p11-tests.h @@ -21,20 +21,10 @@ extern int p11t_test_unexpected; /* ------------------------------------------------------------------- - * msg.c + * certificate.c */ -const char* p11t_msg_rv(CK_RV rv); -const char* p11t_msg_lasterr(void); - -void p11t_msg_va(const char *message, va_list va); -void p11t_msg_code(const char* code, const char *message, va_list va); -void p11t_msg_print(const char *message, ...); -void p11t_msg_fatal(const char *message, ...); -const char* p11t_msg_prefix(const char *prefix); - -#define p11t_msg_here() \ - (__func__ "() at " __FILE__ ":" __LINE__) +void p11t_certificate_tests(void); /* ------------------------------------------------------------------- * check.c @@ -57,6 +47,9 @@ extern int p11t_check_verbose; #define P11T_CHECK_FAIL(what) \ _P11T_BEGIN p11t_check_fail("%s", (what)); return STOP; _P11T_END +#define P11T_CHECK_FAIL_MSG(what, msg) \ + _P11T_BEGIN p11t_check_fail("%s: %s", (what), (msg)); return STOP; _P11T_END + int p11t_check_fail(const char *message, ...); int p11t_check_warn(const char *message, ...); int p11t_check_info(const char *message, ...); @@ -101,6 +94,11 @@ int _p11t_check_bool(const char *what, CK_BBOOL value); int _p11t_check_string(const char *what, CK_UTF8CHAR_PTR value, CK_ULONG length); +#define P11T_CHECK_DATE(what, value) \ + _P11T_BEGIN if(!_p11t_check_date((what), (value))) return STOP; _P11T_END + +int _p11t_check_date(const char *what, CK_DATE* value); + #define P11T_CHECK_NOTE(what) /* ------------------------------------------------------------------- @@ -120,6 +118,23 @@ CK_OBJECT_HANDLE p11t_key_get_private(CK_SESSION_HANDLE session, CK_OBJECT_HANDL RSA* p11t_key_export_public_rsa(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key); +/* ------------------------------------------------------------------- + * msg.c + */ + +const char* p11t_msg_rv(CK_RV rv); +const char* p11t_msg_os(void); +const char* p11t_msg_openssl(void); + +void p11t_msg_va(const char *message, va_list va); +void p11t_msg_code(const char* code, const char *message, va_list va); +void p11t_msg_print(const char *message, ...); +void p11t_msg_fatal(const char *message, ...); +const char* p11t_msg_prefix(const char *prefix); + +#define p11t_msg_here() \ + (__func__ "() at " __FILE__ ":" __LINE__) + /* ------------------------------------------------------------------- * module.c */ diff --git a/src/rsa.c b/src/rsa.c index b8371cc..fa3d372 100644 --- a/src/rsa.c +++ b/src/rsa.c @@ -444,6 +444,5 @@ test_rsa_pkcs(CK_SLOT_ID slot, CK_MECHANISM_TYPE mech, CK_MECHANISM_INFO_PTR inf void p11t_rsa_tests(void) { - OpenSSL_add_all_digests(); p11t_slot_for_each_mech(CKM_RSA_PKCS, test_rsa_pkcs); } -- cgit v1.2.3