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

randombit / botan / 16298250663

15 Jul 2025 12:20PM UTC coverage: 90.623% (-0.004%) from 90.627%
16298250663

push

github

web-flow
Merge pull request #4992 from randombit/jack/clang-tidy-readability-redundant-member-init

Enable and fix clang-tidy warning readability-redundant-member-init

99624 of 109932 relevant lines covered (90.62%)

12389847.2 hits per line

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

97.02
/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 "test_pkcs11.h"
10
#include "tests.h"
11

12
#include <array>
13
#include <functional>
14
#include <map>
15
#include <memory>
16
#include <string>
17
#include <vector>
18

19
#if defined(BOTAN_HAS_PKCS11)
20
   #include <botan/p11.h>
21
   #include <botan/internal/dyn_load.h>
22
#endif
23

24
namespace Botan_Tests {
25

26
namespace {
27

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

31
using namespace Botan;
32
using namespace PKCS11;
33

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

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

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

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

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

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

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

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

79
         return slots;
19✔
80
      }
×
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

183
   // test ReturnValue variant
184
   ReturnValue rv = ReturnValue::OK;
26✔
185
   success = test_func(&rv);
26✔
186
   result.test_eq(name, success, !expect_failure);
26✔
187
   if(!expect_failure) {
26✔
188
      result.test_rc_ok(name, static_cast<uint32_t>(rv));
25✔
189
   } else {
190
      result.test_rc_fail(name,
2✔
191
                          "return value should be: " + std::to_string(static_cast<uint32_t>(expected_return_value)),
3✔
192
                          static_cast<uint32_t>(rv));
193
   }
194

195
   if(success && !revert_fn_name.empty()) {
26✔
196
      success = revert_func(&rv);
10✔
197
      result.test_eq(revert_fn_name, success, !expect_failure);
10✔
198
      result.test_rc_ok(revert_fn_name, static_cast<uint32_t>(rv));
10✔
199
   }
200

201
   return result;
26✔
202
}
26✔
203

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

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

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

222
Test::Result test_low_level_ctor() {
1✔
223
   Test::Result result("PKCS 11 low level - LowLevel ctor");
1✔
224

225
   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
226
   FunctionListPtr func_list(nullptr);
1✔
227
   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
1✔
228

229
   LowLevel p11_low_level(func_list);
1✔
230
   result.test_success("LowLevel ctor does complete for valid function list");
1✔
231

232
   result.test_throws("LowLevel ctor fails for invalid function list pointer",
2✔
233
                      []() { LowLevel p11_low_level2(nullptr); });
1✔
234

235
   return result;
1✔
236
}
1✔
237

238
// NOLINTBEGIN(*-avoid-bind)
239

240
Test::Result test_c_get_function_list() {
1✔
241
   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
242
   FunctionListPtr func_list = nullptr;
1✔
243
   return test_function(
1✔
244
      "C_GetFunctionList",
245
      std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list, std::placeholders::_1));
3✔
246
}
1✔
247

248
Test::Result test_initialize_finalize() {
1✔
249
   Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib());
1✔
250
   FunctionListPtr func_list = nullptr;
1✔
251
   LowLevel::C_GetFunctionList(pkcs11_module, &func_list);
1✔
252

253
   LowLevel p11_low_level(func_list);
1✔
254

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

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

263
Test::Result test_c_get_info() {
1✔
264
   RAII_LowLevel p11_low_level;
1✔
265

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

271
   return result;
1✔
272
}
1✔
273

274
Test::Result test_c_get_slot_list() {
1✔
275
   RAII_LowLevel p11_low_level;
1✔
276

277
   std::vector<SlotId> slot_vec;
1✔
278

279
   // assumes smartcard reader is attached without card
280

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

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

291
   // assumes smartcard reader is attached with a card
292

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

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

304
   return result;
1✔
305
}
1✔
306

307
Test::Result test_c_get_slot_info() {
1✔
308
   RAII_LowLevel p11_low_level;
1✔
309
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(false);
1✔
310

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

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

319
   return result;
2✔
320
}
2✔
321

322
Test::Result test_c_get_token_info() {
1✔
323
   RAII_LowLevel p11_low_level;
1✔
324
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
325

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

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

334
   return result;
2✔
335
}
1✔
336

337
Test::Result test_c_wait_for_slot_event() {
1✔
338
   RAII_LowLevel p11_low_level;
1✔
339

340
   Flags flags = PKCS11::flags(Flag::DontBlock);
1✔
341
   SlotId slot_id = 0;
1✔
342

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

350
Test::Result test_c_get_mechanism_list() {
1✔
351
   RAII_LowLevel p11_low_level;
1✔
352
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
353

354
   std::vector<MechanismType> mechanisms;
1✔
355

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

363
   Test::Result result = test_function("C_GetMechanismList", binder);
2✔
364
   result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty());
2✔
365

366
   return result;
1✔
367
}
1✔
368

369
Test::Result test_c_get_mechanism_info() {
1✔
370
   RAII_LowLevel p11_low_level;
1✔
371
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
372

373
   std::vector<MechanismType> mechanisms;
1✔
374
   p11_low_level.get()->C_GetMechanismList(slot_vec.at(0), mechanisms);
1✔
375

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

386
Test::Result test_c_init_token() {
1✔
387
   RAII_LowLevel p11_low_level;
1✔
388
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
389

390
   const std::string token_label = "Botan PKCS#11 tests";
1✔
391
   std::string_view label_view(token_label);
1✔
392

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

402
   return test_function("C_InitToken", sec_vec_binder);
4✔
403
}
1✔
404

405
Test::Result test_open_close_session() {
1✔
406
   RAII_LowLevel p11_low_level;
1✔
407
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
408

409
   // public read only session
410
   const Flags ro_flags = PKCS11::flags(Flag::SerialSession);
1✔
411
   SessionHandle session_handle = 0;
1✔
412

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

422
   auto close_session =
1✔
423
      std::bind(&LowLevel::C_CloseSession, *p11_low_level.get(), std::ref(session_handle), std::placeholders::_1);
1✔
424

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

427
   // public read write session
428
   const Flags rw_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession);
1✔
429

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

439
   result.merge(test_function("C_OpenSession", open_session_rw, "C_CloseSession", close_session));
3✔
440

441
   return result;
1✔
442
}
1✔
443

444
Test::Result test_c_close_all_sessions() {
1✔
445
   RAII_LowLevel p11_low_level;
1✔
446
   std::vector<SlotId> slot_vec = p11_low_level.get_slots(true);
1✔
447

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

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

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

459
   open_two_sessions();
1✔
460

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

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

467
   // test bool return variant
468
   open_two_sessions();
1✔
469

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

473
   // test ReturnValue variant
474
   open_two_sessions();
1✔
475

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

481
   return result;
1✔
482
}
1✔
483

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

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

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

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

504
   return result;
1✔
505
}
1✔
506

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

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

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

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

531
Test::Result test_c_login_logout_security_officier() {
1✔
532
   RAII_LowLevel p11_low_level;
1✔
533

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

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

541
Test::Result test_c_login_logout_user() {
1✔
542
   RAII_LowLevel p11_low_level;
1✔
543

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

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

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

556
   return result;
1✔
557
}
1✔
558

559
Test::Result test_c_init_pin() {
1✔
560
   RAII_LowLevel p11_low_level;
1✔
561

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

566
   p11_low_level.login(UserType::SO, SO_PIN());
1✔
567

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

576
   return test_function("C_InitPIN", sec_vec_binder);
4✔
577
}
1✔
578

579
Test::Result test_c_set_pin() {
1✔
580
   RAII_LowLevel p11_low_level;
1✔
581

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

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

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

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

605
   PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN(), test_pin_secvec);
1✔
606
   PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN());
1✔
607

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

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

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

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

621
   PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN(), test_so_pin_secvec);
1✔
622
   PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN());
1✔
623

624
   result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind));
2✔
625

626
   return result;
2✔
627
}
6✔
628

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

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

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

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

656
Test::Result test_c_create_object_c_destroy_object() {
1✔
657
   RAII_LowLevel p11_low_level;
1✔
658
   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
659

660
   ObjectHandle object_handle(0);
1✔
661

662
   auto dtemplate = data_template;
1✔
663

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

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

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

678
Test::Result test_c_get_object_size() {
1✔
679
   RAII_LowLevel p11_low_level;
1✔
680

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

684
   p11_low_level.login(UserType::User, PIN());
1✔
685

686
   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
687
   Ulong object_size = 0;
1✔
688

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

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

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

702
   return result;
1✔
703
}
1✔
704

705
Test::Result test_c_get_attribute_value() {
1✔
706
   RAII_LowLevel p11_low_level;
1✔
707
   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
708

709
   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
710

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

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

724
   Test::Result result = test_function("C_GetAttributeValue", bind);
2✔
725

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

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

734
   return result;
2✔
735
}
1✔
736

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

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

747
   p11_low_level.get()->C_GetAttributeValue(session_handle, object_handle, received_attributes);
2✔
748

749
   return received_attributes;
2✔
750
}
×
751

752
Test::Result test_c_set_attribute_value() {
1✔
753
   RAII_LowLevel p11_low_level;
1✔
754

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

758
   p11_low_level.login(UserType::User, PIN());
1✔
759

760
   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
761

762
   std::string new_label = "A modified data object";
1✔
763

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

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

777
   Test::Result result = test_function("C_SetAttributeValue", bind);
2✔
778

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

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

786
   result.test_eq("label", new_label, retrieved_label);
1✔
787

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

791
   return result;
2✔
792
}
2✔
793

794
Test::Result test_c_copy_object() {
1✔
795
   RAII_LowLevel p11_low_level;
1✔
796
   SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login();
1✔
797

798
   ObjectHandle object_handle = create_simple_data_object(p11_low_level);
1✔
799
   ObjectHandle copied_object_handle = 0;
1✔
800

801
   std::string copied_label = "A copied data object";
1✔
802

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

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

816
   Test::Result result = test_function("C_CopyObject", binder);
2✔
817

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

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

825
   result.test_eq("label", copied_label, retrieved_label);
1✔
826

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

831
   return result;
2✔
832
}
1✔
833

834
// NOLINTEND(*-avoid-bind)
835

836
class LowLevelTests final : public Test {
×
837
   public:
838
      std::vector<Test::Result> run() override {
1✔
839
         std::vector<std::pair<std::string, std::function<Test::Result()>>> fns = {
1✔
840
            {STRING_AND_FUNCTION(test_c_get_function_list)},
841
            {STRING_AND_FUNCTION(test_low_level_ctor)},
842
            {STRING_AND_FUNCTION(test_initialize_finalize)},
843
            {STRING_AND_FUNCTION(test_c_get_info)},
844
            {STRING_AND_FUNCTION(test_c_get_slot_list)},
845
            {STRING_AND_FUNCTION(test_c_get_slot_info)},
846
            {STRING_AND_FUNCTION(test_c_get_token_info)},
847
            {STRING_AND_FUNCTION(test_c_wait_for_slot_event)},
848
            {STRING_AND_FUNCTION(test_c_get_mechanism_list)},
849
            {STRING_AND_FUNCTION(test_c_get_mechanism_info)},
850
            {STRING_AND_FUNCTION(test_open_close_session)},
851
            {STRING_AND_FUNCTION(test_c_close_all_sessions)},
852
            {STRING_AND_FUNCTION(test_c_get_session_info)},
853
            {STRING_AND_FUNCTION(test_c_init_token)},
854
            {STRING_AND_FUNCTION(test_c_login_logout_security_officier)}, /* only possible if token is initialized */
855
            {STRING_AND_FUNCTION(test_c_init_pin)},
856
            {STRING_AND_FUNCTION(
857
               test_c_login_logout_user)}, /* only possible if token is initialized and user pin is set */
858
            {STRING_AND_FUNCTION(test_c_set_pin)},
859
            {STRING_AND_FUNCTION(test_c_create_object_c_destroy_object)},
860
            {STRING_AND_FUNCTION(test_c_get_object_size)},
861
            {STRING_AND_FUNCTION(test_c_get_attribute_value)},
862
            {STRING_AND_FUNCTION(test_c_set_attribute_value)},
863
            {STRING_AND_FUNCTION(test_c_copy_object)}};
24✔
864

865
         return run_pkcs11_tests("PKCS11 low level", fns);
2✔
866
      }
2✔
867
};
868

869
BOTAN_REGISTER_SERIALIZED_TEST("pkcs11", "pkcs11-lowlevel", LowLevelTests);
870

871
   #endif
872
#endif
873

874
}  // namespace
875
}  // 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