diff options
Diffstat (limited to 'src/tests/test_pkcs11_high_level.cpp')
-rw-r--r-- | src/tests/test_pkcs11_high_level.cpp | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp new file mode 100644 index 000000000..f68203496 --- /dev/null +++ b/src/tests/test_pkcs11_high_level.cpp @@ -0,0 +1,1509 @@ +/* +* (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> +#include <numeric> + +#if defined(BOTAN_HAS_PKCS11) + #include <botan/p11.h> + #include <botan/p11_slot.h> + #include <botan/p11_session.h> + #include <botan/p11_module.h> + #include <botan/p11_object.h> + #include <botan/p11_randomgenerator.h> +#endif + +#include <botan/der_enc.h> +#include <botan/pubkey.h> + +#if defined(BOTAN_HAS_RSA) + #include <botan/rsa.h> + #include <botan/p11_rsa.h> +#endif + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + #include <botan/ecc_key.h> + #include <botan/ecdsa.h> + #include <botan/ecdh.h> + #include <botan/p11_ecc_key.h> + #include <botan/p11_ecdh.h> + #include <botan/p11_ecdsa.h> +#endif + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + #include <botan/p11_x509.h> +#endif + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif + +#if defined(BOTAN_HAS_HMAC_DRBG) + #include <botan/hmac_drbg.h> +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_PKCS11) + +using namespace Botan; +using namespace PKCS11; + +class TestSession + { + public: + explicit TestSession(bool login) : + m_module(new Module(Test::pkcs11_lib())) + { + std::vector<SlotId> slot_vec = Slot::get_available_slots(*m_module, true); + m_slot.reset(new Slot(*m_module, slot_vec.at(0))); + m_session.reset(new Session(*m_slot, false)); + if(login) + { + m_session->login(UserType::User, PIN_SECVEC); + } + } + + inline Module& module() const { return *m_module; } + inline Slot& slot() const { return *m_slot; } + inline Session& session() const { return *m_session; } + + private: + std::unique_ptr<Module> m_module = nullptr; + std::unique_ptr<Slot> m_slot = nullptr; + std::unique_ptr<Session> m_session = nullptr; + }; + +/***************************** Module *****************************/ + +Test::Result test_module_ctor() + { + Test::Result result("Module ctor"); + + result.test_throws("Module ctor fails for non existent path", []() + { + Module module("/a/b/c"); + }); + + Module module(Test::pkcs11_lib()); + result.test_success("Module ctor did not throw and completed successfully"); + + return result; + } + +Test::Result test_module_reload() + { + Test::Result result("Module reload"); + + Module module(Test::pkcs11_lib()); + + module.reload(); + result.test_success("Module reload did not throw and completed successfully"); + + module.get_info(); + result.test_success("Module get_info() still works after reload"); + + return result; + } + +Test::Result test_multiple_modules() + { + Test::Result result("Module copy"); + Module first_module(Test::pkcs11_lib()); + + result.test_throws("Module ctor fails if module is already initialized", []() + { + Module second_module(Test::pkcs11_lib()); + }); + + return result; + } + +Test::Result test_module_get_info() + { + Test::Result result("Module info"); + + Module module(Test::pkcs11_lib()); + + Info info = module.get_info(); + result.test_ne("Cryptoki version != 0", info.cryptokiVersion.major, 0); + + return result; + } + +class Module_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_module_ctor, + test_multiple_modules, + test_module_get_info, + test_module_reload + + }; + + return run_pkcs11_tests("Module", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-module", Module_Tests); + +/***************************** Slot *****************************/ + +Test::Result test_slot_get_available_slots() + { + Test::Result result("Slot get_available_slots"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + result.test_gte("Available Slots with attached token >= 1", slot_vec.size(), 1); + + return result; + } + +Test::Result test_slot_ctor() + { + Test::Result result("Slot ctor"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + + Slot slot(module, slot_vec.at(0)); + result.test_success("Slot ctor completed successfully"); + result.test_is_eq(slot.slot_id(), slot_vec.at(0)); + + return result; + } + +Test::Result test_get_slot_info() + { + Test::Result result("Slot get_slot_info"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + SlotInfo info = slot.get_slot_info(); + std::string description = reinterpret_cast< char* >(info.slotDescription); + result.confirm("Slot description is not empty", !description.empty()); + + return result; + } + +Test::Result test_get_token_info() + { + Test::Result result("Slot get_token_info"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + TokenInfo info = slot.get_token_info(); + std::string label = reinterpret_cast< char* >(info.label); + result.confirm("Token label is not empty", ! label.empty()); + + return result; + } + +Test::Result test_get_mechanism_list() + { + Test::Result result("Slot get_mechanism_list"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + std::vector<MechanismType> mechanisms = slot.get_mechanism_list(); + result.confirm("The Slot supports at least one mechanism", !mechanisms.empty()); + + return result; + } + +Test::Result test_get_mechanisms_info() + { + Test::Result result("Slot get_mechanism_info"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + slot.get_mechanism_info(MechanismType::RsaPkcsKeyPairGen); + result.test_success("get_mechanism_info() completed successfully."); + + return result; + } + +class Slot_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_slot_get_available_slots, + test_slot_ctor, + test_get_slot_info, + test_get_token_info, + test_get_mechanism_list, + test_get_mechanisms_info + }; + + return run_pkcs11_tests("Slot", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-slot", Slot_Tests); + +/***************************** Session *****************************/ + +Test::Result test_session_ctor() + { + Test::Result result("Session ctor"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + { + Session read_only_session(slot, true); + result.test_success("read only session opened successfully"); + } + { + Session read_write_session(slot, false); + result.test_success("read write session opened successfully"); + } + { + Flags flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + Session read_write_session2(slot, flags, nullptr, nullptr); + result.test_success("read write session with flags param opened successfully"); + } + { + Session read_only_session(slot, true); + Session read_write_session(slot, false); + result.test_success("Opened multiple sessions successfully"); + } + + return result; + } + +Test::Result test_session_release() + { + Test::Result result("Session release/take ownership"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + SessionHandle handle = session.release(); + + Session session2(slot, handle); + result.test_success("releasing ownership and taking ownership works as expected."); + + return result; + } + +Test::Result test_session_login_logout() + { + Test::Result result("Session login/logout"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + session.login(UserType::User, PIN_SECVEC); + session.logoff(); + result.test_success("user login/logout succeeded"); + + session.login(UserType::SO, SO_PIN_SECVEC); + result.test_success("SO login succeeded"); + + return result; + } + +Test::Result test_session_info() + { + Test::Result result("Session session info"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + Session session(slot, false); + SessionInfo info = session.get_info(); + result.test_is_eq("slot id is correct", info.slotID, slot_vec.at(0)); + result.test_is_eq("state is a read write public session", info.state, + static_cast<CK_STATE>(SessionState::RwPublicSession)); + + session.login(UserType::User, PIN_SECVEC); + info = session.get_info(); + result.test_is_eq("state is a read write user session", info.state, + static_cast<CK_STATE>(SessionState::RwUserFunctions)); + + session.logoff(); + result.test_success("user login/logout succeeded"); + + session.login(UserType::SO, SO_PIN_SECVEC); + result.test_success("SO login succeeded"); + + return result; + } + +class Session_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_session_ctor, + test_session_release, + test_session_login_logout, + test_session_info + }; + + return run_pkcs11_tests("Session", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-session", Session_Tests); + +/***************************** Object *****************************/ + +Test::Result test_attribute_container() + { + Test::Result result("AttributeContainer"); + + AttributeContainer attributes; + attributes.add_class(ObjectClass::PrivateKey); + + std::string label("test"); + attributes.add_string(AttributeType::Label, label); + + std::vector<byte> bin(4); + attributes.add_binary(AttributeType::Value, bin); + + attributes.add_bool(AttributeType::Sensitive, true); + attributes.add_numeric(AttributeType::ObjectId, 12); + + result.test_eq("Five elements in attribute container", attributes.count(), 5); + + return result; + } + +Test::Result test_create_destroy_data_object() + { + Test::Result result("Object create/delete data object"); + + TestSession test_session(true); + + std::string value_string("test data"); + secure_vector<byte> value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + + Object data_obj(test_session.session(), data_obj_props); + result.test_success("Data object creation was successful"); + + data_obj.destroy(); + result.test_success("Data object deletion was successful"); + + return result; + } + +Test::Result test_get_set_attribute_values() + { + Test::Result result("Object get/set attributes"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector<byte> value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // get attribute + secure_vector<byte> retrieved_label = data_obj.get_attribute_value(AttributeType::Label); + std::string retrieved_label_string(retrieved_label.begin(), retrieved_label.end()); + result.test_eq("label was set correctly", retrieved_label_string, label); + + // set attribute + std::string new_label = "Botan test modified data object label"; + secure_vector<byte> new_label_secvec(new_label.begin(), new_label.end()); + data_obj.set_attribute_value(AttributeType::Label, new_label_secvec); + + // get and check attribute + retrieved_label = data_obj.get_attribute_value(AttributeType::Label); + retrieved_label_string = std::string(retrieved_label.begin(), retrieved_label.end()); + result.test_eq("label was modified correctly", retrieved_label_string, new_label); + + data_obj.destroy(); + return result; + } + +Test::Result test_object_finder() + { + Test::Result result("ObjectFinder"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector<byte> value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // search created object + AttributeContainer search_template; + search_template.add_string(AttributeType::Label, label); + ObjectFinder finder(test_session.session(), search_template.attributes()); + + auto search_result = finder.find(); + result.test_eq("one object found", search_result.size(), 1); + finder.finish(); + + Object obj_found(test_session.session(), search_result.at(0)); + result.test_eq("found the object just created (same application)", + obj_found.get_attribute_value(AttributeType::Application) , data_obj.get_attribute_value(AttributeType::Application)); + + auto search_result2 = Object::search<Object>(test_session.session(), search_template.attributes()); + result.test_eq("found the object just created (same label)", obj_found.get_attribute_value(AttributeType::Label), + search_result2.at(0).get_attribute_value(AttributeType::Label)); + + data_obj.destroy(); + return result; + } + +Test::Result test_object_copy() + { + Test::Result result("Object copy"); + + TestSession test_session(true); + + // create object + std::string value_string("test data"); + secure_vector<byte> value(value_string.begin(), value_string.end()); + + std::size_t id = 1337; + std::string label = "Botan test data object"; + std::string application = "Botan test application"; + DataObjectProperties data_obj_props; + data_obj_props.set_application(application); + data_obj_props.set_label(label); + data_obj_props.set_value(value); + data_obj_props.set_token(true); + data_obj_props.set_modifiable(true); + data_obj_props.set_object_id(DER_Encoder().encode(id).get_contents_unlocked()); + Object data_obj(test_session.session(), data_obj_props); + + // copy created object + AttributeContainer copy_attributes; + copy_attributes.add_string(AttributeType::Label, "Botan test copied object"); + ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); + + ObjectFinder searcher(test_session.session(), copy_attributes.attributes()); + auto search_result = searcher.find(); + result.test_eq("one object found", search_result.size(), 1); + + data_obj.destroy(); + + Object copied_obj(test_session.session(), copied_obj_handle); + copied_obj.destroy(); + return result; + } + +class Object_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_attribute_container, + test_create_destroy_data_object, + test_get_set_attribute_values, + test_object_finder, + test_object_copy + }; + + return run_pkcs11_tests("Object", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-object", Object_Tests); + +/***************************** PKCS11 RSA *****************************/ + +#if defined(BOTAN_HAS_RSA) + +Test::Result test_rsa_privkey_import() + { + Test::Result result("PKCS11 import RSA private key"); + + TestSession test_session(true); + + // create private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); + props.set_pub_exponent(priv_key.get_e()); + props.set_prime_1(priv_key.get_p()); + props.set_prime_2(priv_key.get_q()); + props.set_coefficient(priv_key.get_c()); + props.set_exponent_1(priv_key.get_d1()); + props.set_exponent_2(priv_key.get_d2()); + + props.set_token(true); + props.set_private(true); + props.set_decrypt(true); + props.set_sign(true); + + PKCS11_RSA_PrivateKey pk(test_session.session(), props); + result.test_success("RSA private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_rsa_privkey_export() + { + Test::Result result("PKCS11 export RSA private key"); + + TestSession test_session(true); + + // create private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); + props.set_pub_exponent(priv_key.get_e()); + props.set_prime_1(priv_key.get_p()); + props.set_prime_2(priv_key.get_q()); + props.set_coefficient(priv_key.get_c()); + props.set_exponent_1(priv_key.get_d1()); + props.set_exponent_2(priv_key.get_d2()); + + props.set_token(true); + props.set_private(true); + props.set_decrypt(true); + props.set_sign(true); + props.set_extractable(true); + props.set_sensitive(false); + + PKCS11_RSA_PrivateKey pk(test_session.session(), props); + + RSA_PrivateKey exported = pk.export_key(); + result.test_success("RSA private key export was successful"); + result.test_eq("pkcs8 private key", pk.pkcs8_private_key(), priv_key.pkcs8_private_key()); + + pk.destroy(); + return result; + } + +Test::Result test_rsa_pubkey_import() + { + Test::Result result("PKCS11 import RSA public key"); + + TestSession test_session(true); + + // create public key from private key + RSA_PrivateKey priv_key(Test::rng(), 2048); + + // import to card + RSA_PublicKeyImportProperties props(priv_key.get_n(), priv_key.get_e()); + props.set_token(true); + props.set_encrypt(true); + props.set_private(false); + + PKCS11_RSA_PublicKey pk(test_session.session(), props); + result.test_success("RSA public key import was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_rsa_generate_private_key() + { + Test::Result result("PKCS11 generate RSA private key"); + TestSession test_session(true); + + RSA_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_sign(true); + props.set_decrypt(true); + + PKCS11_RSA_PrivateKey pk(test_session.session(), 2048, props); + result.test_success("RSA private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_RSA_KeyPair generate_rsa_keypair(const TestSession& test_session) + { + RSA_PublicKeyGenerationProperties pub_props(2048UL); + pub_props.set_pub_exponent(); + pub_props.set_label("BOTAN_TEST_RSA_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_encrypt(true); + pub_props.set_verify(true); + pub_props.set_private(false); + + RSA_PrivateKeyGenerationProperties priv_props; + priv_props.set_label("BOTAN_TEST_RSA_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sign(true); + priv_props.set_decrypt(true); + + return PKCS11::generate_rsa_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_rsa_generate_key_pair() + { + Test::Result result("PKCS11 generate RSA key pair"); + TestSession test_session(true); + + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + result.test_success("RSA key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_rsa_encrypt_decrypt() + { + Test::Result result("PKCS11 RSA encrypt decrypt"); + TestSession test_session(true); + + // generate key pair + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + + auto encrypt_and_decrypt = [&keypair, &result](const std::vector<byte>& plaintext, const std::string& padding) -> void + { + Botan::PK_Encryptor_EME encryptor(keypair.first, padding, "pkcs11"); + auto encrypted = encryptor.encrypt(plaintext, Test::rng()); + + Botan::PK_Decryptor_EME decryptor(keypair.second, padding, "pkcs11"); + auto decrypted = decryptor.decrypt(encrypted); + + // some token / middlewares do not remove the padding bytes + decrypted.resize(plaintext.size()); + + result.test_eq("RSA PKCS11 encrypt and decrypt: " + padding, decrypted, plaintext); + }; + + std::vector<byte> plaintext(256); + std::iota(std::begin(plaintext), std::end(plaintext), 0); + encrypt_and_decrypt(plaintext, "Raw"); + + plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x00 }; + encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5"); + + encrypt_and_decrypt(plaintext, "OAEP(SHA-1)"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_rsa_sign_verify() + { + Test::Result result("PKCS11 RSA sign and verify"); + TestSession test_session(true); + + // generate key pair + PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + + std::vector<byte> plaintext(256); + std::iota(std::begin(plaintext), std::end(plaintext), 0); + + auto sign_and_verify = [&keypair, &plaintext, &result](const std::string& emsa, bool multipart) -> void + { + Botan::PK_Signer signer(keypair.second, emsa, Botan::IEEE_1363, "pkcs11"); + std::vector<byte> signature; + if ( multipart ) + { + signer.update(plaintext.data(), plaintext.size() / 2); + signature = signer.sign_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, Test::rng()); + } + else + { + signature = signer.sign_message(plaintext, Test::rng()); + } + + + Botan::PK_Verifier verifier(keypair.first, emsa, Botan::IEEE_1363, "pkcs11"); + bool rsa_ok = false; + if ( multipart ) + { + verifier.update(plaintext.data(), plaintext.size() / 2); + rsa_ok = verifier.verify_message(plaintext.data() + plaintext.size() / 2, plaintext.size() / 2, signature.data(), signature.size()); + } + else + { + rsa_ok = verifier.verify_message(plaintext, signature); + } + + result.test_eq("RSA PKCS11 sign and verify: " + emsa, rsa_ok, true); + }; + + // single-part sign + sign_and_verify("Raw", false); + sign_and_verify("EMSA3(SHA-256)", false); + sign_and_verify("EMSA4(SHA-256)", false); + + // multi-part sign + sign_and_verify("EMSA3(SHA-256)", true); + sign_and_verify("EMSA4(SHA-256)", true); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +class PKCS11_RSA_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_rsa_privkey_import, + test_rsa_pubkey_import, + test_rsa_privkey_export, + test_rsa_generate_private_key, + test_rsa_generate_key_pair, + test_rsa_encrypt_decrypt, + test_rsa_sign_verify + }; + + return run_pkcs11_tests("PKCS11 RSA", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-rsa", PKCS11_RSA_Tests); +#endif + +/***************************** PKCS11 ECDSA *****************************/ + +#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) + +Test::Result test_ecdsa_privkey_import() + { + Test::Result result("PKCS11 import ECDSA private key"); + + TestSession test_session(true); + + // create ecdsa private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_sign(true); + + // label + std::string label = "Botan test ecdsa key"; + props.set_label(label); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); + result.test_success("ECDSA private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_privkey_export() + { + Test::Result result("PKCS11 export ECDSA private key"); + + TestSession test_session(true); + + // create private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_sign(true); + props.set_extractable(true); + + // label + std::string label = "Botan test ecdsa key"; + props.set_label(label); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); + + ECDSA_PrivateKey exported = pk.export_key(); + result.test_success("ECDSA private key export was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_pubkey_import() + { + Test::Result result("PKCS11 import ECDSA public key"); + + TestSession test_session(true); + + // create ecdsa private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_verify(true); + props.set_private(false); + + // label + std::string label = "Botan test ecdsa pub key"; + props.set_label(label); + + PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + result.test_success("ECDSA public key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdsa_pubkey_export() + { + Test::Result result("PKCS11 export ECDSA public key"); + + TestSession test_session(true); + + // create public key from private key + ECDSA_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_verify(true); + props.set_private(false); + + // label + std::string label = "Botan test ecdsa pub key"; + props.set_label(label); + + PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + + ECDSA_PublicKey exported = pk.export_key(); + result.test_success("ECDSA public key export was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_ecdsa_generate_private_key() + { + Test::Result result("PKCS11 generate ECDSA private key"); + TestSession test_session(true); + + EC_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_sign(true); + + PKCS11_ECDSA_PrivateKey pk(test_session.session(), + EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props); + result.test_success("ECDSA private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(const TestSession& test_session) + { + EC_PublicKeyGenerationProperties pub_props(EC_Group("secp256r1").DER_encode( + EC_Group_Encoding::EC_DOMPAR_ENC_OID)); + pub_props.set_label("BOTAN_TEST_ECDSA_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_verify(true); + pub_props.set_private(false); + pub_props.set_modifiable(true); + + EC_PrivateKeyGenerationProperties priv_props; + priv_props.set_label("BOTAN_TEST_ECDSA_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sensitive(true); + priv_props.set_extractable(false); + priv_props.set_sign(true); + priv_props.set_modifiable(true); + + return PKCS11::generate_ecdsa_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_ecdsa_generate_keypair() + { + Test::Result result("PKCS11 generate ECDSA key pair"); + TestSession test_session(true); + + PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session); + result.test_success("ECDSA key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_ecdsa_sign_verify() + { + Test::Result result("PKCS11 ECDSA sign and verify"); + TestSession test_session(true); + + // generate key pair + PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session); + + std::vector<byte> plaintext(20, 0x01); + + auto sign_and_verify = [ &keypair, &plaintext, &result ](const std::string& emsa) -> void + { + Botan::PK_Signer signer(keypair.second, emsa, Botan::IEEE_1363, "pkcs11"); + auto signature = signer.sign_message(plaintext, Test::rng()); + + Botan::PK_Verifier token_verifier(keypair.first, emsa, Botan::IEEE_1363, "pkcs11"); + bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); + + result.test_eq("ECDSA PKCS11 sign and verify: " + emsa, ecdsa_ok, true); + + Botan::PK_Verifier soft_verifier(keypair.first, emsa, Botan::IEEE_1363); + bool soft_ecdsa_ok = soft_verifier.verify_message(plaintext, signature); + + result.test_eq("ECDSA PKCS11 verify (in software): " + emsa, soft_ecdsa_ok, true); + }; + + sign_and_verify("Raw"); // SoftHSMv2 until now only supports "Raw" + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +class PKCS11_ECDSA_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_ecdsa_privkey_import, + test_ecdsa_privkey_export, + test_ecdsa_pubkey_import, + test_ecdsa_pubkey_export, + test_ecdsa_generate_private_key, + test_ecdsa_generate_keypair, + test_ecdsa_sign_verify + }; + + return run_pkcs11_tests("PKCS11 ECDSA", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-ecdsa", PKCS11_ECDSA_Tests); + +/***************************** PKCS11 ECDH *****************************/ + +Test::Result test_ecdh_privkey_import() + { + Test::Result result("PKCS11 import ECDH private key"); + + TestSession test_session(true); + + // create ecdh private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_derive(true); + + // label + std::string label = "Botan test ecdh key"; + props.set_label(label); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + result.test_success("ECDH private key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_privkey_export() + { + Test::Result result("PKCS11 export ECDH private key"); + + TestSession test_session(true); + + // create private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); + props.set_token(true); + props.set_private(true); + props.set_derive(true); + props.set_extractable(true); + + // label + std::string label = "Botan test ecdh key"; + props.set_label(label); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + + ECDH_PrivateKey exported = pk.export_key(); + result.test_success("ECDH private key export was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_pubkey_import() + { + Test::Result result("PKCS11 import ECDH public key"); + + TestSession test_session(true); + + // create ECDH private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_private(false); + props.set_derive(true); + + // label + std::string label = "Botan test ECDH pub key"; + props.set_label(label); + + PKCS11_ECDH_PublicKey pk(test_session.session(), props); + result.test_success("ECDH public key import was successful"); + + pk.destroy(); + return result; + } + +Test::Result test_ecdh_pubkey_export() + { + Test::Result result("PKCS11 export ECDH public key"); + + TestSession test_session(true); + + // create public key from private key + ECDH_PrivateKey priv_key(Test::rng(), EC_Group("secp256r1")); + priv_key.set_parameter_encoding(EC_Group_Encoding::EC_DOMPAR_ENC_OID); + + // import to card + EC_PublicKeyImportProperties props(priv_key.DER_domain(), DER_Encoder().encode(EC2OSP(priv_key.public_point(), + PointGFp::UNCOMPRESSED), OCTET_STRING).get_contents_unlocked()); + props.set_token(true); + props.set_derive(true); + props.set_private(false); + + // label + std::string label = "Botan test ECDH pub key"; + props.set_label(label); + + PKCS11_ECDH_PublicKey pk(test_session.session(), props); + + ECDH_PublicKey exported = pk.export_key(); + result.test_success("ECDH public key export was successful"); + + pk.destroy(); + + return result; + } + +Test::Result test_ecdh_generate_private_key() + { + Test::Result result("PKCS11 generate ECDH private key"); + TestSession test_session(true); + + EC_PrivateKeyGenerationProperties props; + props.set_token(true); + props.set_private(true); + props.set_derive(true); + + PKCS11_ECDH_PrivateKey pk(test_session.session(), + EC_Group("secp256r1").DER_encode(EC_Group_Encoding::EC_DOMPAR_ENC_OID), props); + result.test_success("ECDH private key generation was successful"); + + pk.destroy(); + + return result; + } + +PKCS11_ECDH_KeyPair generate_ecdh_keypair(const TestSession& test_session, const std::string& label) + { + EC_PublicKeyGenerationProperties pub_props(EC_Group("secp256r1").DER_encode( + EC_Group_Encoding::EC_DOMPAR_ENC_OID)); + pub_props.set_label(label + "_PUB_KEY"); + pub_props.set_token(true); + pub_props.set_derive(true); + pub_props.set_private(false); + pub_props.set_modifiable(true); + + EC_PrivateKeyGenerationProperties priv_props; + priv_props.set_label(label + "_PRIV_KEY"); + priv_props.set_token(true); + priv_props.set_private(true); + priv_props.set_sensitive(true); + priv_props.set_extractable(false); + priv_props.set_derive(true); + priv_props.set_modifiable(true); + + return PKCS11::generate_ecdh_keypair(test_session.session(), pub_props, priv_props); + } + +Test::Result test_ecdh_generate_keypair() + { + Test::Result result("PKCS11 generate ECDH key pair"); + TestSession test_session(true); + + PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + result.test_success("ECDH key pair generation was successful"); + + keypair.first.destroy(); + keypair.second.destroy(); + + return result; + } + +Test::Result test_ecdh_derive() + { + Test::Result result("PKCS11 ECDH derive"); + TestSession test_session(true); + + PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + PKCS11_ECDH_KeyPair keypair2 = generate_ecdh_keypair(test_session, "Botan test ECDH key2"); + + // SoftHSMv2 only supports CKD_NULL KDF at the moment + Botan::PK_Key_Agreement ka(keypair.second, "Raw", "pkcs11"); + Botan::PK_Key_Agreement kb(keypair2.second, "Raw", "pkcs11"); + + Botan::SymmetricKey alice_key = ka.derive_key(32, unlock(EC2OSP(keypair2.first.public_point(), + PointGFp::UNCOMPRESSED))); + Botan::SymmetricKey bob_key = kb.derive_key(32, unlock(EC2OSP(keypair.first.public_point(), PointGFp::UNCOMPRESSED))); + + bool eq = alice_key == bob_key; + result.test_eq("same secret key derived", eq, true); + + keypair.first.destroy(); + keypair.second.destroy(); + keypair2.first.destroy(); + keypair2.second.destroy(); + + return result; + } + +class PKCS11_ECDH_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_ecdh_privkey_import, + test_ecdh_privkey_export, + test_ecdh_pubkey_import, + test_ecdh_pubkey_export, + test_ecdh_generate_private_key, + test_ecdh_generate_keypair, + test_ecdh_derive + }; + + return run_pkcs11_tests("PKCS11 ECDH", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-ecdh", PKCS11_ECDH_Tests); + +#endif + +/***************************** PKCS11 RNG *****************************/ + +Test::Result test_rng_generate_random() + { + Test::Result result("PKCS11 RNG generate random"); + TestSession test_session(true); + + PKCS11_RNG rng(test_session.session()); + + std::vector<byte> random(20); + rng.randomize(random.data(), random.size()); + result.test_ne("random data generated", random, std::vector<byte>(20)); + + return result; + } + +Test::Result test_rng_add_entropy() + { + Test::Result result("PKCS11 RNG add entropy random"); + TestSession test_session(true); + + PKCS11_RNG rng(test_session.session()); + + auto random = Test::rng().random_vec(20); + rng.add_entropy(random.data(), random.size()); + result.test_success("entropy added"); + + return result; + } + +#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_64) + +Test::Result test_pkcs11_hmac_drbg() + { + Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG"); + TestSession test_session(true); + + HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)").release(), new PKCS11_RNG(test_session.session())); + result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG"); + + result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false); + + std::string personalization_string = "Botan PKCS#11 Tests"; + std::vector<byte> personalization_data(personalization_string.begin(), personalization_string.end()); + drbg.add_entropy(personalization_data.data(), personalization_data.size()); + + result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true); + + auto rnd_vec = drbg.random_vec(256); + result.test_ne("HMAC_DRBG generated a random vector", rnd_vec, std::vector<byte>(256)); + + return result; + } +#endif + +class PKCS11_RNG_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_rng_generate_random + ,test_rng_add_entropy +#if defined(BOTAN_HAS_HMAC_DRBG )&& defined(BOTAN_HAS_SHA2_64) + ,test_pkcs11_hmac_drbg +#endif + }; + + return run_pkcs11_tests("PKCS11 RNG", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-rng", PKCS11_RNG_Tests); + +/***************************** PKCS11 token management *****************************/ + +Test::Result test_set_pin() + { + Test::Result result("PKCS11 set pin"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::set_pin(slot, SO_PIN_SECVEC, TEST_PIN_SECVEC); + result.test_success("PIN set with SO_PIN to TEST_PIN"); + + PKCS11::set_pin(slot, SO_PIN_SECVEC, PIN_SECVEC); + result.test_success("PIN changed back with SO_PIN"); + + return result; + } + +Test::Result test_initialize() + { + Test::Result result("PKCS11 initialize token"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::initialize_token(slot, "Botan PKCS#11 tests", SO_PIN_SECVEC, PIN_SECVEC); + result.test_success("token initialized"); + + return result; + } + +Test::Result test_change_pin() + { + Test::Result result("PKCS11 change pin"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::change_pin(slot, PIN_SECVEC, TEST_PIN_SECVEC); + result.test_success("PIN changed with PIN to TEST_PIN"); + + PKCS11::change_pin(slot, TEST_PIN_SECVEC, PIN_SECVEC); + result.test_success("PIN changed back with TEST_PIN to PIN"); + + return result; + } + +Test::Result test_change_so_pin() + { + Test::Result result("PKCS11 change so_pin"); + + Module module(Test::pkcs11_lib()); + std::vector<SlotId> slot_vec = Slot::get_available_slots(module, true); + Slot slot(module, slot_vec.at(0)); + + PKCS11::change_so_pin(slot, SO_PIN_SECVEC, TEST_SO_PIN_SECVEC); + result.test_success("SO_PIN changed with SO_PIN to TEST_SO_PIN"); + + PKCS11::change_so_pin(slot, TEST_SO_PIN_SECVEC, SO_PIN_SECVEC); + result.test_success("SO_PIN changed back with TEST_SO_PIN to SO_PIN"); + + return result; + } + +class PKCS11_Token_Management_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_set_pin, + test_initialize, + test_change_pin, + test_change_so_pin + }; + + return run_pkcs11_tests("PKCS11 token management", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-manage", PKCS11_Token_Management_Tests); + +/***************************** PKCS11 token management *****************************/ + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +Test::Result test_x509_import() + { + Test::Result result("PKCS11 X509 cert import"); + + TestSession test_session(true); + + X509_Certificate root(Test::data_file("nist_x509/test01/end.crt")); + X509_CertificateProperties props(DER_Encoder().encode(root.subject_dn()).get_contents_unlocked(), root.BER_encode()); + props.set_label("Botan PKCS#11 test certificate"); + props.set_private(false); + props.set_token(true); + + PKCS11_X509_Certificate pkcs11_cert(test_session.session(), props); + result.test_success("X509 certificate imported"); + + PKCS11_X509_Certificate pkcs11_cert2(test_session.session(), pkcs11_cert.handle()); + result.test_eq("X509 certificate by handle", pkcs11_cert == pkcs11_cert2, true); + + pkcs11_cert.destroy(); + + return result; + } + +class PKCS11_X509_Tests : public PKCS11_Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<std::function<Test::Result()>> fns = + { + test_x509_import + }; + + return run_pkcs11_tests("PKCS11 X509", fns); + } + }; + +BOTAN_REGISTER_TEST("pkcs11-x509", PKCS11_X509_Tests); + +#endif + +#endif + +} +} |