Botan 3.13.0
Crypto and TLS for C&
p11_interface.cpp
Go to the documentation of this file.
1/*
2* PKCS #11 Interface Wrapper Implementation
3* (C) 2025 Jack Lloyd
4* 2025 Fabian Albert - Rohde & Schwarz Cybersecurity GmbH
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8#include <botan/p11.h>
9
10#include <algorithm>
11#include <array>
12#include <iterator>
13#include <string_view>
14#include <vector>
15
16namespace Botan::PKCS11 {
17
18namespace {
19
20constexpr std::array<Utf8Char, 8> PKCS11_INTERFACE_NAME_ARR = {"PKCS 11"}; // including \0
21const std::span<const Utf8Char> PKCS11_INTERFACE_NAME(PKCS11_INTERFACE_NAME_ARR.data(),
22 PKCS11_INTERFACE_NAME_ARR.size() - 1);
23
24std::strong_ordering operator<=>(const Version& left, const Version& right) {
25 // Compare both versions by concatenating their bytes: major || minor
26 auto version_value = [](const Version& v) -> uint16_t {
27 return static_cast<uint16_t>(v.major) << 8 | static_cast<uint16_t>(v.minor);
28 };
29 return version_value(left) <=> version_value(right);
30}
31
32bool operator==(const Version& left, const Version& right) {
33 return left.major == right.major && left.minor == right.minor;
34}
35
36Version version_of(const Interface& p11_interface) {
37 // PKCS #11 CK_INTERFACE documentation:
38 // pFunctionList - the interface function list which must always begin with
39 // a CK_VERSION structure as the first field
40 return *reinterpret_cast<Version*>(p11_interface.pFunctionList);
41}
42
43std::span<const Utf8Char> name_of(const Interface& p11_interface) {
44 // We cannot use std::basic_string_view<Utf8Char> since some compilers do not
45 // seem to support string views with unsigned characters.
46 const std::string_view s(reinterpret_cast<char*>(p11_interface.pInterfaceName));
47 return std::span<const Utf8Char>(reinterpret_cast<const Utf8Char*>(s.data()), s.size());
48}
49
50} // namespace
51
52InterfaceWrapper::InterfaceWrapper(Interface p11_interface) : m_p11_interface(p11_interface) {
53 if(p11_interface.pInterfaceName == nullptr || p11_interface.pFunctionList == nullptr) {
54 throw PKCS11_Error("PKCS #11 interface has null pInterfaceName or pFunctionList");
55 }
56}
57
59 return version_of(m_p11_interface);
60}
61
62std::span<const Utf8Char> InterfaceWrapper::name() const {
63 return name_of(m_p11_interface);
64}
65
67 Ulong count = 0;
68 auto rv = LowLevel::C_GetInterfaceList(library, nullptr, &count, nullptr);
69 if(!rv) {
70 // Method could not be executed. Probably due to a cryptoki library with PKCS #11 < 3.0.
71 // Try the legacy C_GetFunctionList method (for PKCS#11 version 2.40).
72 FunctionList* func_list = nullptr; // NOLINT(*-const-correctness) bug in clang-tidy
73 rv = LowLevel::C_GetFunctionList(library, &func_list, nullptr);
74 if(!rv || func_list == nullptr) {
75 throw Invalid_Argument("Failed to load function list for PKCS#11 library.");
76 }
77
80 .pFunctionList = func_list,
81 .flags = 0,
82 }};
83 }
84 std::vector<Interface> interface_list(count);
85 rv = LowLevel::C_GetInterfaceList(library, interface_list.data(), &count, nullptr);
86 if(!rv) {
87 // The interface list count could be computed but the interface list cannot be received. This should not happen.
88 throw Invalid_Argument("Unexpected error while loading PKCS#11 interface list.");
89 }
90
91 // We only load interfaces named "PKCS 11" (which are the pure ones defined in the spec) with
92 // version >= 2.40.
93 auto is_valid_interface = [](const Interface& i) {
94 if(i.pFunctionList == nullptr || i.pInterfaceName == nullptr) {
95 return false;
96 }
97 if(!std::ranges::equal(name_of(i), PKCS11_INTERFACE_NAME)) {
98 return false;
99 }
100 const Version version = version_of(i);
101 return version >= Version{2, 40};
102 };
103 std::vector<Interface> valid_interfaces;
104 std::copy_if(interface_list.begin(), interface_list.end(), std::back_inserter(valid_interfaces), is_valid_interface);
105
106 if(valid_interfaces.empty()) {
107 throw Invalid_Argument("No supported PKCS #11 interfaces found.");
108 }
109
110 // We prioritize valid interfaces the following way:
111 // Higher versions are preferred over lower ones. If multiple interfaces of
112 // the highest version exist, fork safe interfaces are preferred.
113 auto priority_comparator = [](const Interface& left, const Interface& right) {
114 const Version left_version = version_of(left);
115 const Version right_version = version_of(right);
116
117 if(left_version == right_version) {
118 return (left.flags & static_cast<CK_FLAGS>(Flag::InterfaceForkSafe)) <
119 (right.flags & static_cast<CK_FLAGS>(Flag::InterfaceForkSafe));
120 }
121 return left_version < right_version;
122 };
123 auto best_interface = std::max_element(valid_interfaces.begin(), valid_interfaces.end(), priority_comparator);
124 return InterfaceWrapper(*best_interface);
125}
126
128 if(!std::ranges::equal(name(), PKCS11_INTERFACE_NAME)) {
129 throw Botan::Invalid_State("Vendor defined PKCS #11 interfaces are not supported.");
130 }
131 return *reinterpret_cast<FunctionList*>(raw_interface().pFunctionList);
132}
133
135 if(!std::ranges::equal(name(), PKCS11_INTERFACE_NAME)) {
136 throw Botan::Invalid_State("Vendor defined PKCS #11 interfaces are not supported.");
137 }
138 if(version() < Version{3, 0}) {
139 throw Botan::Invalid_State("Loaded interface does not support PKCS #11 v3.0 features");
140 }
141 return *reinterpret_cast<FunctionList30*>(raw_interface().pFunctionList);
142}
143
145 if(!std::ranges::equal(name(), PKCS11_INTERFACE_NAME)) {
146 throw Botan::Invalid_State("Vendor defined PKCS #11 interfaces are not supported.");
147 }
148 if(version() < Version{3, 2}) {
149 throw Botan::Invalid_State("Loaded interface does not support PKCS #11 v3.2 features");
150 }
151 return *reinterpret_cast<FunctionList32*>(raw_interface().pFunctionList);
152}
153
155 static std::array<Utf8Char, 8> STATIC_PKCS11_INTERFACE_NAME_ARR = {"PKCS 11"};
156 return STATIC_PKCS11_INTERFACE_NAME_ARR.data();
157}
158
159} // namespace Botan::PKCS11
const FunctionList32 & func_3_2() const
Access a function list that contains all methods since PKCS #11 v.3.2.
static Utf8Char * p11_interface_name_ptr()
InterfaceWrapper(Interface p11_interface)
Basic constructor using an interface.
std::span< const Utf8Char > name() const
Access the name of the interface.
const Interface & raw_interface() const
Access the underlying interface object.
Definition p11.h:1298
const FunctionList30 & func_3_0() const
Access a function list that contains all methods since PKCS #11 v.3.0.
const FunctionList & func_2_40() const
Access a function list that contains all methods since PKCS #11 v.2.40.
static InterfaceWrapper latest_p11_interface(Dynamically_Loaded_Library &library)
Version version() const
Access the version of the interface.
static bool C_GetFunctionList(const Dynamically_Loaded_Library &pkcs11_module, FunctionList **function_list_ptr_ptr, ReturnValue *return_value=ThrowException)
Definition p11.cpp:90
static bool C_GetInterfaceList(const Dynamically_Loaded_Library &pkcs11_module, Interface *interface_list_ptr, Ulong *count_ptr, ReturnValue *return_value=ThrowException)
Definition p11.cpp:100
CK_FUNCTION_LIST FunctionList
Definition p11.h:1188
CK_VERSION Version
Definition p11.h:1201
CK_INTERFACE Interface
Definition p11.h:1192
CK_FUNCTION_LIST_3_0 FunctionList30
Definition p11.h:1190
CK_UTF8CHAR Utf8Char
Definition p11.h:1209
CK_FUNCTION_LIST_3_2 FunctionList32
Definition p11.h:1191
CK_ULONG Ulong
Definition p11.h:1204
auto operator<=>(const Strong< T, Tags... > &lhs, const Strong< T, Tags... > &rhs)
bool operator==(const AlgorithmIdentifier &x, const AlgorithmIdentifier &y)
Definition alg_id.cpp:54
CK_ULONG CK_FLAGS
Definition pkcs11.h:49
CK_UTF8CHAR * pInterfaceName
Definition pkcs11.h:1306
CK_FLAGS flags
Definition pkcs11.h:1308
void * pFunctionList
Definition pkcs11.h:1307