aboutsummaryrefslogtreecommitdiffstats
path: root/src/tests/test_pkcs11_low_level.cpp
diff options
context:
space:
mode:
authorDaniel Neus <[email protected]>2016-06-17 11:37:18 +0200
committerDaniel Neus <[email protected]>2016-06-17 16:19:40 +0200
commit2ea6f9b1963795dad74489b41bc7d37f897d7a21 (patch)
treec9120503521633ee4a25ac2021b392f33d82e8d7 /src/tests/test_pkcs11_low_level.cpp
parent601f8f6d6075ff2f944c11d357f2309da0c4deb1 (diff)
add PKCS#11 support
Diffstat (limited to 'src/tests/test_pkcs11_low_level.cpp')
-rw-r--r--src/tests/test_pkcs11_low_level.cpp852
1 files changed, 852 insertions, 0 deletions
diff --git a/src/tests/test_pkcs11_low_level.cpp b/src/tests/test_pkcs11_low_level.cpp
new file mode 100644
index 000000000..7b18f6f8b
--- /dev/null
+++ b/src/tests/test_pkcs11_low_level.cpp
@@ -0,0 +1,852 @@
+/*
+* (C) 2016 Daniel Neus
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_pkcs11.h"
+
+#include <string>
+#include <vector>
+#include <functional>
+#include <memory>
+#include <array>
+#include <type_traits>
+#include <map>
+
+#if defined(BOTAN_HAS_PKCS11)
+ #include <botan/p11.h>
+#endif
+
+#if defined(BOTAN_HAS_DYNAMIC_LOADER)
+ #include <botan/dyn_load.h>
+#endif
+
+namespace Botan_Tests {
+
+namespace {
+
+#if defined(BOTAN_HAS_PKCS11)
+#if defined(BOTAN_HAS_DYNAMIC_LOADER)
+
+using namespace Botan;
+using namespace PKCS11;
+
+class RAII_LowLevel
+ {
+ public:
+ RAII_LowLevel() : m_module(Test::pkcs11_lib()), m_func_list(nullptr), m_low_level(), m_session_handle(0),
+ m_is_session_open(false), m_is_logged_in(false)
+ {
+ LowLevel::C_GetFunctionList(m_module, &m_func_list);
+ m_low_level.reset(new LowLevel(m_func_list));
+
+ C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr };
+
+ m_low_level->C_Initialize(&init_args);
+ }
+ ~RAII_LowLevel() BOTAN_NOEXCEPT
+ {
+
+ if(m_is_session_open)
+ {
+ if(m_is_logged_in)
+ {
+ m_low_level.get()->C_Logout(m_session_handle, nullptr);
+ }
+
+ m_low_level.get()->C_CloseSession(m_session_handle, nullptr);
+ }
+
+ m_low_level.get()->C_Finalize(nullptr, nullptr);
+ }
+
+ std::vector<SlotId> get_slots(bool token_present) const
+ {
+ std::vector<SlotId> slots;
+ m_low_level.get()->C_GetSlotList(token_present, slots);
+
+ if(slots.empty())
+ {
+ throw Exception("No slot with attached token found");
+ }
+
+ return slots;
+ }
+
+ inline SessionHandle open_session(Flags session_flags)
+ {
+ std::vector<SlotId> slots = get_slots(true);
+ m_low_level.get()->C_OpenSession(slots.at(0), session_flags, nullptr, nullptr, &m_session_handle);
+ m_is_session_open = true;
+ return m_session_handle;
+ }
+
+ inline SessionHandle open_rw_session_with_user_login()
+ {
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle handle = open_session(session_flags);
+ login(UserType::User, PIN_SECVEC);
+ return handle;
+ }
+
+ inline SessionHandle get_session_handle() const
+ {
+ if(!m_is_session_open)
+ {
+ throw Exception("no open session");
+ }
+ return m_session_handle;
+ }
+
+ inline void close_session()
+ {
+ if(!m_is_session_open)
+ {
+ throw Exception("no open session");
+ }
+
+ m_low_level.get()->C_CloseSession(m_session_handle);
+ m_is_session_open = false;
+ }
+
+ inline void login(UserType user_type, const secure_vector<byte>& pin)
+ {
+ if(!m_is_session_open)
+ {
+ throw Exception("no open session");
+ }
+
+ if(m_is_logged_in)
+ {
+ throw Exception("Already logged in");
+ }
+
+ m_low_level.get()->C_Login(m_session_handle, user_type, pin);
+ m_is_logged_in = true;
+ }
+
+ inline void logout()
+ {
+ if(!m_is_logged_in)
+ {
+ throw Exception("Not logged in");
+ }
+
+ m_low_level.get()->C_Logout(m_session_handle);
+ m_is_logged_in = false;
+ }
+
+ LowLevel* get() const
+ {
+ return m_low_level.get();
+ }
+ private:
+ Dynamically_Loaded_Library m_module;
+ FunctionListPtr m_func_list;
+ std::unique_ptr<LowLevel> m_low_level;
+ SessionHandle m_session_handle;
+ bool m_is_session_open;
+ bool m_is_logged_in;
+ };
+
+bool no_op(ReturnValue*)
+ {
+ return true;
+ }
+
+using PKCS11_BoundTestFunction = std::function<bool(ReturnValue* return_value)>;
+
+// tests all 3 variants
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+ const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func,
+ bool expect_failure, ReturnValue expected_return_value)
+ {
+ std::string test_name = revert_fn_name.empty() ? "PKCS 11 low level - " + name : "PKCS 11 low level - " + name + "/" +
+ revert_fn_name;
+ Test::Result result(test_name);
+
+ // test throw variant
+ if(expect_failure)
+ {
+ result.test_throws(name + " fails as expected", [ test_func ]()
+ {
+ test_func(ThrowException);
+ });
+ }
+ else
+ {
+ test_func(ThrowException);
+ result.test_success(name + " did not throw and completed successfully");
+
+ if(!revert_fn_name.empty())
+ {
+ revert_func(ThrowException);
+ result.test_success(revert_fn_name + " did not throw and completed successfully");
+ }
+ }
+
+ // test bool return variant
+ bool success = test_func(nullptr);
+ result.test_eq(name, success, !expect_failure);
+ if(success && !revert_fn_name.empty())
+ {
+ success = revert_func(nullptr);
+ result.test_eq(revert_fn_name, success, !expect_failure);
+ }
+
+ // test ReturnValue variant
+ ReturnValue rv;
+ success = test_func(&rv);
+ result.test_eq(name, success, !expect_failure);
+ if(!expect_failure)
+ {
+ result.test_rc_ok(name, static_cast< uint32_t >(rv));
+ }
+ else
+ {
+ result.test_rc_fail(name, "return value should be: " + std::to_string(static_cast< uint32_t >(expected_return_value)),
+ static_cast< uint32_t >(rv));
+ }
+
+ if(success && !revert_fn_name.empty())
+ {
+ success = revert_func(&rv);
+ result.test_eq(revert_fn_name, success, !expect_failure);
+ result.test_rc_ok(revert_fn_name, static_cast< uint32_t >(rv));
+ }
+
+ return result;
+ }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+ bool expect_failure, ReturnValue expected_return_value)
+ {
+ return test_function(name, test_func, std::string(), no_op, expect_failure, expected_return_value);
+ }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func)
+ {
+ return test_function(name, test_func, std::string(), no_op, false, ReturnValue::OK);
+ }
+
+Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func,
+ const std::string& revert_fn_name, const PKCS11_BoundTestFunction& revert_func)
+ {
+ return test_function(name, test_func, revert_fn_name, revert_func, false, ReturnValue::OK);
+ }
+
+Test::Result test_low_level_ctor()
+ {
+ Test::Result result("PKCS 11 low level - LowLevel ctor");
+
+ Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+ FunctionListPtr func_list(nullptr);
+ LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
+
+ LowLevel p11_low_level(func_list);
+ result.test_success("LowLevel ctor does complete for valid function list");
+
+ result.test_throws("LowLevel ctor fails for invalid function list pointer", []()
+ {
+ LowLevel p11_low_level2(nullptr);
+ });
+
+ return result;
+ }
+
+Test::Result test_c_get_function_list()
+ {
+ Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+ FunctionListPtr func_list = nullptr;
+ return test_function("C_GetFunctionList", std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list,
+ std::placeholders::_1));
+ }
+
+Test::Result test_initialize_finalize()
+ {
+ Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
+ FunctionListPtr func_list = nullptr;
+ LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
+
+ LowLevel p11_low_level(func_list);
+
+ // setting Flag::OsLockingOk should be the normal use case
+ C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr };
+
+ auto init_bind = std::bind(&LowLevel::C_Initialize, p11_low_level, &init_args, std::placeholders::_1);
+ auto finalize_bind = std::bind(&LowLevel::C_Finalize, p11_low_level, nullptr, std::placeholders::_1);
+ return test_function("C_Initialize", init_bind, "C_Finalize", finalize_bind);
+ }
+
+Test::Result test_c_get_info()
+ {
+ RAII_LowLevel p11_low_level;
+
+ Info info = {};
+ Test::Result result = test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, *p11_low_level.get(), &info,
+ std::placeholders::_1));
+ result.test_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0);
+
+ return result;
+ }
+
+Test::Result test_c_get_slot_list()
+ {
+ RAII_LowLevel p11_low_level;
+
+ std::vector<SlotId> slot_vec;
+
+ // assumes at least one smartcard reader is attached
+ bool token_present = false;
+
+ auto binder = std::bind(static_cast< bool (LowLevel::*)(bool, std::vector<SlotId>&, ReturnValue*) const>
+ (&LowLevel::C_GetSlotList), *p11_low_level.get(), std::ref(token_present), std::ref(slot_vec), std::placeholders::_1);
+
+ Test::Result result = test_function("C_GetSlotList", binder);
+ result.test_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0);
+
+ // assumes at least one smartcard reader with connected smartcard is attached
+ slot_vec.clear();
+ token_present = true;
+ result.merge(test_function("C_GetSlotList", binder));
+ result.test_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0);
+
+ return result;
+ }
+
+Test::Result test_c_get_slot_info()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(false);
+
+ SlotInfo slot_info = {};
+ Test::Result result = test_function("C_GetSlotInfo", std::bind(&LowLevel::C_GetSlotInfo, *p11_low_level.get(),
+ slot_vec.at(0), &slot_info, std::placeholders::_1));
+
+ std::string slot_desc(reinterpret_cast< char* >(slot_info.slotDescription));
+ result.test_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0);
+
+ return result;
+ }
+
+Test::Result test_c_get_token_info()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ TokenInfo token_info = {};
+ Test::Result result = test_function("C_GetTokenInfo", std::bind(&LowLevel::C_GetTokenInfo, *p11_low_level.get(),
+ slot_vec.at(0), &token_info, std::placeholders::_1));
+
+ std::string serial(reinterpret_cast< char* >(token_info.serialNumber));
+ result.test_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0);
+
+ return result;
+ }
+
+Test::Result test_c_wait_for_slot_event()
+ {
+ RAII_LowLevel p11_low_level;
+
+ Flags flags = PKCS11::flags(Flag::DontBlock);
+ SlotId slot_id = 0;
+
+ return test_function("C_WaitForSlotEvent", std::bind(&LowLevel::C_WaitForSlotEvent, *p11_low_level.get(),
+ flags, &slot_id, nullptr, std::placeholders::_1), true, ReturnValue::NoEvent);
+ }
+
+Test::Result test_c_get_mechanism_list()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ std::vector<MechanismType> mechanisms;
+
+ auto binder = std::bind(static_cast< bool (LowLevel::*)(SlotId, std::vector<MechanismType>&, ReturnValue*) const>
+ (&LowLevel::C_GetMechanismList), *p11_low_level.get(), slot_vec.at(0), std::ref(mechanisms), std::placeholders::_1);
+
+ Test::Result result = test_function("C_GetMechanismList", binder);
+ result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty());
+
+ return result;
+ }
+
+Test::Result test_c_get_mechanism_info()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ std::vector<MechanismType> mechanisms;
+ p11_low_level.get()->C_GetMechanismList(slot_vec.at(0), mechanisms);
+
+ MechanismInfo mechanism_info = {};
+ return test_function("C_GetMechanismInfo", std::bind(&LowLevel::C_GetMechanismInfo, *p11_low_level.get(),
+ slot_vec.at(0), mechanisms.at(0), &mechanism_info, std::placeholders::_1));
+ }
+
+Test::Result test_c_init_token()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ const std::string label = "Botan PKCS#11 tests";
+
+ auto sec_vec_binder = std::bind(
+ static_cast< bool (LowLevel::*)(SlotId, const secure_vector<byte>&, const std::string&, ReturnValue*) const>
+ (&LowLevel::C_InitToken<secure_allocator<byte>>), *p11_low_level.get(), slot_vec.at(0), std::ref(SO_PIN_SECVEC),
+ std::ref(label), std::placeholders::_1);
+
+ return test_function("C_InitToken", sec_vec_binder);
+ }
+
+Test::Result test_open_close_session()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ // public read only session
+ Flags flags = PKCS11::flags(Flag::SerialSession);
+ SessionHandle session_handle = 0;
+
+ auto open_session_bind = std::bind(&LowLevel::C_OpenSession, *p11_low_level.get(),
+ slot_vec.at(0), std::ref(flags), nullptr, nullptr, &session_handle, std::placeholders::_1);
+
+ auto close_session_bind = std::bind(&LowLevel::C_CloseSession, *p11_low_level.get(),
+ std::ref(session_handle), std::placeholders::_1);
+
+ Test::Result result = test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind);
+
+ // public read write session
+ flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ result.merge(test_function("C_OpenSession", open_session_bind, "C_CloseSession", close_session_bind));
+
+ return result;
+ }
+
+Test::Result test_c_close_all_sessions()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ auto open_two_sessions = [ &slot_vec, &p11_low_level ]() -> void
+ {
+ // public read only session
+ Flags flags = PKCS11::flags(Flag::SerialSession);
+ SessionHandle first_session_handle = 0, second_session_handle = 0;
+
+ p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &first_session_handle);
+
+ flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &second_session_handle);
+ };
+
+ open_two_sessions();
+
+ Test::Result result("PKCS 11 low level - C_CloseAllSessions");
+
+ // test throw variant
+ p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0));
+ result.test_success("C_CloseAllSessions does not throw");
+
+ // test bool return variant
+ open_two_sessions();
+
+ bool success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), nullptr);
+ result.test_eq("C_CloseAllSessions", success, true);
+
+ // test ReturnValue variant
+ open_two_sessions();
+
+ ReturnValue rv = static_cast< ReturnValue >(-1);
+ success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), &rv);
+ result.test_eq("C_CloseAllSessions", success, true);
+ result.test_rc_ok("C_CloseAllSessions", static_cast< uint32_t >(rv));
+
+ return result;
+ }
+
+Test::Result test_c_get_session_info()
+ {
+ RAII_LowLevel p11_low_level;
+ std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
+
+ // public read only session
+ Flags flags = PKCS11::flags(Flag::SerialSession);
+ SessionHandle session_handle = p11_low_level.open_session(flags);
+
+ SessionInfo session_info = {};
+ Test::Result result = test_function("C_GetSessionInfo", std::bind(&LowLevel::C_GetSessionInfo, *p11_low_level.get(),
+ session_handle, &session_info, std::placeholders::_1));
+
+ result.confirm("C_GetSessionInfo returns same slot id as during call to C_OpenSession",
+ session_info.slotID == slot_vec.at(0));
+ result.confirm("C_GetSessionInfo returns same flags as during call to C_OpenSession", session_info.flags == flags);
+ result.confirm("C_GetSessionInfo returns public read only session state",
+ session_info.state == static_cast<CK_FLAGS>(SessionState::RoPublicSession));
+
+ return result;
+ }
+
+Test::Result login_logout_helper(const RAII_LowLevel& p11_low_level, SessionHandle handle, UserType user_type,
+ const std::string& pin)
+ {
+ secure_vector<byte> pin_as_sec_vec(pin.begin(), pin.end());
+
+ auto login_secvec_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, UserType,
+ const secure_vector<byte>&, ReturnValue*) const>
+ (&LowLevel::C_Login<secure_allocator<byte>>), *p11_low_level.get(),
+ handle, user_type, std::ref(pin_as_sec_vec), std::placeholders::_1);
+
+ auto logout_binder = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ReturnValue*) const>
+ (&LowLevel::C_Logout), *p11_low_level.get(), handle, std::placeholders::_1);
+
+ return test_function("C_Login", login_secvec_binder, "C_Logout", logout_binder);
+ }
+
+Test::Result test_c_login_logout_security_officier()
+ {
+ RAII_LowLevel p11_low_level;
+
+ // can only login to R/W session
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+ return login_logout_helper(p11_low_level, session_handle, UserType::SO, SO_PIN);
+ }
+
+Test::Result test_c_login_logout_user()
+ {
+ RAII_LowLevel p11_low_level;
+
+ // R/O session
+ Flags session_flags = PKCS11::flags(Flag::SerialSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+ Test::Result result = login_logout_helper(p11_low_level, session_handle, UserType::User, PIN);
+ p11_low_level.close_session();
+
+ // R/W session
+ session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ session_handle = p11_low_level.open_session(session_flags);
+
+ result.merge(login_logout_helper(p11_low_level, session_handle, UserType::User, PIN));
+
+ return result;
+ }
+
+Test::Result test_c_init_pin()
+ {
+ RAII_LowLevel p11_low_level;
+
+ // C_InitPIN can only be called in the "R/W SO Functions" state
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+ p11_low_level.login(UserType::SO, SO_PIN_SECVEC);
+
+ auto sec_vec_binder = std::bind(
+ static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector<byte>&, ReturnValue*) const>
+ (&LowLevel::C_InitPIN<secure_allocator<byte>>), *p11_low_level.get(), session_handle, std::ref(PIN_SECVEC),
+ std::placeholders::_1);
+
+ return test_function("C_InitPIN", sec_vec_binder);
+ }
+
+Test::Result test_c_set_pin()
+ {
+ RAII_LowLevel p11_low_level;
+
+ // C_SetPIN can only be called in the "R / W Public Session" state, "R / W SO Functions" state, or "R / W User Functions" state
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+ // now we are in "R / W Public Session" state: this will change the pin of the user
+
+ auto get_pin_bind = [ &session_handle, &p11_low_level ](const secure_vector<byte>& old_pin,
+ const secure_vector<byte>& new_pin) -> PKCS11_BoundTestFunction
+ {
+ return std::bind(static_cast< bool (LowLevel::*)(SessionHandle, const secure_vector<byte>&,
+ const secure_vector<byte>&, ReturnValue*) const>
+ (&LowLevel::C_SetPIN<secure_allocator<byte>>), *p11_low_level.get(), session_handle,
+ old_pin, new_pin, std::placeholders::_1);
+ };
+
+ const std::string test_pin("654321");
+ const auto test_pin_secvec = secure_vector<byte>(test_pin.begin(), test_pin.end());
+
+ PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN_SECVEC, test_pin_secvec);
+ PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN_SECVEC);
+
+ Test::Result result = test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind);
+
+ // change pin in "R / W User Functions" state
+ p11_low_level.login(UserType::User, PIN_SECVEC);
+
+ result.merge(test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind));
+ p11_low_level.logout();
+
+ // change so_pin in "R / W SO Functions" state
+ const std::string test_so_pin = "87654321";
+ secure_vector<byte> test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end());
+ p11_low_level.login(UserType::SO, SO_PIN_SECVEC);
+
+ PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN_SECVEC, test_so_pin_secvec);
+ PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN_SECVEC);
+
+ result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind));
+
+ return result;
+ }
+
+// Simple data object
+ObjectClass object_class = ObjectClass::Data;
+std::string label = "A data object";
+std::string data = "Sample data";
+Bbool btrue = True;
+
+std::array<Attribute, 4> dtemplate =
+ {
+ {
+ { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Class), &object_class, sizeof(object_class) },
+ { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Token), &btrue, sizeof(btrue) },
+ { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Label), const_cast< char* >(label.c_str()), label.size() },
+ { static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Value), const_cast< char* >(data.c_str()), data.size() }
+ }
+ };
+
+ObjectHandle create_simple_data_object(const RAII_LowLevel& p11_low_level)
+ {
+ ObjectHandle object_handle;
+ p11_low_level.get()->C_CreateObject(p11_low_level.get_session_handle(), dtemplate.data(), dtemplate.size(),
+ &object_handle);
+ return object_handle;
+ }
+
+Test::Result test_c_create_object_c_destroy_object()
+ {
+ RAII_LowLevel p11_low_level;
+ SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+ ObjectHandle object_handle(0);
+
+ auto create_bind = std::bind(&LowLevel::C_CreateObject, *p11_low_level.get(),
+ session_handle, dtemplate.data(), dtemplate.size(), &object_handle, std::placeholders::_1);
+
+ auto destroy_bind = std::bind(&LowLevel::C_DestroyObject, *p11_low_level.get(), session_handle,
+ std::ref(object_handle), std::placeholders::_1);
+
+ return test_function("C_CreateObject", create_bind, "C_DestroyObject", destroy_bind);
+ }
+
+Test::Result test_c_get_object_size()
+ {
+ RAII_LowLevel p11_low_level;
+
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+ p11_low_level.login(UserType::User, PIN_SECVEC);
+
+ ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+ Ulong object_size = 0;
+
+ auto bind = std::bind(&LowLevel::C_GetObjectSize, *p11_low_level.get(),
+ session_handle, object_handle, &object_size, std::placeholders::_1);
+
+ Test::Result result = test_function("C_GetObjectSize", bind);
+ result.test_ne("Object size", object_size, 0);
+
+ // cleanup
+ p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+ return result;
+ }
+
+Test::Result test_c_get_attribute_value()
+ {
+ RAII_LowLevel p11_low_level;
+ SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+ ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+
+ std::map < AttributeType, secure_vector<byte>> getter =
+ {
+ { AttributeType::Label, secure_vector<byte>() },
+ { AttributeType::Value, secure_vector<byte>() }
+ };
+
+ auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle,
+ std::map<AttributeType, secure_vector<byte>>&, ReturnValue*) const>
+ (&LowLevel::C_GetAttributeValue<secure_allocator<byte>>), *p11_low_level.get(), session_handle,
+ object_handle, std::ref(getter), std::placeholders::_1);
+
+ Test::Result result = test_function("C_GetAttributeValue", bind);
+
+ std::string _label(getter[ AttributeType::Label ].begin(), getter[ AttributeType::Label ].end());
+ std::string value(getter[ AttributeType::Value ].begin(), getter[ AttributeType::Value ].end());
+ result.test_eq("label", _label, "A data object");
+ result.test_eq("value", value, "Sample data");
+
+ // cleanup
+ p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+ return result;
+ }
+
+std::map < AttributeType, std::vector<byte>> get_attribute_values(const RAII_LowLevel& p11_low_level,
+ SessionHandle session_handle,
+ ObjectHandle object_handle, const std::vector<AttributeType>& attribute_types)
+ {
+ std::map < AttributeType, std::vector<byte>> received_attributes;
+
+ for(const auto& type : attribute_types)
+ {
+ received_attributes.emplace(type, std::vector<byte>());
+ }
+
+ p11_low_level.get()->C_GetAttributeValue(session_handle, object_handle, received_attributes);
+
+ return received_attributes;
+ }
+
+Test::Result test_c_set_attribute_value()
+ {
+ RAII_LowLevel p11_low_level;
+
+ Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
+ SessionHandle session_handle = p11_low_level.open_session(session_flags);
+
+ p11_low_level.login(UserType::User, PIN_SECVEC);
+
+ ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+
+ std::string new_label = "A modified data object";
+
+ std::map < AttributeType, secure_vector<byte>> new_attributes =
+ {
+ { AttributeType::Label, secure_vector<byte>(new_label.begin(), new_label.end()) }
+ };
+
+ auto bind = std::bind(static_cast< bool (LowLevel::*)(SessionHandle, ObjectHandle,
+ std::map<AttributeType, secure_vector<byte>>&, ReturnValue*) const>
+ (&LowLevel::C_SetAttributeValue<secure_allocator<byte>>), *p11_low_level.get(), session_handle,
+ object_handle, std::ref(new_attributes), std::placeholders::_1);
+
+ Test::Result result = test_function("C_SetAttributeValue", bind);
+
+ // get attributes and check if they are changed correctly
+ std::vector<AttributeType> types = { AttributeType::Label, AttributeType::Value };
+ auto received_attributes = get_attribute_values(p11_low_level, session_handle, object_handle, types);
+
+ std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(),
+ received_attributes[ AttributeType::Label ].end());
+
+ result.test_eq("label", new_label, retrieved_label);
+
+ // cleanup
+ p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+
+ return result;
+ }
+
+Test::Result test_c_copy_object()
+ {
+ RAII_LowLevel p11_low_level;
+ SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
+
+ ObjectHandle object_handle = create_simple_data_object(p11_low_level);
+ ObjectHandle copied_object_handle = 0;
+
+ std::string copied_label = "A copied data object";
+
+ Attribute copy_attribute_values = { static_cast< CK_ATTRIBUTE_TYPE >(AttributeType::Label), const_cast< char* >(copied_label.c_str()), copied_label.size() };
+
+ auto binder = std::bind(&LowLevel::C_CopyObject, *p11_low_level.get(), session_handle, object_handle,
+ &copy_attribute_values, 1, &copied_object_handle, std::placeholders::_1);
+
+ Test::Result result = test_function("C_CopyObject", binder);
+
+ // get attributes and check if its copied correctly
+ std::vector<AttributeType> types = { AttributeType::Label, AttributeType::Value };
+ auto received_attributes = get_attribute_values(p11_low_level, session_handle, copied_object_handle, types);
+
+ std::string retrieved_label(received_attributes[ AttributeType::Label ].begin(),
+ received_attributes[ AttributeType::Label ].end());
+
+ result.test_eq("label", copied_label, retrieved_label);
+
+ // cleanup
+ p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
+ p11_low_level.get()->C_DestroyObject(session_handle, copied_object_handle);
+
+ return result;
+ }
+
+class LowLevelTests : public Test
+ {
+ public:
+ std::vector<Test::Result> run() override
+ {
+ std::vector<Test::Result> results;
+
+ std::vector<std::function<Test::Result()>> fns =
+ {
+ test_c_get_function_list
+ ,test_low_level_ctor
+ ,test_initialize_finalize
+ ,test_c_get_info
+ ,test_c_get_slot_list
+ ,test_c_get_slot_info
+ ,test_c_get_token_info
+ ,test_c_wait_for_slot_event
+ ,test_c_get_mechanism_list
+ ,test_c_get_mechanism_info
+ ,test_open_close_session
+ ,test_c_close_all_sessions
+ ,test_c_get_session_info
+ ,test_c_init_token
+ ,test_c_login_logout_security_officier /* only possible if token is initialized */
+ ,test_c_init_pin
+ ,test_c_login_logout_user /* only possible if token is initialized and user pin is set */
+ ,test_c_set_pin
+ ,test_c_create_object_c_destroy_object
+ ,test_c_get_object_size
+ ,test_c_get_attribute_value
+ ,test_c_set_attribute_value
+ ,test_c_copy_object
+ };
+
+ for(size_t i = 0; i != fns.size(); ++i)
+ {
+ try
+ {
+ results.push_back(fns[ i ]());
+ }
+ catch(PKCS11_ReturnError& e)
+ {
+ results.push_back(Test::Result::Failure("PKCS11 low level test " + std::to_string(i), e.what()));
+
+ if(e.get_return_value() == ReturnValue::PinIncorrect)
+ {
+ break; // Do not continue to not potentially lock the token
+ }
+ }
+ catch(std::exception& e)
+ {
+ results.push_back(Test::Result::Failure("PKCS11 low level test " + std::to_string(i), e.what()));
+ }
+ }
+
+ return results;
+ }
+ };
+
+BOTAN_REGISTER_TEST("pkcs11-lowlevel", LowLevelTests);
+
+#endif
+#endif
+
+}
+}