• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 21783203606

07 Feb 2026 04:30PM UTC coverage: 90.068% (-0.005%) from 90.073%
21783203606

Pull #5295

github

web-flow
Merge a6c023b97 into ebf8f0044
Pull Request #5295: Reduce header dependencies in tests and cli

102233 of 113507 relevant lines covered (90.07%)

11542227.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.68
/src/tests/test_pkcs11_low_level.cpp
1
/*
2
* (C) 2016 Daniel Neus
3
* (C) 2016 Philipp Weber
4
* (C) 2019 Michael Boric
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include "tests.h"
10

11
#if defined(BOTAN_HAS_PKCS11)
12
   #include "test_pkcs11.h"
13
   #include <botan/p11.h>
14
   #include <botan/internal/dyn_load.h>
15
   #include <array>
16
   #include <functional>
17
   #include <map>
18
   #include <memory>
19
   #include <string>
20
   #include <vector>
21
#endif
22

23
namespace Botan_Tests {
24

25
namespace {
26

27
#if defined(BOTAN_HAS_PKCS11)
28
   #if defined(BOTAN_HAS_DYNAMIC_LOADER)
29

30
using namespace Botan;
31
using namespace PKCS11;
32

33
class RAII_LowLevel {
34
   public:
35
      RAII_LowLevel() :
20✔
36
            m_module(Test::pkcs11_lib()),
20✔
37
            m_func_list(nullptr),
20✔
38
            m_session_handle(0),
20✔
39
            m_is_session_open(false),
20✔
40
            m_is_logged_in(false) {
20✔
41
         LowLevel::C_GetFunctionList(m_module, &m_func_list);
20✔
42
         m_low_level = std::make_unique<LowLevel>(m_func_list);
20✔
43

44
         C_InitializeArgs init_args = {
20✔
45
            nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr};
46

47
         m_low_level->C_Initialize(&init_args);
20✔
48
      }
20✔
49

50
      ~RAII_LowLevel() noexcept {
20✔
51
         try {
20✔
52
            if(m_is_session_open) {
20✔
53
               if(m_is_logged_in) {
10✔
54
                  m_low_level->C_Logout(m_session_handle, nullptr);
7✔
55
               }
56

57
               m_low_level->C_CloseSession(m_session_handle, nullptr);
10✔
58
            }
59
            m_low_level->C_Finalize(nullptr, nullptr);
20✔
60
         } catch(...) {
×
61
            // ignore errors here
62
         }
×
63
      }
40✔
64

65
      RAII_LowLevel(const RAII_LowLevel& other) = delete;
66
      RAII_LowLevel(RAII_LowLevel&& other) = delete;
67
      RAII_LowLevel& operator=(const RAII_LowLevel& other) = delete;
68
      RAII_LowLevel& operator=(RAII_LowLevel&& other) = delete;
69

70
      std::vector<SlotId> get_slots(bool token_present) const {
19✔
71
         std::vector<SlotId> slots;
19✔
72
         m_low_level->C_GetSlotList(token_present, slots);
19✔
73

74
         if(slots.empty()) {
19✔
75
            throw Test_Error("No slot with attached token found");
×
76
         }
77

78
         return slots;
19✔
79
      }
×
80

81
      SessionHandle open_session(Flags session_flags) {
11✔
82
         std::vector<SlotId> slots = get_slots(true);
11✔
83
         m_low_level->C_OpenSession(slots.at(0), session_flags, nullptr, nullptr, &m_session_handle);
11✔
84
         m_is_session_open = true;
11✔
85
         return m_session_handle;
11✔
86
      }
11✔
87

88
      SessionHandle open_rw_session_with_user_login() {
3✔
89
         const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
3✔
90
         const SessionHandle handle = open_session(session_flags);
3✔
91
         login(UserType::User, PIN());
3✔
92
         return handle;
3✔
93
      }
94

95
      SessionHandle get_session_handle() const {
4✔
96
         if(!m_is_session_open) {
4✔
97
            throw Test_Error("no open session");
×
98
         }
99
         return m_session_handle;
4✔
100
      }
101

102
      void close_session() {
1✔
103
         if(!m_is_session_open) {
1✔
104
            throw Test_Error("no open session");
×
105
         }
106

107
         m_low_level->C_CloseSession(m_session_handle);
1✔
108
         m_is_session_open = false;
1✔
109
      }
1✔
110

111
      void login(UserType user_type, const secure_vector<uint8_t>& pin) {
8✔
112
         if(!m_is_session_open) {
8✔
113
            throw Test_Error("no open session");
×
114
         }
115

116
         if(m_is_logged_in) {
8✔
117
            throw Test_Error("Already logged in");
×
118
         }
119

120
         m_low_level->C_Login(m_session_handle, user_type, pin);
8✔
121
         m_is_logged_in = true;
8✔
122
      }
8✔
123

124
      void logout() {
1✔
125
         if(!m_is_logged_in) {
1✔
126
            throw Test_Error("Not logged in");
×
127
         }
128

129
         m_low_level->C_Logout(m_session_handle);
1✔
130
         m_is_logged_in = false;
1✔
131
      }
1✔
132

133
      LowLevel* get() const { return m_low_level.get(); }
1✔
134

135
   private:
136
      Dynamically_Loaded_Library m_module;
137
      FunctionList* m_func_list;
138
      std::unique_ptr<LowLevel> m_low_level;
139
      SessionHandle m_session_handle;
140
      bool m_is_session_open;
141
      bool m_is_logged_in;
142
};
143

144
bool no_op(ReturnValue* /*unused*/) {
×
145
   return true;
×
146
}
147

148
using PKCS11_BoundTestFunction = std::function<bool(ReturnValue* return_value)>;
149

150
// tests all 3 variants
151
Test::Result test_function(const std::string& name,
26✔
152
                           const PKCS11_BoundTestFunction& test_func,
153
                           const std::string& revert_fn_name,
154
                           const PKCS11_BoundTestFunction& revert_func,
155
                           bool expect_failure,
156
                           ReturnValue expected_return_value) {
157
   const std::string test_name =
26✔
158
      revert_fn_name.empty() ? "PKCS 11 low level - " + name : "PKCS 11 low level - " + name + "/" + revert_fn_name;
56✔
159
   Test::Result result(test_name);
52✔
160

161
   // test throw variant
162
   if(expect_failure) {
26✔
163
      result.test_throws(name + " fails as expected", [test_func]() { test_func(ThrowException); });
11✔
164
   } else {
165
      test_func(ThrowException);
25✔
166
      result.test_success(name + " did not throw and completed successfully");
25✔
167

168
      if(!revert_fn_name.empty()) {
25✔
169
         revert_func(ThrowException);
10✔
170
         result.test_success(revert_fn_name + " did not throw and completed successfully");
20✔
171
      }
172
   }
173

174
   // test bool return variant
175
   bool success = test_func(nullptr);
26✔
176
   result.test_eq(name, success, !expect_failure);
26✔
177
   if(success && !revert_fn_name.empty()) {
26✔
178
      success = revert_func(nullptr);
10✔
179
      result.test_eq(revert_fn_name, success, !expect_failure);
10✔
180
   }
181

182
   // test ReturnValue variant
183
   ReturnValue rv = ReturnValue::OK;
26✔
184
   success = test_func(&rv);
26✔
185
   result.test_eq(name, success, !expect_failure);
26✔
186
   if(!expect_failure) {
26✔
187
      result.test_is_eq<uint32_t>(name, static_cast<uint32_t>(rv), 0);
25✔
188
   } else {
189
      result.test_is_eq<uint32_t>(name, static_cast<uint32_t>(rv), static_cast<uint32_t>(expected_return_value));
1✔
190
   }
191

192
   if(success && !revert_fn_name.empty()) {
26✔
193
      success = revert_func(&rv);
10✔
194
      result.test_eq(revert_fn_name, success, !expect_failure);
10✔
195
      result.test_is_eq<uint32_t>(revert_fn_name, static_cast<uint32_t>(rv), 0);
10✔
196
   }
197

198
   return result;
26✔
199
}
26✔
200

201
Test::Result test_function(const std::string& name,
1✔
202
                           const PKCS11_BoundTestFunction& test_func,
203
                           bool expect_failure,
204
                           ReturnValue expected_return_value) {
205
   return test_function(name, test_func, std::string(), no_op, expect_failure, expected_return_value);
3✔
206
}
207

208
Test::Result test_function(const std::string& name, const PKCS11_BoundTestFunction& test_func) {
15✔
209
   return test_function(name, test_func, std::string(), no_op, false, ReturnValue::OK);
45✔
210
}
211

212
Test::Result test_function(const std::string& name,
10✔
213
                           const PKCS11_BoundTestFunction& test_func,
214
                           const std::string& revert_fn_name,
215
                           const PKCS11_BoundTestFunction& revert_func) {
216
   return test_function(name, test_func, revert_fn_name, revert_func, false, ReturnValue::OK);
10✔
217
}
218

219
Test::Result test_low_level_ctor() {
1✔
220
   Test::Result result("PKCS 11 low level - LowLevel ctor");
1✔
221

222
   const Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
223
   FunctionList* func_list(nullptr);
1✔
224
   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
1✔
225

226
   const LowLevel p11_low_level(func_list);
1✔
227
   result.test_success("LowLevel ctor does complete for valid function list");
1✔
228

229
   result.test_throws("LowLevel ctor fails for invalid function list pointer", []() { LowLevel(nullptr); });
3✔
230

231
   return result;
1✔
232
}
1✔
233

234
// NOLINTBEGIN(*-avoid-bind)
235

236
Test::Result test_c_get_function_list() {
1✔
237
   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
238
   // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy
239
   FunctionList* func_list = nullptr;
1✔
240
   return test_function(
1✔
241
      "C_GetFunctionList",
242
      std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list, std::placeholders::_1));
3✔
243
}
1✔
244

245
Test::Result test_initialize_finalize() {
1✔
246
   const Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
247
   FunctionList* func_list = nullptr;
1✔
248
   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
1✔
249

250
   LowLevel p11_low_level(func_list);
1✔
251

252
   // setting Flag::OsLockingOk should be the normal use case
253
   C_InitializeArgs init_args = {nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr};
1✔
254

255
   auto init_bind = std::bind(&LowLevel::C_Initialize, std::ref(p11_low_level), &init_args, std::placeholders::_1);
1✔
256
   auto finalize_bind = std::bind(&LowLevel::C_Finalize, std::ref(p11_low_level), nullptr, std::placeholders::_1);
1✔
257
   return test_function("C_Initialize", init_bind, "C_Finalize", finalize_bind);
4✔
258
}
1✔
259

260
Test::Result test_c_get_info() {
1✔
261
   const RAII_LowLevel p11_low_level;
1✔
262

263
   Info info = {};
1✔
264
   Test::Result result =
1✔
265
      test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, p11_low_level.get(), &info, std::placeholders::_1));
2✔
266
   result.test_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0);
1✔
267

268
   return result;
1✔
269
}
1✔
270

271
Test::Result test_c_get_slot_list() {
1✔
272
   const RAII_LowLevel p11_low_level;
1✔
273

274
   std::vector<SlotId> slot_vec;
1✔
275

276
   // assumes smartcard reader is attached without card
277

278
   auto slots_no_card = std::bind(
1✔
279
      static_cast<bool (LowLevel::*)(bool, std::vector<SlotId>&, ReturnValue*) const>(&LowLevel::C_GetSlotList),
1✔
280
      p11_low_level.get(),
1✔
281
      false,  // no card present
1✔
282
      std::ref(slot_vec),
1✔
283
      std::placeholders::_1);
1✔
284

285
   Test::Result result = test_function("C_GetSlotList", slots_no_card);
2✔
286
   result.test_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0);
1✔
287

288
   // assumes smartcard reader is attached with a card
289

290
   auto slots_with_card = std::bind(
1✔
291
      static_cast<bool (LowLevel::*)(bool, std::vector<SlotId>&, ReturnValue*) const>(&LowLevel::C_GetSlotList),
1✔
292
      p11_low_level.get(),
1✔
293
      true,  // card present
1✔
294
      std::ref(slot_vec),
1✔
295
      std::placeholders::_1);
1✔
296

297
   slot_vec.clear();
1✔
298
   result.merge(test_function("C_GetSlotList", slots_with_card));
1✔
299
   result.test_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0);
1✔
300

301
   return result;
1✔
302
}
1✔
303

304
Test::Result test_c_get_slot_info() {
1✔
305
   const RAII_LowLevel p11_low_level;
1✔
306
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(false);
1✔
307

308
   SlotInfo slot_info = {};
1✔
309
   Test::Result result = test_function(
1✔
310
      "C_GetSlotInfo",
311
      std::bind(&LowLevel::C_GetSlotInfo, p11_low_level.get(), slot_vec.at(0), &slot_info, std::placeholders::_1));
2✔
312

313
   const std::string slot_desc(reinterpret_cast<char*>(slot_info.slotDescription));
1✔
314
   result.test_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0);
1✔
315

316
   return result;
2✔
317
}
2✔
318

319
Test::Result test_c_get_token_info() {
1✔
320
   const RAII_LowLevel p11_low_level;
1✔
321
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
322

323
   TokenInfo token_info = {};
1✔
324
   Test::Result result = test_function(
1✔
325
      "C_GetTokenInfo",
326
      std::bind(&LowLevel::C_GetTokenInfo, p11_low_level.get(), slot_vec.at(0), &token_info, std::placeholders::_1));
3✔
327

328
   const std::string serial(reinterpret_cast<char*>(token_info.serialNumber));
1✔
329
   result.test_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0);
1✔
330

331
   return result;
2✔
332
}
1✔
333

334
Test::Result test_c_wait_for_slot_event() {
1✔
335
   const RAII_LowLevel p11_low_level;
1✔
336

337
   const Flags flags = PKCS11::flags(Flag::DontBlock);
1✔
338
   SlotId slot_id = 0;
1✔
339

340
   return test_function(
1✔
341
      "C_WaitForSlotEvent",
342
      std::bind(&LowLevel::C_WaitForSlotEvent, p11_low_level.get(), flags, &slot_id, nullptr, std::placeholders::_1),
1✔
343
      true,
344
      ReturnValue::NoEvent);
4✔
345
}
1✔
346

347
Test::Result test_c_get_mechanism_list() {
1✔
348
   const RAII_LowLevel p11_low_level;
1✔
349
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
350

351
   std::vector<MechanismType> mechanisms;
1✔
352

353
   auto binder = std::bind(static_cast<bool (LowLevel::*)(SlotId, std::vector<MechanismType>&, ReturnValue*) const>(
1✔
354
                              &LowLevel::C_GetMechanismList),
355
                           p11_low_level.get(),
1✔
356
                           slot_vec.at(0),
1✔
357
                           std::ref(mechanisms),
1✔
358
                           std::placeholders::_1);
1✔
359

360
   Test::Result result = test_function("C_GetMechanismList", binder);
2✔
361
   result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty());
2✔
362

363
   return result;
1✔
364
}
1✔
365

366
Test::Result test_c_get_mechanism_info() {
1✔
367
   const RAII_LowLevel p11_low_level;
1✔
368
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
369

370
   std::vector<MechanismType> mechanisms;
1✔
371
   p11_low_level.get()->C_GetMechanismList(slot_vec.at(0), mechanisms);
1✔
372

373
   MechanismInfo mechanism_info = {};
1✔
374
   return test_function("C_GetMechanismInfo",
1✔
375
                        std::bind(&LowLevel::C_GetMechanismInfo,
2✔
376
                                  p11_low_level.get(),
1✔
377
                                  slot_vec.at(0),
1✔
378
                                  mechanisms.at(0),
1✔
379
                                  &mechanism_info,
1✔
380
                                  std::placeholders::_1));
4✔
381
}
1✔
382

383
Test::Result test_c_init_token() {
1✔
384
   const RAII_LowLevel p11_low_level;
1✔
385
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
386

387
   const std::string token_label = "Botan PKCS#11 tests";
1✔
388
   std::string_view label_view(token_label);
1✔
389

390
   auto sec_vec_binder = std::bind(
1✔
391
      static_cast<bool (LowLevel::*)(SlotId, const secure_vector<uint8_t>&, std::string_view, ReturnValue*) const>(
1✔
392
         &LowLevel::C_InitToken<secure_allocator<uint8_t>>),
393
      p11_low_level.get(),
1✔
394
      slot_vec.at(0),
1✔
395
      SO_PIN(),
×
396
      std::ref(label_view),
1✔
397
      std::placeholders::_1);
1✔
398

399
   return test_function("C_InitToken", sec_vec_binder);
4✔
400
}
1✔
401

402
Test::Result test_open_close_session() {
1✔
403
   const RAII_LowLevel p11_low_level;
1✔
404
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
405

406
   // public read only session
407
   const Flags ro_flags = PKCS11::flags(Flag::SerialSession);
1✔
408
   SessionHandle session_handle = 0;
1✔
409

410
   auto open_session_ro = std::bind(&LowLevel::C_OpenSession,
2✔
411
                                    p11_low_level.get(),
1✔
412
                                    slot_vec.at(0),
1✔
413
                                    ro_flags,
414
                                    nullptr,
1✔
415
                                    nullptr,
416
                                    &session_handle,
417
                                    std::placeholders::_1);
1✔
418

419
   auto close_session =
1✔
420
      std::bind(&LowLevel::C_CloseSession, p11_low_level.get(), std::ref(session_handle), std::placeholders::_1);
1✔
421

422
   Test::Result result = test_function("C_OpenSession", open_session_ro, "C_CloseSession", close_session);
3✔
423

424
   // public read write session
425
   const Flags rw_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
426

427
   auto open_session_rw = std::bind(&LowLevel::C_OpenSession,
2✔
428
                                    p11_low_level.get(),
1✔
429
                                    slot_vec.at(0),
1✔
430
                                    rw_flags,
431
                                    nullptr,
1✔
432
                                    nullptr,
433
                                    &session_handle,
434
                                    std::placeholders::_1);
1✔
435

436
   result.merge(test_function("C_OpenSession", open_session_rw, "C_CloseSession", close_session));
3✔
437

438
   return result;
1✔
439
}
1✔
440

441
Test::Result test_c_close_all_sessions() {
1✔
442
   RAII_LowLevel p11_low_level;
1✔
443
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
444

445
   auto open_two_sessions = [&slot_vec, &p11_low_level]() -> void {
4✔
446
      // public read only session
447
      Flags flags = PKCS11::flags(Flag::SerialSession);
3✔
448
      SessionHandle first_session_handle = 0;
3✔
449
      SessionHandle second_session_handle = 0;
3✔
450

451
      p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &first_session_handle);
3✔
452

453
      flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
3✔
454
      p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &second_session_handle);
3✔
455
   };
4✔
456

457
   open_two_sessions();
1✔
458

459
   Test::Result result("PKCS 11 low level - C_CloseAllSessions");
1✔
460

461
   // test throw variant
462
   p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0));
1✔
463
   result.test_success("C_CloseAllSessions does not throw");
1✔
464

465
   // test bool return variant
466
   open_two_sessions();
1✔
467

468
   bool success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), nullptr);
1✔
469
   result.test_eq("C_CloseAllSessions", success, true);
1✔
470

471
   // test ReturnValue variant
472
   open_two_sessions();
1✔
473

474
   ReturnValue rv = ReturnValue::OK;
1✔
475
   success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), &rv);
1✔
476
   result.test_eq("C_CloseAllSessions", success, true);
1✔
477
   result.test_is_eq<uint32_t>("C_CloseAllSessions", static_cast<uint32_t>(rv), 0);
1✔
478

479
   return result;
1✔
480
}
1✔
481

482
Test::Result test_c_get_session_info() {
1✔
483
   RAII_LowLevel p11_low_level;
1✔
484
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
485

486
   // public read only session
487
   const Flags flags = PKCS11::flags(Flag::SerialSession);
1✔
488
   const SessionHandle session_handle = p11_low_level.open_session(flags);
1✔
489

490
   SessionInfo session_info = {};
1✔
491
   Test::Result result = test_function(
1✔
492
      "C_GetSessionInfo",
493
      std::bind(
1✔
494
         &LowLevel::C_GetSessionInfo, p11_low_level.get(), session_handle, &session_info, std::placeholders::_1));
3✔
495

496
   result.confirm("C_GetSessionInfo returns same slot id as during call to C_OpenSession",
1✔
497
                  session_info.slotID == slot_vec.at(0));
1✔
498
   result.confirm("C_GetSessionInfo returns same flags as during call to C_OpenSession", session_info.flags == flags);
2✔
499
   result.confirm("C_GetSessionInfo returns public read only session state",
1✔
500
                  session_info.state == static_cast<CK_FLAGS>(SessionState::RoPublicSession));
1✔
501

502
   return result;
1✔
503
}
1✔
504

505
Test::Result login_logout_helper(const RAII_LowLevel& p11_low_level,
3✔
506
                                 SessionHandle handle,
507
                                 UserType user_type,
508
                                 std::string_view pin) {
509
   secure_vector<uint8_t> pin_as_sec_vec(pin.begin(), pin.end());
3✔
510

511
   auto login_secvec_binder = std::bind(
3✔
512
      static_cast<bool (LowLevel::*)(SessionHandle, UserType, const secure_vector<uint8_t>&, ReturnValue*) const>(
3✔
513
         &LowLevel::C_Login<secure_allocator<uint8_t>>),
514
      p11_low_level.get(),
3✔
515
      handle,
516
      user_type,
517
      std::ref(pin_as_sec_vec),
3✔
518
      std::placeholders::_1);
3✔
519

520
   auto logout_binder =
3✔
521
      std::bind(static_cast<bool (LowLevel::*)(SessionHandle, ReturnValue*) const>(&LowLevel::C_Logout),
3✔
522
                p11_low_level.get(),
3✔
523
                handle,
524
                std::placeholders::_1);
3✔
525

526
   return test_function("C_Login", login_secvec_binder, "C_Logout", logout_binder);
12✔
527
}
3✔
528

529
Test::Result test_c_login_logout_security_officier() {
1✔
530
   RAII_LowLevel p11_low_level;
1✔
531

532
   // can only login to R/W session
533
   const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
534
   const SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
535

536
   return login_logout_helper(p11_low_level, session_handle, UserType::SO, PKCS11_SO_PIN);
2✔
537
}
1✔
538

539
Test::Result test_c_login_logout_user() {
1✔
540
   RAII_LowLevel p11_low_level;
1✔
541

542
   // R/O session
543
   Flags session_flags = PKCS11::flags(Flag::SerialSession);
1✔
544
   SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
545
   Test::Result result = login_logout_helper(p11_low_level, session_handle, UserType::User, PKCS11_USER_PIN);
1✔
546
   p11_low_level.close_session();
1✔
547

548
   // R/W session
549
   session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
550
   session_handle = p11_low_level.open_session(session_flags);
1✔
551

552
   result.merge(login_logout_helper(p11_low_level, session_handle, UserType::User, PKCS11_USER_PIN));
1✔
553

554
   return result;
1✔
555
}
1✔
556

557
Test::Result test_c_init_pin() {
1✔
558
   RAII_LowLevel p11_low_level;
1✔
559

560
   // C_InitPIN can only be called in the "R/W SO Functions" state
561
   const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
562
   const SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
563

564
   p11_low_level.login(UserType::SO, SO_PIN());
1✔
565

566
   auto sec_vec_binder =
1✔
567
      std::bind(static_cast<bool (LowLevel::*)(SessionHandle, const secure_vector<uint8_t>&, ReturnValue*) const>(
1✔
568
                   &LowLevel::C_InitPIN<secure_allocator<uint8_t>>),
569
                p11_low_level.get(),
1✔
570
                session_handle,
571
                PIN(),
1✔
572
                std::placeholders::_1);
1✔
573

574
   return test_function("C_InitPIN", sec_vec_binder);
4✔
575
}
1✔
576

577
Test::Result test_c_set_pin() {
1✔
578
   RAII_LowLevel p11_low_level;
1✔
579

580
   // C_SetPIN can only be called in the "R / W Public Session" state, "R / W SO Functions" state, or "R / W User Functions" state
581
   const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
582
   SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
583

584
   // now we are in "R / W Public Session" state: this will change the pin of the user
585

586
   auto get_pin_bind = [&session_handle, &p11_low_level](
5✔
587
                          const secure_vector<uint8_t>& old_pin,
588
                          const secure_vector<uint8_t>& new_pin) -> PKCS11_BoundTestFunction {
589
      return std::bind(
12✔
590
         static_cast<bool (LowLevel::*)(
4✔
591
            SessionHandle, const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, ReturnValue*) const>(
592
            &LowLevel::C_SetPIN<secure_allocator<uint8_t>>),
593
         p11_low_level.get(),
4✔
594
         session_handle,
595
         old_pin,
596
         new_pin,
597
         std::placeholders::_1);
8✔
598
   };
1✔
599

600
   const std::string test_pin("654321");
1✔
601
   const auto test_pin_secvec = secure_vector<uint8_t>(test_pin.begin(), test_pin.end());
1✔
602

603
   const PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN(), test_pin_secvec);
1✔
604
   const PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN());
1✔
605

606
   Test::Result result = test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind);
2✔
607

608
   // change pin in "R / W User Functions" state
609
   p11_low_level.login(UserType::User, PIN());
1✔
610

611
   result.merge(test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind));
2✔
612
   p11_low_level.logout();
1✔
613

614
   // change so_pin in "R / W SO Functions" state
615
   const std::string test_so_pin = "87654321";
1✔
616
   const secure_vector<uint8_t> test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end());
1✔
617
   p11_low_level.login(UserType::SO, SO_PIN());
1✔
618

619
   const PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN(), test_so_pin_secvec);
1✔
620
   const PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN());
1✔
621

622
   result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind));
2✔
623

624
   return result;
2✔
625
}
6✔
626

627
// Simple data object
628
const ObjectClass object_class = ObjectClass::Data;
629
const std::string_view label = "A data object";
630
const std::string_view data = "Sample data";
631
const Bbool btrue = True;
632

633
const std::array<Attribute, 4> data_template = {
634
   {{static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Class),
635
     const_cast<ObjectClass*>(&object_class),
636
     sizeof(object_class)},
637
    {static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Token), const_cast<Bbool*>(&btrue), sizeof(btrue)},
638
    {static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Label),
639
     const_cast<char*>(label.data()),
640
     static_cast<CK_ULONG>(label.size())},
641
    {static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Value),
642
     const_cast<char*>(data.data()),
643
     static_cast<CK_ULONG>(data.size())}}};
644

645
ObjectHandle create_simple_data_object(const RAII_LowLevel& p11_low_level) {
4✔
646
   ObjectHandle object_handle = {};
4✔
647

648
   auto dtemplate = data_template;
4✔
649
   p11_low_level.get()->C_CreateObject(
4✔
650
      p11_low_level.get_session_handle(), dtemplate.data(), static_cast<Ulong>(dtemplate.size()), &object_handle);
651
   return object_handle;
4✔
652
}
653

654
Test::Result test_c_create_object_c_destroy_object() {
1✔
655
   RAII_LowLevel p11_low_level;
1✔
656
   const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
657

658
   ObjectHandle object_handle(0);
1✔
659

660
   auto dtemplate = data_template;
1✔
661

662
   auto create_bind = std::bind(&LowLevel::C_CreateObject,
1✔
663
                                p11_low_level.get(),
1✔
664
                                session_handle,
665
                                dtemplate.data(),
1✔
666
                                static_cast<Ulong>(dtemplate.size()),
1✔
667
                                &object_handle,
668
                                std::placeholders::_1);
1✔
669

670
   auto destroy_bind = std::bind(
1✔
671
      &LowLevel::C_DestroyObject, p11_low_level.get(), session_handle, std::ref(object_handle), std::placeholders::_1);
1✔
672

673
   return test_function("C_CreateObject", create_bind, "C_DestroyObject", destroy_bind);
4✔
674
}
1✔
675

676
Test::Result test_c_get_object_size() {
1✔
677
   RAII_LowLevel p11_low_level;
1✔
678

679
   const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
680
   const SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
681

682
   p11_low_level.login(UserType::User, PIN());
1✔
683

684
   const ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
685
   Ulong object_size = 0;
1✔
686

687
   auto bind = std::bind(&LowLevel::C_GetObjectSize,
1✔
688
                         p11_low_level.get(),
1✔
689
                         session_handle,
690
                         object_handle,
691
                         &object_size,
1✔
692
                         std::placeholders::_1);
1✔
693

694
   Test::Result result = test_function("C_GetObjectSize", bind);
2✔
695
   result.test_ne("Object size", object_size, 0);
1✔
696

697
   // cleanup
698
   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
1✔
699

700
   return result;
1✔
701
}
1✔
702

703
Test::Result test_c_get_attribute_value() {
1✔
704
   RAII_LowLevel p11_low_level;
1✔
705
   const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
706

707
   const ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
708

709
   std::map<AttributeType, secure_vector<uint8_t>> getter = {{AttributeType::Label, secure_vector<uint8_t>()},
1✔
710
                                                             {AttributeType::Value, secure_vector<uint8_t>()}};
3✔
711

712
   auto bind =
1✔
713
      std::bind(static_cast<bool (LowLevel::*)(
1✔
714
                   SessionHandle, ObjectHandle, std::map<AttributeType, secure_vector<uint8_t>>&, ReturnValue*) const>(
715
                   &LowLevel::C_GetAttributeValue<secure_allocator<uint8_t>>),
716
                p11_low_level.get(),
1✔
717
                session_handle,
718
                object_handle,
719
                std::ref(getter),
1✔
720
                std::placeholders::_1);
1✔
721

722
   Test::Result result = test_function("C_GetAttributeValue", bind);
2✔
723

724
   const std::string _label(getter[AttributeType::Label].begin(), getter[AttributeType::Label].end());
2✔
725
   const std::string value(getter[AttributeType::Value].begin(), getter[AttributeType::Value].end());
2✔
726
   result.test_eq("label", _label, "A data object");
2✔
727
   result.test_eq("value", value, "Sample data");
2✔
728

729
   // cleanup
730
   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
1✔
731

732
   return result;
2✔
733
}
1✔
734

735
std::map<AttributeType, std::vector<uint8_t>> get_attribute_values(const RAII_LowLevel& p11_low_level,
2✔
736
                                                                   SessionHandle session_handle,
737
                                                                   ObjectHandle object_handle,
738
                                                                   const std::vector<AttributeType>& attribute_types) {
739
   std::map<AttributeType, std::vector<uint8_t>> received_attributes;
2✔
740

741
   for(const auto& type : attribute_types) {
6✔
742
      received_attributes.emplace(type, std::vector<uint8_t>());
4✔
743
   }
744

745
   p11_low_level.get()->C_GetAttributeValue(session_handle, object_handle, received_attributes);
2✔
746

747
   return received_attributes;
2✔
748
}
×
749

750
Test::Result test_c_set_attribute_value() {
1✔
751
   RAII_LowLevel p11_low_level;
1✔
752

753
   const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
754
   const SessionHandle session_handle = p11_low_level.open_session(session_flags);
1✔
755

756
   p11_low_level.login(UserType::User, PIN());
1✔
757

758
   const ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
759

760
   std::string new_label = "A modified data object";
1✔
761

762
   std::map<AttributeType, secure_vector<uint8_t>> new_attributes = {
1✔
763
      {AttributeType::Label, secure_vector<uint8_t>(new_label.begin(), new_label.end())}};
3✔
764

765
   auto bind =
1✔
766
      std::bind(static_cast<bool (LowLevel::*)(
1✔
767
                   SessionHandle, ObjectHandle, std::map<AttributeType, secure_vector<uint8_t>>&, ReturnValue*) const>(
768
                   &LowLevel::C_SetAttributeValue<secure_allocator<uint8_t>>),
769
                p11_low_level.get(),
1✔
770
                session_handle,
771
                object_handle,
772
                std::ref(new_attributes),
1✔
773
                std::placeholders::_1);
1✔
774

775
   Test::Result result = test_function("C_SetAttributeValue", bind);
2✔
776

777
   // get attributes and check if they are changed correctly
778
   const std::vector<AttributeType> types = {AttributeType::Label, AttributeType::Value};
1✔
779
   auto received_attributes = get_attribute_values(p11_low_level, session_handle, object_handle, types);
1✔
780

781
   const std::string retrieved_label(received_attributes[AttributeType::Label].begin(),
1✔
782
                                     received_attributes[AttributeType::Label].end());
2✔
783

784
   result.test_eq("label", new_label, retrieved_label);
1✔
785

786
   // cleanup
787
   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
1✔
788

789
   return result;
2✔
790
}
2✔
791

792
Test::Result test_c_copy_object() {
1✔
793
   RAII_LowLevel p11_low_level;
1✔
794
   const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
795

796
   const ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
797
   ObjectHandle copied_object_handle = 0;
1✔
798

799
   const std::string copied_label = "A copied data object";
1✔
800

801
   Attribute copy_attribute_values = {static_cast<CK_ATTRIBUTE_TYPE>(AttributeType::Label),
1✔
802
                                      const_cast<char*>(copied_label.c_str()),
1✔
803
                                      static_cast<CK_ULONG>(copied_label.size())};
1✔
804

805
   auto binder = std::bind(&LowLevel::C_CopyObject,
1✔
806
                           p11_low_level.get(),
1✔
807
                           session_handle,
808
                           object_handle,
809
                           &copy_attribute_values,
1✔
810
                           1,
811
                           &copied_object_handle,
812
                           std::placeholders::_1);
1✔
813

814
   Test::Result result = test_function("C_CopyObject", binder);
2✔
815

816
   // get attributes and check if its copied correctly
817
   const std::vector<AttributeType> types = {AttributeType::Label, AttributeType::Value};
1✔
818
   auto received_attributes = get_attribute_values(p11_low_level, session_handle, copied_object_handle, types);
1✔
819

820
   const std::string retrieved_label(received_attributes[AttributeType::Label].begin(),
1✔
821
                                     received_attributes[AttributeType::Label].end());
2✔
822

823
   result.test_eq("label", copied_label, retrieved_label);
1✔
824

825
   // cleanup
826
   p11_low_level.get()->C_DestroyObject(session_handle, object_handle);
1✔
827
   p11_low_level.get()->C_DestroyObject(session_handle, copied_object_handle);
1✔
828

829
   return result;
2✔
830
}
1✔
831

832
// NOLINTEND(*-avoid-bind)
833

834
Test::Result test_load_latest_interface() {
1✔
835
   Test::Result res("Load latest PKCS #11 interface");
1✔
836
   Botan::Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
837
   res.test_no_throw("Get function lists of latest interface", [&] {
2✔
838
      auto latest_interface = InterfaceWrapper::latest_p11_interface(pkcs11_module);
1✔
839
      latest_interface.func_2_40();
1✔
840
      if(latest_interface.version().major >= 3) {
1✔
841
         latest_interface.func_3_0();
×
842

843
         if(latest_interface.version().major > 3 || latest_interface.version().minor >= 2) {
×
844
            latest_interface.func_3_2();
×
845
         }
846
      }
847
   });
1✔
848
   return res;
1✔
849
}
1✔
850

851
class LowLevelTests final : public Test {
1✔
852
   public:
853
      std::vector<Test::Result> run() override {
1✔
854
         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
1✔
855
            {STRING_AND_FUNCTION(test_c_get_function_list)},
856
            {STRING_AND_FUNCTION(test_low_level_ctor)},
857
            {STRING_AND_FUNCTION(test_initialize_finalize)},
858
            {STRING_AND_FUNCTION(test_c_get_info)},
859
            {STRING_AND_FUNCTION(test_c_get_slot_list)},
860
            {STRING_AND_FUNCTION(test_c_get_slot_info)},
861
            {STRING_AND_FUNCTION(test_c_get_token_info)},
862
            {STRING_AND_FUNCTION(test_c_wait_for_slot_event)},
863
            {STRING_AND_FUNCTION(test_c_get_mechanism_list)},
864
            {STRING_AND_FUNCTION(test_c_get_mechanism_info)},
865
            {STRING_AND_FUNCTION(test_open_close_session)},
866
            {STRING_AND_FUNCTION(test_c_close_all_sessions)},
867
            {STRING_AND_FUNCTION(test_c_get_session_info)},
868
            {STRING_AND_FUNCTION(test_c_init_token)},
869
            {STRING_AND_FUNCTION(test_c_login_logout_security_officier)}, /* only possible if token is initialized */
870
            {STRING_AND_FUNCTION(test_c_init_pin)},
871
            {STRING_AND_FUNCTION(
872
               test_c_login_logout_user)}, /* only possible if token is initialized and user pin is set */
873
            {STRING_AND_FUNCTION(test_c_set_pin)},
874
            {STRING_AND_FUNCTION(test_c_create_object_c_destroy_object)},
875
            {STRING_AND_FUNCTION(test_c_get_object_size)},
876
            {STRING_AND_FUNCTION(test_c_get_attribute_value)},
877
            {STRING_AND_FUNCTION(test_c_set_attribute_value)},
878
            {STRING_AND_FUNCTION(test_c_copy_object)},
879
            {STRING_AND_FUNCTION(test_load_latest_interface)},
880
         };
25✔
881

882
         return run_pkcs11_tests("PKCS11 low level", fns);
2✔
883
      }
2✔
884
};
885

886
BOTAN_REGISTER_SERIALIZED_TEST("pkcs11", "pkcs11-lowlevel", LowLevelTests);
887

888
   #endif
889
#endif
890

891
}  // namespace
892
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc