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

randombit / botan / 20902528672

11 Jan 2026 06:12PM UTC coverage: 90.457% (+0.02%) from 90.434%
20902528672

push

github

web-flow
Merge pull request #5217 from Rohde-Schwarz/feature/ffi_issuer_subject_alt_name_and_name_constraints

FFI: Query Certificate Subject/Issuer Alternative Names and Name Constraints

102095 of 112866 relevant lines covered (90.46%)

12763301.21 hits per line

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

94.54
/src/lib/ffi/ffi_cert.cpp
1
/*
2
* (C) 2015,2017,2018 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include <botan/ffi.h>
8

9
#include <botan/internal/ffi_pkey.h>
10
#include <botan/internal/ffi_util.h>
11
#include <memory>
12

13
#if defined(BOTAN_HAS_X509_CERTIFICATES)
14
   #include <botan/data_src.h>
15
   #include <botan/x509_crl.h>
16
   #include <botan/x509cert.h>
17
   #include <botan/x509path.h>
18
   #include <botan/internal/ffi_oid.h>
19
#endif
20

21
extern "C" {
22

23
using namespace Botan_FFI;
24

25
#if defined(BOTAN_HAS_X509_CERTIFICATES)
26

27
BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
30✔
28
BOTAN_FFI_DECLARE_STRUCT(botan_x509_general_name_struct, Botan::GeneralName, 0x563654FD);
198✔
29

30
#endif
31

32
int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path) {
29✔
33
   if(cert_obj == nullptr || cert_path == nullptr) {
29✔
34
      return BOTAN_FFI_ERROR_NULL_POINTER;
35
   }
36

37
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
38

39
   return ffi_guard_thunk(__func__, [=]() -> int {
29✔
40
      auto c = std::make_unique<Botan::X509_Certificate>(cert_path);
29✔
41
      return ffi_new_object(cert_obj, std::move(c));
29✔
42
   });
58✔
43

44
#else
45
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
46
#endif
47
}
48

49
int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert) {
1✔
50
   if(cert_obj == nullptr) {
1✔
51
      return BOTAN_FFI_ERROR_NULL_POINTER;
52
   }
53

54
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
55

56
   return ffi_guard_thunk(__func__, [=]() -> int {
1✔
57
      auto c = std::make_unique<Botan::X509_Certificate>(safe_get(cert));
1✔
58
      return ffi_new_object(cert_obj, std::move(c));
1✔
59
   });
2✔
60

61
#else
62
   BOTAN_UNUSED(cert);
63
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
64
#endif
65
}
66

67
int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert_bits[], size_t cert_bits_len) {
×
68
   if(cert_obj == nullptr || cert_bits == nullptr) {
×
69
      return BOTAN_FFI_ERROR_NULL_POINTER;
70
   }
71

72
#if defined(BOTAN_HAS_X509_CERTIFICATES)
73
   return ffi_guard_thunk(__func__, [=]() -> int {
×
74
      Botan::DataSource_Memory bits(cert_bits, cert_bits_len);
×
75
      auto c = std::make_unique<Botan::X509_Certificate>(bits);
×
76
      return ffi_new_object(cert_obj, std::move(c));
×
77
   });
×
78
#else
79
   BOTAN_UNUSED(cert_bits_len);
80
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
81
#endif
82
}
83

84
int botan_x509_cert_is_ca(botan_x509_cert_t cert) {
2✔
85
#if defined(BOTAN_HAS_X509_CERTIFICATES)
86
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return c.is_CA_cert() ? BOTAN_FFI_SUCCESS : 1; });
4✔
87
#else
88
   BOTAN_UNUSED(cert);
89
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
90
#endif
91
}
92

93
int botan_x509_cert_get_path_length_constraint(botan_x509_cert_t cert, size_t* path_limit) {
2✔
94
#if defined(BOTAN_HAS_X509_CERTIFICATES)
95
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
4✔
96
      if(Botan::any_null_pointers(path_limit)) {
97
         return BOTAN_FFI_ERROR_NULL_POINTER;
98
      }
99

100
      if(const auto path_len = c.path_length_constraint()) {
101
         *path_limit = path_len.value();
102
         return BOTAN_FFI_SUCCESS;
103
      } else {
104
         return BOTAN_FFI_ERROR_NO_VALUE;
105
      }
106
   });
107
#else
108
   BOTAN_UNUSED(cert, path_limit);
109
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
110
#endif
111
}
112

113
int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key) {
2✔
114
   if(key == nullptr) {
2✔
115
      return BOTAN_FFI_ERROR_NULL_POINTER;
116
   }
117

118
   *key = nullptr;
2✔
119

120
#if defined(BOTAN_HAS_X509_CERTIFICATES)
121
   return ffi_guard_thunk(__func__, [=]() -> int {
2✔
122
      auto public_key = safe_get(cert).subject_public_key();
2✔
123
      return ffi_new_object(key, std::move(public_key));
2✔
124
   });
4✔
125
#else
126
   BOTAN_UNUSED(cert);
127
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
128
#endif
129
}
130

131
int botan_x509_cert_get_issuer_dn(
8✔
132
   botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) {
133
#if defined(BOTAN_HAS_X509_CERTIFICATES)
134
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
16✔
135
      auto issuer_info = c.issuer_info(key);
136
      if(index < issuer_info.size()) {
137
         // TODO(Botan4) change the type of out and remove this cast
138
         return write_str_output(reinterpret_cast<char*>(out), out_len, c.issuer_info(key).at(index));
139
      } else {
140
         return BOTAN_FFI_ERROR_BAD_PARAMETER;
141
      }
142
   });
143
#else
144
   BOTAN_UNUSED(cert, key, index, out, out_len);
145
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
146
#endif
147
}
148

149
int botan_x509_cert_get_subject_dn(
8✔
150
   botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) {
151
#if defined(BOTAN_HAS_X509_CERTIFICATES)
152
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
16✔
153
      auto subject_info = c.subject_info(key);
154
      if(index < subject_info.size()) {
155
         // TODO(Botan4) change the type of out and remove this cast
156
         return write_str_output(reinterpret_cast<char*>(out), out_len, c.subject_info(key).at(index));
157
      } else {
158
         return BOTAN_FFI_ERROR_BAD_PARAMETER;
159
      }
160
   });
161
#else
162
   BOTAN_UNUSED(cert, key, index, out, out_len);
163
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
164
#endif
165
}
166

167
int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len) {
2✔
168
   return copy_view_str(reinterpret_cast<uint8_t*>(out), out_len, botan_x509_cert_view_as_string, cert);
2✔
169
}
170

171
int botan_x509_cert_view_as_string(botan_x509_cert_t cert, botan_view_ctx ctx, botan_view_str_fn view) {
3✔
172
#if defined(BOTAN_HAS_X509_CERTIFICATES)
173
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return invoke_view_callback(view, ctx, c.to_string()); });
9✔
174
#else
175
   BOTAN_UNUSED(cert, ctx, view);
176
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
177
#endif
178
}
179

180
int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage) {
7✔
181
#if defined(BOTAN_HAS_X509_CERTIFICATES)
182
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
14✔
183
      const Botan::Key_Constraints k = static_cast<Botan::Key_Constraints>(key_usage);
184
      if(c.allowed_usage(k)) {
185
         return BOTAN_FFI_SUCCESS;
186
      }
187
      return 1;
188
   });
189
#else
190
   BOTAN_UNUSED(cert, key_usage);
191
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
192
#endif
193
}
194

195
int botan_x509_cert_allowed_extended_usage_str(botan_x509_cert_t cert, const char* oid) {
12✔
196
#if defined(BOTAN_HAS_X509_CERTIFICATES)
197
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
32✔
198
      if(Botan::any_null_pointers(oid)) {
199
         return BOTAN_FFI_ERROR_NULL_POINTER;
200
      }
201

202
      return c.has_ex_constraint(oid) ? BOTAN_FFI_SUCCESS : 1;
203
   });
204
#else
205
   BOTAN_UNUSED(cert, oid);
206
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
207
#endif
208
}
209

210
int botan_x509_cert_allowed_extended_usage_oid(botan_x509_cert_t cert, botan_asn1_oid_t oid) {
4✔
211
#if defined(BOTAN_HAS_X509_CERTIFICATES)
212
   return BOTAN_FFI_VISIT(
8✔
213
      cert, [=](const auto& c) -> int { return c.has_ex_constraint(safe_get(oid)) ? BOTAN_FFI_SUCCESS : 1; });
214
#else
215
   BOTAN_UNUSED(cert, oid);
216
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
217
#endif
218
}
219

220
int botan_x509_cert_destroy(botan_x509_cert_t cert) {
30✔
221
#if defined(BOTAN_HAS_X509_CERTIFICATES)
222
   return BOTAN_FFI_CHECKED_DELETE(cert);
30✔
223
#else
224
   BOTAN_UNUSED(cert);
225
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
226
#endif
227
}
228

229
int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len) {
3✔
230
#if defined(BOTAN_HAS_X509_CERTIFICATES)
231
   return BOTAN_FFI_VISIT(cert,
6✔
232
                          [=](const auto& c) { return write_str_output(out, out_len, c.not_before().to_string()); });
233
#else
234
   BOTAN_UNUSED(cert, out, out_len);
235
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
236
#endif
237
}
238

239
int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len) {
2✔
240
#if defined(BOTAN_HAS_X509_CERTIFICATES)
241
   return BOTAN_FFI_VISIT(cert,
4✔
242
                          [=](const auto& c) { return write_str_output(out, out_len, c.not_after().to_string()); });
243
#else
244
   BOTAN_UNUSED(cert, out, out_len);
245
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
246
#endif
247
}
248

249
int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch) {
2✔
250
#if defined(BOTAN_HAS_X509_CERTIFICATES)
251
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_before().time_since_epoch(); });
4✔
252
#else
253
   BOTAN_UNUSED(cert, time_since_epoch);
254
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
255
#endif
256
}
257

258
int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch) {
2✔
259
#if defined(BOTAN_HAS_X509_CERTIFICATES)
260
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_after().time_since_epoch(); });
4✔
261
#else
262
   BOTAN_UNUSED(cert, time_since_epoch);
263
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
264
#endif
265
}
266

267
int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
5✔
268
#if defined(BOTAN_HAS_X509_CERTIFICATES)
269
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.serial_number()); });
10✔
270
#else
271
   BOTAN_UNUSED(cert, out, out_len);
272
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
273
#endif
274
}
275

276
int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len) {
3✔
277
#if defined(BOTAN_HAS_X509_CERTIFICATES)
278
   // TODO(Botan4) change the type of out and remove this cast
279

280
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) {
6✔
281
      return write_str_output(reinterpret_cast<char*>(out), out_len, c.fingerprint(hash));
282
   });
283
#else
284
   BOTAN_UNUSED(cert, hash, out, out_len);
285
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
286
#endif
287
}
288

289
int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
1✔
290
#if defined(BOTAN_HAS_X509_CERTIFICATES)
291
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.authority_key_id()); });
2✔
292
#else
293
   BOTAN_UNUSED(cert, out, out_len);
294
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
295
#endif
296
}
297

298
int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
3✔
299
#if defined(BOTAN_HAS_X509_CERTIFICATES)
300
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.subject_key_id()); });
6✔
301
#else
302
   BOTAN_UNUSED(cert, out, out_len);
303
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
304
#endif
305
}
306

307
int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
2✔
308
   return copy_view_bin(out, out_len, botan_x509_cert_view_public_key_bits, cert);
2✔
309
}
310

311
int botan_x509_cert_view_public_key_bits(botan_x509_cert_t cert, botan_view_ctx ctx, botan_view_bin_fn view) {
3✔
312
#if defined(BOTAN_HAS_X509_CERTIFICATES)
313
   return BOTAN_FFI_VISIT(cert,
6✔
314
                          [=](const auto& c) { return invoke_view_callback(view, ctx, c.subject_public_key_bits()); });
315
#else
316
   BOTAN_UNUSED(cert, ctx, view);
317
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
318
#endif
319
}
320
}
321

322
namespace {
323

324
std::optional<botan_x509_general_name_types> to_botan_x509_general_name_types(Botan::GeneralName::NameType gn_type) {
301✔
325
   using Type = Botan::GeneralName::NameType;
301✔
326
   switch(gn_type) {
301✔
327
      case Type::Unknown:
1✔
328
         return std::nullopt;
1✔
329
      case Type::RFC822:
83✔
330
         return BOTAN_X509_EMAIL_ADDRESS;
83✔
331
      case Type::DNS:
73✔
332
         return BOTAN_X509_DNS_NAME;
73✔
333
      case Type::URI:
28✔
334
         return BOTAN_X509_URI;
28✔
335
      case Type::DN:
95✔
336
         return BOTAN_X509_DIRECTORY_NAME;
95✔
337
      case Type::IPv4:
21✔
338
         return BOTAN_X509_IP_ADDRESS;
21✔
339
      case Type::Other:
×
340
         return BOTAN_X509_OTHER_NAME;
×
341
   }
342

343
   BOTAN_ASSERT_UNREACHABLE();
×
344
}
345

346
}  // namespace
347

348
extern "C" {
349

350
int botan_x509_general_name_get_type(botan_x509_general_name_t name, unsigned int* type) {
198✔
351
#if defined(BOTAN_HAS_X509_CERTIFICATES)
352
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) {
396✔
353
      if(Botan::any_null_pointers(type)) {
354
         return BOTAN_FFI_ERROR_NULL_POINTER;
355
      }
356

357
      const auto mapped_type = to_botan_x509_general_name_types(n.type_code());
358
      if(!mapped_type.has_value()) {
359
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
360
      }
361

362
      *type = mapped_type.value();
363
      if(*type == BOTAN_X509_OTHER_NAME /* ... viewing of other-names not supported */) {
364
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
365
      }
366

367
      return BOTAN_FFI_SUCCESS;
368
   });
369
#else
370
   BOTAN_UNUSED(name, type);
371
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
372
#endif
373
}
374

375
int botan_x509_general_name_view_string_value(botan_x509_general_name_t name,
66✔
376
                                              botan_view_ctx ctx,
377
                                              botan_view_str_fn view) {
378
#if defined(BOTAN_HAS_X509_CERTIFICATES)
379
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int {
197✔
380
      const auto type = to_botan_x509_general_name_types(n.type_code());
381
      if(!type) {
382
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
383
      }
384

385
      if(type != BOTAN_X509_EMAIL_ADDRESS && type != BOTAN_X509_DNS_NAME && type != BOTAN_X509_URI &&
386
         type != BOTAN_X509_IP_ADDRESS) {
387
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
388
      }
389

390
      return invoke_view_callback(view, ctx, n.name());
391
   });
392
#else
393
   BOTAN_UNUSED(name, ctx, view);
394
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
395
#endif
396
}
397

398
int botan_x509_general_name_view_binary_value(botan_x509_general_name_t name,
37✔
399
                                              botan_view_ctx ctx,
400
                                              botan_view_bin_fn view) {
401
#if defined(BOTAN_HAS_X509_CERTIFICATES)
402
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int {
144✔
403
      const auto type = to_botan_x509_general_name_types(n.type_code());
404
      if(!type) {
405
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
406
      }
407

408
      if(type != BOTAN_X509_DIRECTORY_NAME && type != BOTAN_X509_IP_ADDRESS) {
409
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
410
      }
411

412
      return invoke_view_callback(view, ctx, n.binary_name());
413
   });
414
#else
415
   BOTAN_UNUSED(name, ctx, view);
416
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
417
#endif
418
}
419

420
int botan_x509_general_name_destroy(botan_x509_general_name_t name) {
198✔
421
#if defined(BOTAN_HAS_X509_CERTIFICATES)
422
   return BOTAN_FFI_CHECKED_DELETE(name);
198✔
423
#else
424
   BOTAN_UNUSED(name);
425
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
426
#endif
427
}
428

429
int botan_x509_cert_permitted_name_constraints(botan_x509_cert_t cert,
76✔
430
                                               size_t index,
431
                                               botan_x509_general_name_t* constraint) {
432
#if defined(BOTAN_HAS_X509_CERTIFICATES)
433
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
152✔
434
      if(Botan::any_null_pointers(constraint)) {
435
         return BOTAN_FFI_ERROR_NULL_POINTER;
436
      }
437

438
      const auto& constraints = c.name_constraints().permitted();
439
      if(index >= constraints.size()) {
440
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
441
      }
442

443
      return ffi_new_object(constraint, std::make_unique<Botan::GeneralName>(constraints[index].base()));
444
   });
445
#else
446
   BOTAN_UNUSED(cert, index, constraint);
447
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
448
#endif
449
}
450

451
int botan_x509_cert_excluded_name_constraints(botan_x509_cert_t cert,
4✔
452
                                              size_t index,
453
                                              botan_x509_general_name_t* constraint) {
454
#if defined(BOTAN_HAS_X509_CERTIFICATES)
455
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
8✔
456
      if(Botan::any_null_pointers(constraint)) {
457
         return BOTAN_FFI_ERROR_NULL_POINTER;
458
      }
459

460
      const auto& constraints = c.name_constraints().excluded();
461
      if(index >= constraints.size()) {
462
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
463
      }
464

465
      return ffi_new_object(constraint, std::make_unique<Botan::GeneralName>(constraints[index].base()));
466
   });
467
#else
468
   BOTAN_UNUSED(cert, index, constraint);
469
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
470
#endif
471
}
472
}
473

474
namespace {
475

476
/**
477
 * As specified in RFC 5280 Section 4.2.1.6. alternative names essentially are a
478
 * collection of GeneralNames. This allows mapping a single entry of @p altnames
479
 * to a GeneralName by its @p index. If the index is out of range, std::nullopt
480
 * is returned.
481
 */
482
std::optional<Botan::GeneralName> extract_general_name_at(const Botan::AlternativeName& altnames, size_t index) {
132✔
483
   if(index < altnames.email().size()) {
132✔
484
      auto itr = altnames.email().begin();
12✔
485
      std::advance(itr, index);
12✔
486
      return Botan::GeneralName::email(*itr);
36✔
487
   }
488
   index -= altnames.email().size();
120✔
489

490
   if(index < altnames.dns().size()) {
120✔
491
      auto itr = altnames.dns().begin();
36✔
492
      std::advance(itr, index);
36✔
493
      return Botan::GeneralName::dns(*itr);
108✔
494
   }
495
   index -= altnames.dns().size();
84✔
496

497
   if(index < altnames.directory_names().size()) {
84✔
498
      auto itr = altnames.directory_names().begin();
36✔
499
      std::advance(itr, index);
36✔
500
      return Botan::GeneralName::directory_name(*itr);
108✔
501
   }
502
   index -= altnames.directory_names().size();
48✔
503

504
   if(index < altnames.uris().size()) {
48✔
505
      auto itr = altnames.uris().begin();
24✔
506
      std::advance(itr, index);
24✔
507
      return Botan::GeneralName::uri(*itr);
72✔
508
   }
509
   index -= altnames.uris().size();
24✔
510

511
   if(index < altnames.ipv4_address().size()) {
24✔
512
      auto itr = altnames.ipv4_address().begin();
12✔
513
      std::advance(itr, index);
12✔
514
      return Botan::GeneralName::ipv4_address(*itr);
36✔
515
   }
516

517
   return std::nullopt;
12✔
518
}
519

520
}  // namespace
521

522
extern "C" {
523

524
int botan_x509_cert_subject_alternative_names(botan_x509_cert_t cert,
73✔
525
                                              size_t index,
526
                                              botan_x509_general_name_t* alt_name) {
527
#if defined(BOTAN_HAS_X509_CERTIFICATES)
528
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
152✔
529
      if(Botan::any_null_pointers(alt_name)) {
530
         return BOTAN_FFI_ERROR_NULL_POINTER;
531
      }
532

533
      if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.SubjectAlternativeName"))) {
534
         return BOTAN_FFI_ERROR_NO_VALUE;
535
      }
536

537
      if(auto name = extract_general_name_at(c.subject_alt_name(), index)) {
538
         return ffi_new_object(alt_name, std::make_unique<Botan::GeneralName>(std::move(name).value()));
539
      }
540

541
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
542
   });
543
#else
544
   BOTAN_UNUSED(cert, index, alt_name);
545
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
546
#endif
547
}
548

549
int botan_x509_cert_issuer_alternative_names(botan_x509_cert_t cert,
61✔
550
                                             size_t index,
551
                                             botan_x509_general_name_t* alt_name) {
552
#if defined(BOTAN_HAS_X509_CERTIFICATES)
553
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
128✔
554
      if(Botan::any_null_pointers(alt_name)) {
555
         return BOTAN_FFI_ERROR_NULL_POINTER;
556
      }
557

558
      if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.IssuerAlternativeName"))) {
559
         return BOTAN_FFI_ERROR_NO_VALUE;
560
      }
561

562
      if(auto name = extract_general_name_at(c.issuer_alt_name(), index)) {
563
         return ffi_new_object(alt_name, std::make_unique<Botan::GeneralName>(std::move(name).value()));
564
      }
565

566
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
567
   });
568
#else
569
   BOTAN_UNUSED(cert, index, alt_name);
570
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
571
#endif
572
}
573

574
int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname) {
6✔
575
   if(hostname == nullptr) {
6✔
576
      return BOTAN_FFI_ERROR_NULL_POINTER;
577
   }
578

579
#if defined(BOTAN_HAS_X509_CERTIFICATES)
580
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return c.matches_dns_name(hostname) ? 0 : -1; });
12✔
581
#else
582
   BOTAN_UNUSED(cert);
583
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
584
#endif
585
}
586

587
int botan_x509_cert_verify(int* result_code,
4✔
588
                           botan_x509_cert_t cert,
589
                           const botan_x509_cert_t* intermediates,
590
                           size_t intermediates_len,
591
                           const botan_x509_cert_t* trusted,
592
                           size_t trusted_len,
593
                           const char* trusted_path,
594
                           size_t required_strength,
595
                           const char* hostname_cstr,
596
                           uint64_t reference_time) {
597
   if(required_strength == 0) {
4✔
598
      required_strength = 110;
3✔
599
   }
600

601
#if defined(BOTAN_HAS_X509_CERTIFICATES)
602
   return ffi_guard_thunk(__func__, [=]() -> int {
4✔
603
      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
4✔
604
      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
4✔
605
      const auto validation_time = reference_time == 0
4✔
606
                                      ? std::chrono::system_clock::now()
4✔
607
                                      : std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
×
608

609
      std::vector<Botan::X509_Certificate> end_certs;
4✔
610
      end_certs.push_back(safe_get(cert));
4✔
611
      for(size_t i = 0; i != intermediates_len; ++i) {
9✔
612
         end_certs.push_back(safe_get(intermediates[i]));
5✔
613
      }
614

615
      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
4✔
616
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
4✔
617
      std::vector<Botan::Certificate_Store*> trusted_roots;
4✔
618

619
      if(trusted_path != nullptr && *trusted_path != 0) {
4✔
620
         trusted_from_path = std::make_unique<Botan::Certificate_Store_In_Memory>(trusted_path);
×
621
         trusted_roots.push_back(trusted_from_path.get());
×
622
      }
623

624
      if(trusted_len > 0) {
4✔
625
         trusted_extra = std::make_unique<Botan::Certificate_Store_In_Memory>();
8✔
626
         for(size_t i = 0; i != trusted_len; ++i) {
8✔
627
            trusted_extra->add_certificate(safe_get(trusted[i]));
4✔
628
         }
629
         trusted_roots.push_back(trusted_extra.get());
4✔
630
      }
631

632
      const Botan::Path_Validation_Restrictions restrictions(false, required_strength);
8✔
633

634
      auto validation_result =
4✔
635
         Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time);
4✔
636

637
      if(result_code != nullptr) {
4✔
638
         *result_code = static_cast<int>(validation_result.result());
4✔
639
      }
640

641
      if(validation_result.successful_validation()) {
4✔
642
         return 0;
643
      } else {
644
         return 1;
3✔
645
      }
646
   });
4✔
647
#else
648
   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
649
   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time);
650
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
651
#endif
652
}
653

654
const char* botan_x509_cert_validation_status(int code) {
11✔
655
   if(code < 0) {
11✔
656
      return nullptr;
657
   }
658

659
#if defined(BOTAN_HAS_X509_CERTIFICATES)
660
   const Botan::Certificate_Status_Code sc = static_cast<Botan::Certificate_Status_Code>(code);
11✔
661
   return Botan::to_string(sc);
11✔
662
#else
663
   return nullptr;
664
#endif
665
}
666

667
#if defined(BOTAN_HAS_X509_CERTIFICATES)
668

669
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910);
8✔
670
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_entry_struct, Botan::CRL_Entry, 0x4EAA5346);
1✔
671

672
#endif
673

674
int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path) {
7✔
675
   if(crl_obj == nullptr || crl_path == nullptr) {
7✔
676
      return BOTAN_FFI_ERROR_NULL_POINTER;
677
   }
678

679
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
680

681
   return ffi_guard_thunk(__func__, [=]() -> int {
7✔
682
      auto c = std::make_unique<Botan::X509_CRL>(crl_path);
7✔
683
      return ffi_new_object(crl_obj, std::move(c));
14✔
684
   });
14✔
685

686
#else
687
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
688
#endif
689
}
690

691
int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len) {
1✔
692
   if(crl_obj == nullptr || crl_bits == nullptr) {
1✔
693
      return BOTAN_FFI_ERROR_NULL_POINTER;
694
   }
695

696
#if defined(BOTAN_HAS_X509_CERTIFICATES)
697
   return ffi_guard_thunk(__func__, [=]() -> int {
1✔
698
      Botan::DataSource_Memory bits(crl_bits, crl_bits_len);
1✔
699
      auto c = std::make_unique<Botan::X509_CRL>(bits);
1✔
700
      return ffi_new_object(crl_obj, std::move(c));
1✔
701
   });
3✔
702
#else
703
   BOTAN_UNUSED(crl_bits_len);
704
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
705
#endif
706
}
707

708
int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) {
2✔
709
#if defined(BOTAN_HAS_X509_CERTIFICATES)
710
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) {
4✔
711
      if(Botan::any_null_pointers(time_since_epoch)) {
712
         return BOTAN_FFI_ERROR_NULL_POINTER;
713
      }
714
      *time_since_epoch = c.this_update().time_since_epoch();
715
      return BOTAN_FFI_SUCCESS;
716
   });
717
#else
718
   BOTAN_UNUSED(crl, time_since_epoch);
719
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
720
#endif
721
}
722

723
int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) {
4✔
724
#if defined(BOTAN_HAS_X509_CERTIFICATES)
725
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) {
8✔
726
      const auto& time = c.next_update();
727
      if(!time.time_is_set()) {
728
         return BOTAN_FFI_ERROR_NO_VALUE;
729
      }
730

731
      if(Botan::any_null_pointers(time_since_epoch)) {
732
         return BOTAN_FFI_ERROR_NULL_POINTER;
733
      }
734

735
      *time_since_epoch = c.next_update().time_since_epoch();
736
      return BOTAN_FFI_SUCCESS;
737
   });
738
#else
739
   BOTAN_UNUSED(crl, time_since_epoch);
740
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
741
#endif
742
}
743

744
int botan_x509_crl_destroy(botan_x509_crl_t crl) {
8✔
745
#if defined(BOTAN_HAS_X509_CERTIFICATES)
746
   return BOTAN_FFI_CHECKED_DELETE(crl);
8✔
747
#else
748
   BOTAN_UNUSED(crl);
749
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
750
#endif
751
}
752

753
int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert) {
6✔
754
#if defined(BOTAN_HAS_X509_CERTIFICATES)
755
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) { return c.is_revoked(safe_get(cert)) ? 0 : -1; });
12✔
756
#else
757
   BOTAN_UNUSED(cert);
758
   BOTAN_UNUSED(crl);
759
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
760
#endif
761
}
762

763
int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry) {
3✔
764
#if defined(BOTAN_HAS_X509_CERTIFICATES)
765
   return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) -> int {
6✔
766
      const auto& entries = c.get_revoked();
767
      if(index >= entries.size()) {
768
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
769
      }
770

771
      if(Botan::any_null_pointers(entry)) {
772
         return BOTAN_FFI_ERROR_NULL_POINTER;
773
      }
774

775
      return ffi_new_object(entry, std::make_unique<Botan::CRL_Entry>(entries[index]));
776
   });
777
#else
778
   BOTAN_UNUSED(crl, index, entry);
779
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
780
#endif
781
}
782

783
int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry) {
1✔
784
#if defined(BOTAN_HAS_X509_CERTIFICATES)
785
   return BOTAN_FFI_CHECKED_DELETE(entry);
1✔
786
#else
787
   BOTAN_UNUSED(entry);
788
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
789
#endif
790
}
791

792
int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code) {
1✔
793
#if defined(BOTAN_HAS_X509_CERTIFICATES)
794
   return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
2✔
795
      if(Botan::any_null_pointers(reason_code)) {
796
         return BOTAN_FFI_ERROR_NULL_POINTER;
797
      }
798

799
      *reason_code = static_cast<int>(e.reason_code());
800
      return BOTAN_FFI_SUCCESS;
801
   });
802
#else
803
   BOTAN_UNUSED(entry, reason_code);
804
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
805
#endif
806
}
807

808
int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view) {
1✔
809
#if defined(BOTAN_HAS_X509_CERTIFICATES)
810
   return BOTAN_FFI_VISIT(
2✔
811
      entry, [=](const Botan::CRL_Entry& e) { return invoke_view_callback(view, ctx, e.serial_number()); });
812
#else
813
   BOTAN_UNUSED(entry, ctx, view);
814
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
815
#endif
816
}
817

818
int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch) {
1✔
819
#if defined(BOTAN_HAS_X509_CERTIFICATES)
820
   return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
2✔
821
      if(Botan::any_null_pointers(time_since_epoch)) {
822
         return BOTAN_FFI_ERROR_NULL_POINTER;
823
      }
824

825
      *time_since_epoch = e.expire_time().time_since_epoch();
826
      return BOTAN_FFI_SUCCESS;
827
   });
828
#else
829
   BOTAN_UNUSED(entry, time_since_epoch);
830
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
831
#endif
832
}
833

834
int botan_x509_cert_verify_with_crl(int* result_code,
12✔
835
                                    botan_x509_cert_t cert,
836
                                    const botan_x509_cert_t* intermediates,
837
                                    size_t intermediates_len,
838
                                    const botan_x509_cert_t* trusted,
839
                                    size_t trusted_len,
840
                                    const botan_x509_crl_t* crls,
841
                                    size_t crls_len,
842
                                    const char* trusted_path,
843
                                    size_t required_strength,
844
                                    const char* hostname_cstr,
845
                                    uint64_t reference_time) {
846
   if(required_strength == 0) {
12✔
847
      required_strength = 110;
2✔
848
   }
849

850
#if defined(BOTAN_HAS_X509_CERTIFICATES)
851
   return ffi_guard_thunk(__func__, [=]() -> int {
12✔
852
      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
14✔
853
      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
12✔
854
      const auto validation_time = reference_time == 0
12✔
855
                                      ? std::chrono::system_clock::now()
12✔
856
                                      : std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
1✔
857

858
      std::vector<Botan::X509_Certificate> end_certs;
12✔
859
      end_certs.push_back(safe_get(cert));
12✔
860
      for(size_t i = 0; i != intermediates_len; ++i) {
30✔
861
         end_certs.push_back(safe_get(intermediates[i]));
18✔
862
      }
863

864
      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
12✔
865
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
12✔
866
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
12✔
867
      std::vector<Botan::Certificate_Store*> trusted_roots;
12✔
868

869
      if(trusted_path != nullptr && *trusted_path != 0) {
12✔
870
         trusted_from_path = std::make_unique<Botan::Certificate_Store_In_Memory>(trusted_path);
2✔
871
         trusted_roots.push_back(trusted_from_path.get());
2✔
872
      }
873

874
      if(trusted_len > 0) {
12✔
875
         trusted_extra = std::make_unique<Botan::Certificate_Store_In_Memory>();
18✔
876
         for(size_t i = 0; i != trusted_len; ++i) {
18✔
877
            trusted_extra->add_certificate(safe_get(trusted[i]));
9✔
878
         }
879
         trusted_roots.push_back(trusted_extra.get());
9✔
880
      }
881

882
      if(crls_len > 0) {
12✔
883
         trusted_crls = std::make_unique<Botan::Certificate_Store_In_Memory>();
10✔
884
         for(size_t i = 0; i != crls_len; ++i) {
13✔
885
            trusted_crls->add_crl(safe_get(crls[i]));
8✔
886
         }
887
         trusted_roots.push_back(trusted_crls.get());
5✔
888
      }
889

890
      const Botan::Path_Validation_Restrictions restrictions(false, required_strength);
24✔
891

892
      auto validation_result =
12✔
893
         Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time);
12✔
894

895
      if(result_code != nullptr) {
12✔
896
         *result_code = static_cast<int>(validation_result.result());
12✔
897
      }
898

899
      if(validation_result.successful_validation()) {
12✔
900
         return 0;
901
      } else {
902
         return 1;
8✔
903
      }
904
   });
14✔
905
#else
906
   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
907
   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time, crls, crls_len);
908
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
909
#endif
910
}
911
}
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