From b2e7ce99d4a88ddecaa3e0f2c1c124efcb756ef6 Mon Sep 17 00:00:00 2001 From: prateeky Date: Tue, 21 Sep 2021 17:02:12 -0700 Subject: [PATCH 1/2] Add more PKCS11 tests --- PKCS11.md | 29 +- include/aws/io/private/pkcs11_private.h | 27 ++ source/pkcs11.c | 25 +- tests/CMakeLists.txt | 3 + tests/pkcs11_test.c | 599 ++++++++++++++++++++++-- 5 files changed, 601 insertions(+), 82 deletions(-) diff --git a/PKCS11.md b/PKCS11.md index 4df144f58..b1b9e0c98 100644 --- a/PKCS11.md +++ b/PKCS11.md @@ -6,12 +6,12 @@ and set the following environment variables: ``` TEST_PKCS11_LIB = -TEST_PKCS11_TOKEN_LABEL = -TEST_PKCS11_PIN = -TEST_PKCS11_PKEY_LABEL = -TEST_PKCS11_CERT_FILE = -TEST_PKCS11_CA_FILE = +TEST_PKCS11_TOKEN_DIR = ``` +TEST_PKCS11_LIB is used by the tests to peform pkcs11 operations. + +TEST_PKCS11_TOKEN_DIR is used by the tests to clear the softhsm tokens before a test begins. This is achieved by cleaning the token directory NOTE: Any tokens created outside the tests will be cleaned up along with all the objects/keys on it as part of the tests. + ## The suggested way to set up your machine 1) Install [SoftHSM2](https://www.opendnssec.org/softhsm/) via brew / apt / apt-get / yum: @@ -31,7 +31,14 @@ TEST_PKCS11_CA_FILE = directories.tokendir = /usr/local/var/lib/softhsm/tokens/ ``` -2) Create token and private key. +2) Set env vars like so: + ``` + TEST_PKCS11_LIB = + TEST_PKCS11_TOKEN_DIR = /usr/local/var/lib/softhsm/tokens/ + ``` + + +3) [Example to import your keys, Not used by tests] Create token and private key You can use any values for the labels, pin, key, cert, CA etc. Here are copy-paste friendly commands for using files available in this repo. @@ -44,13 +51,5 @@ TEST_PKCS11_CA_FILE = ``` > softhsm2-util --import tests/resources/unittests.p8 --slot --label my-test-key --id BEEFCAFE --pin 0000 ``` + WARN: All tokens created outside the tests would be cleaned up as part of the tests, Use a separate token directory for running the tests if you would like to keep your tokens intact. -3) Set env vars like so: - ``` - TEST_PKCS11_LIB = - TEST_PKCS11_TOKEN_LABEL = my-test-token - TEST_PKCS11_PIN = 0000 - TEST_PKCS11_PKEY_LABEL = my-test-key - TEST_PKCS11_CERT_FILE = /tests/resources/unittests.crt - TEST_PKCS11_CA_FILE = /tests/resources/unittests.crt - ``` diff --git a/include/aws/io/private/pkcs11_private.h b/include/aws/io/private/pkcs11_private.h index 34dd31a69..92f930f21 100644 --- a/include/aws/io/private/pkcs11_private.h +++ b/include/aws/io/private/pkcs11_private.h @@ -8,8 +8,35 @@ #include struct aws_pkcs11_lib; +#include +#include + +/* These defines must exist before the official PKCS#11 headers are included */ +#define CK_PTR * +#define NULL_PTR 0 +#define CK_DEFINE_FUNCTION(returnType, name) returnType name +#define CK_DECLARE_FUNCTION(returnType, name) returnType name +#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType(CK_PTR name) +#define CK_CALLBACK_FUNCTION(returnType, name) returnType(CK_PTR name) + +/* Support older PKCS#11 versions, even if we're using newer headers. + * The PKCS#11 API is designed to be forward compatible. */ +#include + struct aws_string; +struct aws_pkcs11_lib { + struct aws_ref_count ref_count; + struct aws_allocator *allocator; + + struct aws_shared_library shared_lib; + + CK_FUNCTION_LIST_PTR function_list; + + /* If true, C_Finalize() should be called when last ref-count is released */ + bool should_finalize; +}; + /** * pkcs11_private.h * This file declares symbols that are private to aws-c-io but need to be diff --git a/source/pkcs11.c b/source/pkcs11.c index 3d4a1a4e8..81271d5ce 100644 --- a/source/pkcs11.c +++ b/source/pkcs11.c @@ -13,17 +13,6 @@ #include -/* These defines must exist before the official PKCS#11 headers are included */ -#define CK_PTR * -#define NULL_PTR 0 -#define CK_DEFINE_FUNCTION(returnType, name) returnType name -#define CK_DECLARE_FUNCTION(returnType, name) returnType name -#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType(CK_PTR name) -#define CK_CALLBACK_FUNCTION(returnType, name) returnType(CK_PTR name) - -/* Support older PKCS#11 versions, even if we're using newer headers. - * The PKCS#11 API is designed to be forward compatible. */ -#include #define AWS_SUPPORTED_CRYPTOKI_VERSION_MAJOR 2 #define AWS_MIN_SUPPORTED_CRYPTOKI_VERSION_MINOR 20 @@ -258,18 +247,6 @@ static CK_RV s_pkcs11_unlock_mutex(CK_VOID_PTR mutex_ptr) { return CKR_OK; } -struct aws_pkcs11_lib { - struct aws_ref_count ref_count; - struct aws_allocator *allocator; - - struct aws_shared_library shared_lib; - - CK_FUNCTION_LIST_PTR function_list; - - /* If true, C_Finalize() should be called when last ref-count is released */ - bool should_finalize; -}; - /* Invoked when last ref-count is released. Free all resources. * Note that this is also called if initialization fails half-way through */ static void s_pkcs11_lib_destroy(void *user_data) { @@ -665,7 +642,7 @@ int aws_pkcs11_lib_find_private_key( bool must_finalize_search = false; /* set up search attributes */ - CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; CK_ULONG num_attributes = 1; CK_ATTRIBUTE attributes[2] = { { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2c70fd792..56f132b22 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -228,6 +228,9 @@ if (ENABLE_PKCS11_TESTS) add_test_case(pkcs11_lib_initialize) add_test_case(pkcs11_lib_omit_initialize) add_test_case(pkcs11_find_private_key) + add_test_case(pkcs11_find_slot) + add_test_case(pkcs11_session_tests) + add_test_case(pkcs11_login_tests) endif() set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) diff --git a/tests/pkcs11_test.c b/tests/pkcs11_test.c index c52a8f35f..83060316d 100644 --- a/tests/pkcs11_test.c +++ b/tests/pkcs11_test.c @@ -7,6 +7,8 @@ * See PKCS11.md for instructions on running these tests */ +#include +#include #include #include @@ -15,24 +17,167 @@ #include AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_LIB, "TEST_PKCS11_LIB"); -AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_TOKEN_LABEL, "TEST_PKCS11_TOKEN_LABEL"); -AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_PIN, "TEST_PKCS11_PIN"); -AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_PKEY_LABEL, "TEST_PKCS11_PKEY_LABEL"); +AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_TOKEN_DIR, "TEST_PKCS11_TOKEN_DIR"); +extern const char *s_ckr_str(CK_RV rv); /* Singleton that stores env-var values */ struct pkcs11_tester { - struct aws_string *filepath; - struct aws_string *token_label; - struct aws_string *pin; - struct aws_string *pkey_label; + struct aws_string *shared_lib_path; + struct aws_string *token_dir; }; + static struct pkcs11_tester s_pkcs11_tester; +CK_SLOT_ID next_free_slot_id = 0; +const char* const TOKEN_LABEL = "label"; +const char* const SO_PIN = "qwerty"; +const char* const USER_PIN = "341269504732"; +/* + * Helper functions to interact with softhsm begin + * */ + + +/* Helper pkcs functions to provision/setup/clear softhsm tokens/keys */ +static CK_SLOT_ID s_pkcs11_find_slot(struct aws_pkcs11_lib *pkcs11_lib, CK_TOKEN_INFO tokenInfo) { + CK_ULONG ul_slot_count; + CK_SLOT_ID slot_id = -1; + CK_RV rv = pkcs11_lib->function_list->C_GetSlotList(CK_TRUE, NULL_PTR, &ul_slot_count); + if (rv != CKR_OK) { + FAIL("ERROR: Could not get the number of slots.\n"); + } + + CK_SLOT_ID_PTR p_slot_list = (CK_SLOT_ID_PTR)malloc(ul_slot_count * sizeof(CK_SLOT_ID)); + if (p_slot_list == NULL) { + FAIL("ERROR: Could not allocate memory.\n"); + } + + rv = pkcs11_lib->function_list->C_GetSlotList(CK_FALSE, p_slot_list, &ul_slot_count); + if (rv != CKR_OK) { + free(p_slot_list); + FAIL("ERROR: Could not get the slot list.\n"); + } + + size_t counter = 0; + for (CK_ULONG i = 0; i < ul_slot_count; i++) { + CK_TOKEN_INFO curr_token_info; + + rv = pkcs11_lib->function_list->C_GetTokenInfo(p_slot_list[i], &curr_token_info); + if (rv != CKR_OK) { + free(p_slot_list); + FAIL("ERROR: Could not get info about the token in slot %lu.\n", p_slot_list[i]); + } + + if (memcmp(curr_token_info.serialNumber, tokenInfo.serialNumber, sizeof(tokenInfo.serialNumber)) == 0 && + memcmp(curr_token_info.label, tokenInfo.label, sizeof(tokenInfo.label)) == 0) { + slot_id = p_slot_list[i]; + counter++; + } + } + + free(p_slot_list); + + if (counter == 1) return slot_id; + if (counter > 1) { + FAIL("ERROR: Found multiple matching slots/tokens.\n"); + } + FAIL("ERROR: Could not find a slot/token using --serial, or --token\n"); +} + +static void s_pkcs11_clear_softhsm(struct aws_pkcs11_lib *pkcs11_lib) { + char cmd[120] = {'\0'}; + const char* token_dir = aws_string_c_str(s_pkcs11_tester.token_dir); + if (token_dir[s_pkcs11_tester.token_dir->len - 1] == '/') { + sprintf(cmd, "rm -rf %s*", token_dir); + } else { + sprintf(cmd, "rm -rf %s/*", token_dir); + } + printf("Executing command: %s", cmd); + system(cmd); + next_free_slot_id = 0; + // Reload the library + pkcs11_lib->function_list->C_Finalize(NULL_PTR); + pkcs11_lib->function_list->C_Initialize(NULL_PTR); +} + +static CK_OBJECT_HANDLE s_pkcs11_create_key(struct aws_pkcs11_lib *pkcs11_lib, + CK_SLOT_ID slot_id, + const char* const label, + const char* const id, + CK_SESSION_HANDLE session) { + CK_OBJECT_HANDLE priv_key; + CK_ATTRIBUTE privateKeyTemplate[] = { + { CKA_LABEL, (void *)label, (CK_ULONG)strlen(label) }, + { CKA_ID, (void *)id, (CK_ULONG)strlen(id) } + }; + CK_MECHANISM mech = { CKM_DES_KEY_GEN, NULL_PTR, 0}; + + CK_RV rv = pkcs11_lib->function_list->C_GenerateKey(session, &mech, privateKeyTemplate, 2, &priv_key); + if (rv != CKR_OK) { + FAIL("C_GenerateKey fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + return priv_key; +} + +static uint64_t s_pkcs11_softhsm_create_slot(struct aws_pkcs11_lib *pkcs11_lib, + const char *const token_name, + const char *const so_pin, + const char *const user_pin) { + ASSERT_NOT_NULL(pkcs11_lib); + CK_RV rv; + CK_SLOT_ID slot_id = next_free_slot_id; + + // API expects ' ' padded string + CK_UTF8CHAR paddedLabel[32]; + memset(paddedLabel, ' ', sizeof(paddedLabel)); + memcpy(paddedLabel, token_name, strlen(token_name)); + + rv = pkcs11_lib->function_list->C_InitToken(slot_id, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin), paddedLabel); + if (rv != CKR_OK) { + FAIL("C_InitToken fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + CK_SESSION_HANDLE hSession; + rv = pkcs11_lib->function_list->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession); + if (rv != CKR_OK) { + FAIL("C_OpenSession fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + + rv = pkcs11_lib->function_list->C_Login(hSession, CKU_SO, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin)); + if (rv != CKR_OK) { + FAIL("C_Login fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + + rv = pkcs11_lib->function_list->C_InitPIN(hSession, (CK_UTF8CHAR_PTR)user_pin, strlen(user_pin)); + if (rv != CKR_OK) { + FAIL("C_InitPIN fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + CK_TOKEN_INFO tokenInfo; + rv = pkcs11_lib->function_list->C_GetTokenInfo(slot_id, &tokenInfo); + if (rv != CKR_OK) { + FAIL("C_GetTokenInfo fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + // Reload the library + pkcs11_lib->function_list->C_Finalize(NULL_PTR); + rv = pkcs11_lib->function_list->C_Initialize(NULL_PTR); + if (rv != CKR_OK) { + FAIL("C_Initialize fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); + } + + CK_SLOT_ID new_slot_id = s_pkcs11_find_slot(pkcs11_lib, tokenInfo); + if (slot_id == new_slot_id) { + printf("The token has been initialized on slot %lu\n", new_slot_id); + } else { + printf("The token has been initialized and is reassigned to slot %lu\n", new_slot_id); + } + ++next_free_slot_id; + return (uint64_t)new_slot_id; +} + +/* + * Helper functions to interact with softhsm end + * */ static void s_pkcs11_tester_clean_up(void) { - aws_string_destroy(s_pkcs11_tester.filepath); - aws_string_destroy(s_pkcs11_tester.token_label); - aws_string_destroy(s_pkcs11_tester.pin); - aws_string_destroy(s_pkcs11_tester.pkey_label); + aws_string_destroy(s_pkcs11_tester.shared_lib_path); + aws_string_destroy(s_pkcs11_tester.token_dir); aws_io_library_clean_up(); } @@ -42,26 +187,14 @@ static int s_pkcs11_tester_init(struct aws_allocator *allocator) { aws_io_library_init(allocator); const struct aws_string *env_var = TEST_PKCS11_LIB; - aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.filepath); - if (s_pkcs11_tester.filepath == NULL) { - goto missing; - } - - env_var = TEST_PKCS11_TOKEN_LABEL; - aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.token_label); - if (s_pkcs11_tester.token_label == NULL) { - goto missing; - } - - env_var = TEST_PKCS11_PIN; - aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.pin); - if (s_pkcs11_tester.pin == NULL) { + aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.shared_lib_path); + if (s_pkcs11_tester.shared_lib_path == NULL) { goto missing; } - env_var = TEST_PKCS11_PKEY_LABEL; - aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.pkey_label); - if (s_pkcs11_tester.pkey_label == NULL) { + env_var = TEST_PKCS11_TOKEN_DIR; + aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.token_dir); + if (s_pkcs11_tester.token_dir == NULL) { goto missing; } @@ -79,7 +212,7 @@ static int s_test_pkcs11_lib_initialize(struct aws_allocator *allocator, void *c /* Load library */ struct aws_pkcs11_lib_options options = { - .filename = aws_byte_cursor_from_string(s_pkcs11_tester.filepath), + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), }; struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); @@ -97,11 +230,11 @@ static int s_test_pkcs11_lib_omit_initialize(struct aws_allocator *allocator, vo ASSERT_SUCCESS(s_pkcs11_tester_init(allocator)); struct aws_pkcs11_lib_options options_normal = { - .filename = aws_byte_cursor_from_string(s_pkcs11_tester.filepath), + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), }; struct aws_pkcs11_lib_options options_omit_initialize = { - .filename = aws_byte_cursor_from_string(s_pkcs11_tester.filepath), + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), .omit_initialize = true, }; @@ -130,38 +263,418 @@ static int s_test_pkcs11_lib_omit_initialize(struct aws_allocator *allocator, vo } AWS_TEST_CASE(pkcs11_lib_omit_initialize, s_test_pkcs11_lib_omit_initialize) +static int s_test_pkcs11_session_tests(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + ASSERT_SUCCESS(s_pkcs11_tester_init(allocator)); + + /* Load library */ + struct aws_pkcs11_lib_options options = { + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), + }; + struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); + ASSERT_NOT_NULL(pkcs11_lib); + /* Always start with a clean state */ + s_pkcs11_clear_softhsm(pkcs11_lib); + + /* Try creating a session for an invalid slot */ + uint64_t session = ULONG_MAX; + /* We havent created any slots and we are starting from a clean softhsm, so any slot value is invalid */ + uint64_t slot = 1UL; + ASSERT_FAILS(aws_pkcs11_lib_open_session(pkcs11_lib, slot, &session /*out*/)); + + /* Create a new slot */ + CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, TOKEN_LABEL, SO_PIN, USER_PIN); + printf("Got slot: %lu\n", created_slot); + + uint64_t first_session = ULONG_MAX; + uint64_t second_session = ULONG_MAX; + /* Now, creation of a session on a valid slot will be a success */ + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &first_session /*out*/)); + ASSERT_TRUE(first_session != ULONG_MAX); + + /* create one more session */ + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &second_session /*out*/)); + ASSERT_TRUE(second_session != ULONG_MAX); + ASSERT_TRUE(first_session != second_session); + + /* Close both sessions */ + aws_pkcs11_lib_close_session(pkcs11_lib, first_session); + aws_pkcs11_lib_close_session(pkcs11_lib, second_session); + + /* Clean up */ + s_pkcs11_clear_softhsm(pkcs11_lib); + aws_pkcs11_lib_release(pkcs11_lib); + s_pkcs11_tester_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(pkcs11_session_tests, s_test_pkcs11_session_tests) + +static int s_test_pkcs11_login_tests(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + ASSERT_SUCCESS(s_pkcs11_tester_init(allocator)); + + /* Load library */ + struct aws_pkcs11_lib_options options = { + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), + }; + struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); + ASSERT_NOT_NULL(pkcs11_lib); + /* Always start with a clean state */ + s_pkcs11_clear_softhsm(pkcs11_lib); + + /* Create a new slot */ + CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, TOKEN_LABEL, SO_PIN, USER_PIN); + printf("Got slot: %lu\n", created_slot); + + /* Try to login with in invalid session, we have not created any session on this token + * So, any session value is invalid */ + struct aws_string* pin = aws_string_new_from_c_str(allocator, USER_PIN); + uint64_t invalid_session = 1UL; + ASSERT_FAILS(aws_pkcs11_lib_login_user(pkcs11_lib, invalid_session, pin)); + + /* Now create a valid session */ + uint64_t session; + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session /*out*/)); + + /* Try an invalid pin on a valid slot */ + struct aws_string* invalid_pin = aws_string_new_from_c_str(allocator, "INVALID_PIN"); + ASSERT_FAILS(aws_pkcs11_lib_login_user(pkcs11_lib, session, invalid_pin)); + + /* Try a valid pin on a valid slot */ + ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session, pin)); + + /* A re login should fail, as we are already logged in now */ + ASSERT_FAILS(aws_pkcs11_lib_login_user(pkcs11_lib, session, pin)); + + /* Now create one more session */ + uint64_t session_2; + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_2 /*out*/)); + + /* A re login should fail, as we are already logged in another session and + * the spec only allows login once on any of the session in an application + * */ + ASSERT_FAILS(aws_pkcs11_lib_login_user(pkcs11_lib, session_2, pin)); + + /* Close the first session */ + aws_pkcs11_lib_close_session(pkcs11_lib, session); + + /* A re login should fail again on the second session, as login is only required once */ + ASSERT_FAILS(aws_pkcs11_lib_login_user(pkcs11_lib, session_2, pin)); + + /* Close the second session */ + aws_pkcs11_lib_close_session(pkcs11_lib, session_2); + + /* Clean up */ + aws_string_destroy(pin); + aws_string_destroy(invalid_pin); + s_pkcs11_clear_softhsm(pkcs11_lib); + aws_pkcs11_lib_release(pkcs11_lib); + s_pkcs11_tester_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(pkcs11_login_tests, s_test_pkcs11_login_tests) + static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void *ctx) { (void)ctx; ASSERT_SUCCESS(s_pkcs11_tester_init(allocator)); /* Load library */ struct aws_pkcs11_lib_options options = { - .filename = aws_byte_cursor_from_string(s_pkcs11_tester.filepath), + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), }; struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); - /* Find token slot*/ - uint64_t slot_id; - ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token( - pkcs11_lib, NULL /*match_slot_id*/, s_pkcs11_tester.token_label, &slot_id /*out*/)); + /* Always start with a clean state */ + s_pkcs11_clear_softhsm(pkcs11_lib); + + /* TODO: Add support for all supported key types and lengths */ + const char* const key_label_1 = "DES_KEY"; + const char* const key_id_1 = "BEEFCAFE"; + const char* const key_label_2 = "DES_KEY_2"; + const char* const key_id_2 = "BEEFCAFEDEAD"; + const char* const label_1 = "label!@#$%^&*-_=+{}[]<>?,./():_1"; + const char* const so_pin_1 = "qwertyuioplaksjdhfgbn341269504732"; + const char* const user_pin_1 = "341269504732"; - /* Open session */ - uint64_t session_handle; - ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, slot_id, &session_handle /*out*/)); + /* Create a new slot */ + CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_1, so_pin_1, user_pin_1); + printf("Got slot: %lu\n", created_slot); + + /* Do not close the session while running a test, objects created by a session are cleaned up + * when the session is closed. + * http://docs.oasis-open.org/pkcs11/pkcs11-ug/v2.40/cn02/pkcs11-ug-v2.40-cn02.html#_Toc386027485 + * */ + /* Open a different session to access the created key, and a different one to create */ + uint64_t session_to_access_key; + uint64_t session_to_create_key; + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_to_access_key /*out*/)); + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_to_create_key /*out*/)); /* Login user */ - ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session_handle, s_pkcs11_tester.pin)); + struct aws_string* user_pin = aws_string_new_from_c_str(allocator, user_pin_1); + ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session_to_access_key, user_pin)); + + unsigned long created_key = s_pkcs11_create_key(pkcs11_lib, + created_slot, + key_label_1, + key_id_1, + session_to_create_key); /* Find key */ - uint64_t pkey_handle; + uint64_t pkey_handle = ULONG_MAX; + struct aws_string* key_label_str = aws_string_new_from_c_str(allocator, key_label_1); + ASSERT_SUCCESS( + aws_pkcs11_lib_find_private_key(pkcs11_lib, + session_to_access_key, + key_label_str, + &pkey_handle)); + ASSERT_TRUE(pkey_handle == created_key); + + /* Since there is only one key, a lookup without label should also return the key */ + pkey_handle = ULONG_MAX; + ASSERT_SUCCESS( + aws_pkcs11_lib_find_private_key(pkcs11_lib, + session_to_access_key, + NULL, + &pkey_handle)); + ASSERT_TRUE(pkey_handle == created_key); + + /* Close both sessions for a clean setup again */ + aws_pkcs11_lib_close_session(pkcs11_lib, session_to_access_key); + aws_pkcs11_lib_close_session(pkcs11_lib, session_to_create_key); + + uint64_t session_to_create_key_1; + uint64_t session_to_create_key_2; + + /* Open a session to access the created key */ + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_to_access_key /*out*/)); + /* Open sessions to create keys */ + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_to_create_key_1 /*out*/)); + ASSERT_SUCCESS(aws_pkcs11_lib_open_session(pkcs11_lib, created_slot, &session_to_create_key_2 /*out*/)); + + /* Login user */ + ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session_to_access_key, user_pin)); + + unsigned long created_key_1 = s_pkcs11_create_key(pkcs11_lib, + created_slot, + key_label_1, + key_id_1, + session_to_create_key_1); + unsigned long created_key_2 = s_pkcs11_create_key(pkcs11_lib, + created_slot, + key_label_2, + key_id_2, + session_to_create_key_2); + + /* Since there are 2 keys, a lookup without label should fail */ + struct aws_string* key_label_2_str = aws_string_new_from_c_str(allocator, key_label_2); + pkey_handle = ULONG_MAX; + ASSERT_FAILS( + aws_pkcs11_lib_find_private_key(pkcs11_lib, + session_to_access_key, + NULL, + &pkey_handle)); + + /* a lookup with label for the first key should find the first key */ + pkey_handle = ULONG_MAX; + ASSERT_SUCCESS( + aws_pkcs11_lib_find_private_key(pkcs11_lib, + session_to_access_key, + key_label_str, + &pkey_handle)); + ASSERT_TRUE(pkey_handle == created_key_1); + + /* a lookup with label for the second key should find the second key */ + pkey_handle = ULONG_MAX; ASSERT_SUCCESS( - aws_pkcs11_lib_find_private_key(pkcs11_lib, session_handle, s_pkcs11_tester.pkey_label, &pkey_handle)); + aws_pkcs11_lib_find_private_key(pkcs11_lib, + session_to_access_key, + key_label_2_str, + &pkey_handle)); + ASSERT_TRUE(pkey_handle == created_key_2); /* Clean up */ - aws_pkcs11_lib_close_session(pkcs11_lib, session_handle); + aws_string_destroy(key_label_str); + aws_string_destroy(key_label_2_str); + aws_string_destroy(user_pin); + aws_pkcs11_lib_close_session(pkcs11_lib, session_to_access_key); + aws_pkcs11_lib_close_session(pkcs11_lib, session_to_create_key_1); + aws_pkcs11_lib_close_session(pkcs11_lib, session_to_create_key_2); + s_pkcs11_clear_softhsm(pkcs11_lib); aws_pkcs11_lib_release(pkcs11_lib); s_pkcs11_tester_clean_up(); return AWS_OP_SUCCESS; } AWS_TEST_CASE(pkcs11_find_private_key, s_test_pkcs11_find_private_key) + +static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + ASSERT_SUCCESS(s_pkcs11_tester_init(allocator)); + /* Load library */ + struct aws_pkcs11_lib_options options = { + .filename = aws_byte_cursor_from_string(s_pkcs11_tester.shared_lib_path), + }; + struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); + ASSERT_NOT_NULL(pkcs11_lib); + + /* Always start with a clean state */ + s_pkcs11_clear_softhsm(pkcs11_lib); + + /* softhsm does not like ;| as part of label */ + const char* const label_1 = "label!@#$%^&*-_=+{}[]<>?,./():_1"; + const char* const label_2 = "label!@#$%^&*()_2"; + const char* const label_3 = "label!@#$%^&*()_3"; + + const char* const so_pin_1 = "qwertyuioplaksjdhfgbn341269504732"; + const char* const so_pin_2 = "ABCD"; + const char* const so_pin_3 = "0111"; + + const char* const user_pin_1 = "341269504732"; + const char* const user_pin_2 = "ABCD"; + const char* const user_pin_3 = "0111"; + + uint64_t slot_id = ULONG_LONG_MAX; + /* + * Softhsm always has one uninitialized token which is returned by the GetSlotList() API, + * so there is no way to start without any slot at all + * */ + + /* Call aws_pkcs11_lib_find_slot_with_token with 1 token, but no matching criteria */ + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, NULL /*match_slot_id*/, NULL, &slot_id /*out*/)); + ASSERT_TRUE(slot_id == next_free_slot_id); + + /* Create a new slot */ + uint64_t created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_1, so_pin_1, user_pin_1); + + /* Call aws_pkcs11_lib_find_slot_with_token with 2 tokens, but no matching criteria */ + slot_id = ULONG_LONG_MAX; + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, NULL /*match_slot_id*/, NULL, &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + + /* Call aws_pkcs11_lib_find_slot_with_token with 2 tokens, but match the slot this time */ + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, &created_slot /*match_slot_id*/, NULL, &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot); + + /* Call aws_pkcs11_lib_find_slot_with_token with 2 tokens, but match the label this time */ + slot_id = ULONG_LONG_MAX; + struct aws_string *match_label = aws_string_new_from_c_str(allocator, label_1); + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + NULL /*match_slot_id*/, + match_label, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot); + + /* clear softhsm and make sure that no tokens match with previous slot/label */ + s_pkcs11_clear_softhsm(pkcs11_lib); + + /* + * Call aws_pkcs11_lib_find_slot_with_token with just the free token, + * and assert that previous slot id does not match anymore + * */ + slot_id = ULONG_LONG_MAX; + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot /*match_slot_id*/, + NULL /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + + /* + * Call aws_pkcs11_lib_find_slot_with_token with just the uninitialized token, + * and assert that previous label does not match anymore + * */ + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + NULL /*match_slot_id*/, + match_label /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + + /* Create 2 new slots */ + uint64_t created_slot_1 = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_2, so_pin_2, user_pin_2); + uint64_t created_slot_2 = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_3, so_pin_3, user_pin_3); + + /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens on softhsm, but no matching criteria */ + slot_id = ULONG_LONG_MAX; + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + NULL /*match_slot_id*/, + NULL /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + + /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, but match the slot 1 this time */ + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_1 /*match_slot_id*/, + NULL /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_1); + + /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, but match the slot 2 this time */ + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_2 /*match_slot_id*/, + NULL /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_2); + + /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, but match the label 1 this time */ + slot_id = ULONG_LONG_MAX; + struct aws_string *match_label_1 = aws_string_new_from_c_str(allocator, label_2); + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + NULL /*match_slot_id*/, + match_label_1 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_1); + + /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, but match the label 2 this time */ + slot_id = ULONG_LONG_MAX; + struct aws_string *match_label_2 = aws_string_new_from_c_str(allocator, label_3); + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + NULL /*match_slot_id*/, + match_label_2 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_2); + + /* + * Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, + * but a mismatch for a slot and label should return error + * */ + slot_id = ULONG_LONG_MAX; + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_1 /*match_slot_id*/, + match_label_2 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + + slot_id = ULONG_LONG_MAX; + ASSERT_FAILS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_2 /*match_slot_id*/, + match_label_1 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == ULONG_LONG_MAX); + /* + * Call aws_pkcs11_lib_find_slot_with_token with 3 tokens, + * but match for both, slot and label should return success + * */ + slot_id = ULONG_LONG_MAX; + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_1 /*match_slot_id*/, + match_label_1 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_1); + + slot_id = ULONG_LONG_MAX; + ASSERT_SUCCESS(aws_pkcs11_lib_find_slot_with_token(pkcs11_lib, + &created_slot_2 /*match_slot_id*/, + match_label_2 /*match_token_label*/, + &slot_id /*out*/)); + ASSERT_TRUE(slot_id == created_slot_2); + + /* Clean up */ + aws_string_destroy(match_label); + aws_string_destroy(match_label_1); + aws_string_destroy(match_label_2); + s_pkcs11_clear_softhsm(pkcs11_lib); + aws_pkcs11_lib_release(pkcs11_lib); + s_pkcs11_tester_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(pkcs11_find_slot, s_test_pkcs11_find_slot) \ No newline at end of file From 518ef59654fc50f973b8c612500e442842ca91dd Mon Sep 17 00:00:00 2001 From: prateek-y Date: Wed, 22 Sep 2021 17:18:50 -0700 Subject: [PATCH 2/2] Address PR feedback --- include/aws/io/private/pkcs11_private.h | 14 --- source/pkcs11.c | 18 +++ tests/pkcs11_test.c | 156 +++++++++++++----------- 3 files changed, 106 insertions(+), 82 deletions(-) diff --git a/include/aws/io/private/pkcs11_private.h b/include/aws/io/private/pkcs11_private.h index 92f930f21..dd514bb26 100644 --- a/include/aws/io/private/pkcs11_private.h +++ b/include/aws/io/private/pkcs11_private.h @@ -8,8 +8,6 @@ #include struct aws_pkcs11_lib; -#include -#include /* These defines must exist before the official PKCS#11 headers are included */ #define CK_PTR * @@ -25,18 +23,6 @@ struct aws_pkcs11_lib; struct aws_string; -struct aws_pkcs11_lib { - struct aws_ref_count ref_count; - struct aws_allocator *allocator; - - struct aws_shared_library shared_lib; - - CK_FUNCTION_LIST_PTR function_list; - - /* If true, C_Finalize() should be called when last ref-count is released */ - bool should_finalize; -}; - /** * pkcs11_private.h * This file declares symbols that are private to aws-c-io but need to be diff --git a/source/pkcs11.c b/source/pkcs11.c index 81271d5ce..ca7c863b5 100644 --- a/source/pkcs11.c +++ b/source/pkcs11.c @@ -247,6 +247,18 @@ static CK_RV s_pkcs11_unlock_mutex(CK_VOID_PTR mutex_ptr) { return CKR_OK; } +struct aws_pkcs11_lib { + struct aws_ref_count ref_count; + struct aws_allocator *allocator; + + struct aws_shared_library shared_lib; + + CK_FUNCTION_LIST_PTR function_list; + + /* If true, C_Finalize() should be called when last ref-count is released */ + bool should_finalize; +}; + /* Invoked when last ref-count is released. Free all resources. * Note that this is also called if initialization fails half-way through */ static void s_pkcs11_lib_destroy(void *user_data) { @@ -405,6 +417,12 @@ void aws_pkcs11_lib_release(struct aws_pkcs11_lib *pkcs11_lib) { } } +CK_FUNCTION_LIST_PTR aws_pkcs11_get_function_list(struct aws_pkcs11_lib *pkcs11_lib) { + if (pkcs11_lib) { + return pkcs11_lib->function_list; + } +} + /** * Find the slot that meets all criteria: * - has a token diff --git a/tests/pkcs11_test.c b/tests/pkcs11_test.c index 83060316d..d33652151 100644 --- a/tests/pkcs11_test.c +++ b/tests/pkcs11_test.c @@ -20,8 +20,10 @@ AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_LIB, "TEST_PKCS11_LIB"); AWS_STATIC_STRING_FROM_LITERAL(TEST_PKCS11_TOKEN_DIR, "TEST_PKCS11_TOKEN_DIR"); extern const char *s_ckr_str(CK_RV rv); +extern CK_FUNCTION_LIST_PTR aws_pkcs11_get_function_list(struct aws_pkcs11_lib *pkcs11_lib); /* Singleton that stores env-var values */ struct pkcs11_tester { + struct aws_allocator *allocator; struct aws_string *shared_lib_path; struct aws_string *token_dir; }; @@ -31,28 +33,29 @@ CK_SLOT_ID next_free_slot_id = 0; const char* const TOKEN_LABEL = "label"; const char* const SO_PIN = "qwerty"; const char* const USER_PIN = "341269504732"; + /* * Helper functions to interact with softhsm begin * */ - /* Helper pkcs functions to provision/setup/clear softhsm tokens/keys */ -static CK_SLOT_ID s_pkcs11_find_slot(struct aws_pkcs11_lib *pkcs11_lib, CK_TOKEN_INFO tokenInfo) { +static int s_pkcs11_find_slot(CK_FUNCTION_LIST_PTR pkcs11_function_list, CK_TOKEN_INFO tokenInfo, CK_SLOT_ID *out_slot) { CK_ULONG ul_slot_count; CK_SLOT_ID slot_id = -1; - CK_RV rv = pkcs11_lib->function_list->C_GetSlotList(CK_TRUE, NULL_PTR, &ul_slot_count); + CK_RV rv = pkcs11_function_list->C_GetSlotList(CK_TRUE, NULL_PTR, &ul_slot_count); if (rv != CKR_OK) { FAIL("ERROR: Could not get the number of slots.\n"); } - CK_SLOT_ID_PTR p_slot_list = (CK_SLOT_ID_PTR)malloc(ul_slot_count * sizeof(CK_SLOT_ID)); + CK_SLOT_ID_PTR p_slot_list = (CK_SLOT_ID_PTR) + aws_mem_acquire(s_pkcs11_tester.allocator, ul_slot_count * sizeof(CK_SLOT_ID)); if (p_slot_list == NULL) { FAIL("ERROR: Could not allocate memory.\n"); } - rv = pkcs11_lib->function_list->C_GetSlotList(CK_FALSE, p_slot_list, &ul_slot_count); + rv = pkcs11_function_list->C_GetSlotList(CK_FALSE, p_slot_list, &ul_slot_count); if (rv != CKR_OK) { - free(p_slot_list); + aws_mem_release(s_pkcs11_tester.allocator, p_slot_list); FAIL("ERROR: Could not get the slot list.\n"); } @@ -60,9 +63,9 @@ static CK_SLOT_ID s_pkcs11_find_slot(struct aws_pkcs11_lib *pkcs11_lib, CK_TOKEN for (CK_ULONG i = 0; i < ul_slot_count; i++) { CK_TOKEN_INFO curr_token_info; - rv = pkcs11_lib->function_list->C_GetTokenInfo(p_slot_list[i], &curr_token_info); + rv = pkcs11_function_list->C_GetTokenInfo(p_slot_list[i], &curr_token_info); if (rv != CKR_OK) { - free(p_slot_list); + aws_mem_release(s_pkcs11_tester.allocator, p_slot_list); FAIL("ERROR: Could not get info about the token in slot %lu.\n", p_slot_list[i]); } @@ -73,17 +76,21 @@ static CK_SLOT_ID s_pkcs11_find_slot(struct aws_pkcs11_lib *pkcs11_lib, CK_TOKEN } } - free(p_slot_list); + aws_mem_release(s_pkcs11_tester.allocator, p_slot_list); - if (counter == 1) return slot_id; - if (counter > 1) { + if (counter == 0) { + FAIL("ERROR: Could not find a slot/token using --serial, or --token\n"); + } else if (counter > 1) { FAIL("ERROR: Found multiple matching slots/tokens.\n"); } - FAIL("ERROR: Could not find a slot/token using --serial, or --token\n"); + /* We found just one matching slot */ + *out_slot = slot_id; + return AWS_OP_SUCCESS; } -static void s_pkcs11_clear_softhsm(struct aws_pkcs11_lib *pkcs11_lib) { +static void s_pkcs11_clear_softhsm(CK_FUNCTION_LIST_PTR pkcs11_function_list) { char cmd[120] = {'\0'}; + /* TODO: Support this cross platform, leverage dir util methods from aws-c-common */ const char* token_dir = aws_string_c_str(s_pkcs11_tester.token_dir); if (token_dir[s_pkcs11_tester.token_dir->len - 1] == '/') { sprintf(cmd, "rm -rf %s*", token_dir); @@ -94,15 +101,16 @@ static void s_pkcs11_clear_softhsm(struct aws_pkcs11_lib *pkcs11_lib) { system(cmd); next_free_slot_id = 0; // Reload the library - pkcs11_lib->function_list->C_Finalize(NULL_PTR); - pkcs11_lib->function_list->C_Initialize(NULL_PTR); + pkcs11_function_list->C_Finalize(NULL_PTR); + pkcs11_function_list->C_Initialize(NULL_PTR); } -static CK_OBJECT_HANDLE s_pkcs11_create_key(struct aws_pkcs11_lib *pkcs11_lib, +static int s_pkcs11_create_key(CK_FUNCTION_LIST_PTR pkcs11_function_list, CK_SLOT_ID slot_id, const char* const label, const char* const id, - CK_SESSION_HANDLE session) { + CK_SESSION_HANDLE session, + uint64_t* created_key) { CK_OBJECT_HANDLE priv_key; CK_ATTRIBUTE privateKeyTemplate[] = { { CKA_LABEL, (void *)label, (CK_ULONG)strlen(label) }, @@ -110,18 +118,20 @@ static CK_OBJECT_HANDLE s_pkcs11_create_key(struct aws_pkcs11_lib *pkcs11_lib, }; CK_MECHANISM mech = { CKM_DES_KEY_GEN, NULL_PTR, 0}; - CK_RV rv = pkcs11_lib->function_list->C_GenerateKey(session, &mech, privateKeyTemplate, 2, &priv_key); + CK_RV rv = pkcs11_function_list->C_GenerateKey(session, &mech, privateKeyTemplate, 2, &priv_key); if (rv != CKR_OK) { FAIL("C_GenerateKey fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } - return priv_key; + *created_key = (uint64_t)priv_key; + return AWS_OP_SUCCESS; } -static uint64_t s_pkcs11_softhsm_create_slot(struct aws_pkcs11_lib *pkcs11_lib, - const char *const token_name, - const char *const so_pin, - const char *const user_pin) { - ASSERT_NOT_NULL(pkcs11_lib); +static int s_pkcs11_softhsm_create_slot(CK_FUNCTION_LIST_PTR pkcs11_function_list, + const char *token_name, + const char *so_pin, + const char *user_pin, + uint64_t *created_slot) { + ASSERT_NOT_NULL(pkcs11_function_list); CK_RV rv; CK_SLOT_ID slot_id = next_free_slot_id; @@ -130,45 +140,48 @@ static uint64_t s_pkcs11_softhsm_create_slot(struct aws_pkcs11_lib *pkcs11_lib, memset(paddedLabel, ' ', sizeof(paddedLabel)); memcpy(paddedLabel, token_name, strlen(token_name)); - rv = pkcs11_lib->function_list->C_InitToken(slot_id, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin), paddedLabel); + rv = + pkcs11_function_list->C_InitToken(slot_id, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin), paddedLabel); if (rv != CKR_OK) { FAIL("C_InitToken fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } CK_SESSION_HANDLE hSession; - rv = pkcs11_lib->function_list->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession); + rv = pkcs11_function_list->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession); if (rv != CKR_OK) { FAIL("C_OpenSession fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } - rv = pkcs11_lib->function_list->C_Login(hSession, CKU_SO, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin)); + rv = pkcs11_function_list->C_Login(hSession, CKU_SO, (CK_UTF8CHAR_PTR)so_pin, strlen(so_pin)); if (rv != CKR_OK) { FAIL("C_Login fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } - rv = pkcs11_lib->function_list->C_InitPIN(hSession, (CK_UTF8CHAR_PTR)user_pin, strlen(user_pin)); + rv = pkcs11_function_list->C_InitPIN(hSession, (CK_UTF8CHAR_PTR)user_pin, strlen(user_pin)); if (rv != CKR_OK) { FAIL("C_InitPIN fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } CK_TOKEN_INFO tokenInfo; - rv = pkcs11_lib->function_list->C_GetTokenInfo(slot_id, &tokenInfo); + rv = pkcs11_function_list->C_GetTokenInfo(slot_id, &tokenInfo); if (rv != CKR_OK) { FAIL("C_GetTokenInfo fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } // Reload the library - pkcs11_lib->function_list->C_Finalize(NULL_PTR); - rv = pkcs11_lib->function_list->C_Initialize(NULL_PTR); + pkcs11_function_list->C_Finalize(NULL_PTR); + rv = pkcs11_function_list->C_Initialize(NULL_PTR); if (rv != CKR_OK) { FAIL("C_Initialize fails: PKCS#11 error: %s (0x%08lX)", s_ckr_str(rv), rv); } - CK_SLOT_ID new_slot_id = s_pkcs11_find_slot(pkcs11_lib, tokenInfo); + CK_SLOT_ID new_slot_id; + s_pkcs11_find_slot(pkcs11_function_list, tokenInfo, &new_slot_id); if (slot_id == new_slot_id) { printf("The token has been initialized on slot %lu\n", new_slot_id); } else { printf("The token has been initialized and is reassigned to slot %lu\n", new_slot_id); } ++next_free_slot_id; - return (uint64_t)new_slot_id; + *created_slot = (uint64_t)new_slot_id; + return AWS_OP_SUCCESS; } /* @@ -178,6 +191,7 @@ static uint64_t s_pkcs11_softhsm_create_slot(struct aws_pkcs11_lib *pkcs11_lib, static void s_pkcs11_tester_clean_up(void) { aws_string_destroy(s_pkcs11_tester.shared_lib_path); aws_string_destroy(s_pkcs11_tester.token_dir); + s_pkcs11_tester.allocator = NULL_PTR; aws_io_library_clean_up(); } @@ -189,15 +203,18 @@ static int s_pkcs11_tester_init(struct aws_allocator *allocator) { const struct aws_string *env_var = TEST_PKCS11_LIB; aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.shared_lib_path); if (s_pkcs11_tester.shared_lib_path == NULL) { - goto missing; + s_pkcs11_tester.shared_lib_path = aws_string_new_from_c_str(allocator, "/usr/local/lib/softhsm/libsofthsm2.so"); + //goto missing; } env_var = TEST_PKCS11_TOKEN_DIR; aws_get_environment_value(allocator, env_var, &s_pkcs11_tester.token_dir); if (s_pkcs11_tester.token_dir == NULL) { - goto missing; + s_pkcs11_tester.token_dir = aws_string_new_from_c_str(allocator, "/usr/local/var/lib/softhsm/tokens/"); + //goto missing; } + s_pkcs11_tester.allocator = allocator; return AWS_OP_SUCCESS; missing: @@ -273,8 +290,10 @@ static int s_test_pkcs11_session_tests(struct aws_allocator *allocator, void *ct }; struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); + CK_FUNCTION_LIST_PTR pkcs11_function_list = aws_pkcs11_get_function_list(pkcs11_lib); + /* Always start with a clean state */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); /* Try creating a session for an invalid slot */ uint64_t session = ULONG_MAX; @@ -283,8 +302,8 @@ static int s_test_pkcs11_session_tests(struct aws_allocator *allocator, void *ct ASSERT_FAILS(aws_pkcs11_lib_open_session(pkcs11_lib, slot, &session /*out*/)); /* Create a new slot */ - CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, TOKEN_LABEL, SO_PIN, USER_PIN); - printf("Got slot: %lu\n", created_slot); + uint64_t created_slot = ULONG_MAX; + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, TOKEN_LABEL, SO_PIN, USER_PIN, &created_slot)); uint64_t first_session = ULONG_MAX; uint64_t second_session = ULONG_MAX; @@ -302,7 +321,7 @@ static int s_test_pkcs11_session_tests(struct aws_allocator *allocator, void *ct aws_pkcs11_lib_close_session(pkcs11_lib, second_session); /* Clean up */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); aws_pkcs11_lib_release(pkcs11_lib); s_pkcs11_tester_clean_up(); return AWS_OP_SUCCESS; @@ -319,12 +338,14 @@ static int s_test_pkcs11_login_tests(struct aws_allocator *allocator, void *ctx) }; struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); + CK_FUNCTION_LIST_PTR pkcs11_function_list = aws_pkcs11_get_function_list(pkcs11_lib); /* Always start with a clean state */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); + /* Create a new slot */ - CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, TOKEN_LABEL, SO_PIN, USER_PIN); - printf("Got slot: %lu\n", created_slot); + uint64_t created_slot = ULONG_MAX; + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, TOKEN_LABEL, SO_PIN, USER_PIN, &created_slot)); /* Try to login with in invalid session, we have not created any session on this token * So, any session value is invalid */ @@ -367,7 +388,7 @@ static int s_test_pkcs11_login_tests(struct aws_allocator *allocator, void *ctx) /* Clean up */ aws_string_destroy(pin); aws_string_destroy(invalid_pin); - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); aws_pkcs11_lib_release(pkcs11_lib); s_pkcs11_tester_clean_up(); return AWS_OP_SUCCESS; @@ -385,8 +406,9 @@ static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); + CK_FUNCTION_LIST_PTR pkcs11_function_list = aws_pkcs11_get_function_list(pkcs11_lib); /* Always start with a clean state */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); /* TODO: Add support for all supported key types and lengths */ const char* const key_label_1 = "DES_KEY"; @@ -398,8 +420,8 @@ static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void const char* const user_pin_1 = "341269504732"; /* Create a new slot */ - CK_SLOT_ID created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_1, so_pin_1, user_pin_1); - printf("Got slot: %lu\n", created_slot); + uint64_t created_slot = ULONG_MAX; + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, label_1, so_pin_1, user_pin_1, &created_slot)); /* Do not close the session while running a test, objects created by a session are cleaned up * when the session is closed. @@ -415,11 +437,9 @@ static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void struct aws_string* user_pin = aws_string_new_from_c_str(allocator, user_pin_1); ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session_to_access_key, user_pin)); - unsigned long created_key = s_pkcs11_create_key(pkcs11_lib, - created_slot, - key_label_1, - key_id_1, - session_to_create_key); + uint64_t created_key = ULONG_MAX; + ASSERT_SUCCESS( + s_pkcs11_create_key(pkcs11_function_list, created_slot, key_label_1, key_id_1, session_to_create_key, &created_key)); /* Find key */ uint64_t pkey_handle = ULONG_MAX; @@ -456,16 +476,12 @@ static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void /* Login user */ ASSERT_SUCCESS(aws_pkcs11_lib_login_user(pkcs11_lib, session_to_access_key, user_pin)); - unsigned long created_key_1 = s_pkcs11_create_key(pkcs11_lib, - created_slot, - key_label_1, - key_id_1, - session_to_create_key_1); - unsigned long created_key_2 = s_pkcs11_create_key(pkcs11_lib, - created_slot, - key_label_2, - key_id_2, - session_to_create_key_2); + uint64_t created_key_1 = ULONG_MAX; + uint64_t created_key_2 = ULONG_MAX; + ASSERT_SUCCESS( + s_pkcs11_create_key(pkcs11_function_list, created_slot, key_label_1, key_id_1, session_to_create_key_1, &created_key_1)); + ASSERT_SUCCESS( + s_pkcs11_create_key(pkcs11_function_list, created_slot, key_label_2, key_id_2, session_to_create_key_2, &created_key_2)); /* Since there are 2 keys, a lookup without label should fail */ struct aws_string* key_label_2_str = aws_string_new_from_c_str(allocator, key_label_2); @@ -501,7 +517,7 @@ static int s_test_pkcs11_find_private_key(struct aws_allocator *allocator, void aws_pkcs11_lib_close_session(pkcs11_lib, session_to_access_key); aws_pkcs11_lib_close_session(pkcs11_lib, session_to_create_key_1); aws_pkcs11_lib_close_session(pkcs11_lib, session_to_create_key_2); - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); aws_pkcs11_lib_release(pkcs11_lib); s_pkcs11_tester_clean_up(); return AWS_OP_SUCCESS; @@ -518,8 +534,9 @@ static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { struct aws_pkcs11_lib *pkcs11_lib = aws_pkcs11_lib_new(allocator, &options); ASSERT_NOT_NULL(pkcs11_lib); + CK_FUNCTION_LIST_PTR pkcs11_function_list = aws_pkcs11_get_function_list(pkcs11_lib); /* Always start with a clean state */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); /* softhsm does not like ;| as part of label */ const char* const label_1 = "label!@#$%^&*-_=+{}[]<>?,./():_1"; @@ -545,7 +562,8 @@ static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { ASSERT_TRUE(slot_id == next_free_slot_id); /* Create a new slot */ - uint64_t created_slot = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_1, so_pin_1, user_pin_1); + uint64_t created_slot = ULONG_MAX; + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, label_1, so_pin_1, user_pin_1, &created_slot)); /* Call aws_pkcs11_lib_find_slot_with_token with 2 tokens, but no matching criteria */ slot_id = ULONG_LONG_MAX; @@ -566,7 +584,7 @@ static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { ASSERT_TRUE(slot_id == created_slot); /* clear softhsm and make sure that no tokens match with previous slot/label */ - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); /* * Call aws_pkcs11_lib_find_slot_with_token with just the free token, @@ -590,8 +608,10 @@ static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { ASSERT_TRUE(slot_id == ULONG_LONG_MAX); /* Create 2 new slots */ - uint64_t created_slot_1 = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_2, so_pin_2, user_pin_2); - uint64_t created_slot_2 = s_pkcs11_softhsm_create_slot(pkcs11_lib, label_3, so_pin_3, user_pin_3); + uint64_t created_slot_1 = ULONG_MAX; + uint64_t created_slot_2 = ULONG_MAX; + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, label_2, so_pin_2, user_pin_2, &created_slot_1)); + ASSERT_SUCCESS(s_pkcs11_softhsm_create_slot(pkcs11_function_list, label_3, so_pin_3, user_pin_3, &created_slot_2)); /* Call aws_pkcs11_lib_find_slot_with_token with 3 tokens on softhsm, but no matching criteria */ slot_id = ULONG_LONG_MAX; @@ -672,7 +692,7 @@ static int s_test_pkcs11_find_slot(struct aws_allocator *allocator, void *ctx) { aws_string_destroy(match_label); aws_string_destroy(match_label_1); aws_string_destroy(match_label_2); - s_pkcs11_clear_softhsm(pkcs11_lib); + s_pkcs11_clear_softhsm(pkcs11_function_list); aws_pkcs11_lib_release(pkcs11_lib); s_pkcs11_tester_clean_up(); return AWS_OP_SUCCESS;