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

randombit / botan / 20984815551

14 Jan 2026 06:31AM UTC coverage: 90.409% (+0.02%) from 90.393%
20984815551

Pull #5188

github

web-flow
Merge 9d9388ad1 into 81beaa29d
Pull Request #5188: FFI: Generic getter(s) for X.509 objects

102622 of 113508 relevant lines covered (90.41%)

12770133.01 hits per line

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

96.23
/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/x509_ext.h>
17
   #include <botan/x509cert.h>
18
   #include <botan/x509path.h>
19
   #include <botan/internal/ffi_mp.h>
20
   #include <botan/internal/ffi_oid.h>
21
   #include <botan/internal/loadstor.h>
22
   #include <botan/internal/stl_util.h>
23
#endif
24

25
namespace {
26

27
/**
28
 * Given some enumerator-style function @p fn, count how many values it can
29
 * produce before returning BOTAN_FFI_ERROR_OUT_OF_RANGE. If the first call to
30
 * @p fn returns BOTAN_FFI_ERROR_NO_VALUE, zero is written to @p count.
31
 *
32
 * If this function returns BOTAN_FFI_SUCCESS, @p count contains the number of
33
 * values that can be enumerated. Otherwise, the value of @p count is undefined.
34
 */
35
template <std::invocable<size_t> EnumeratorT>
36
int enumerator_count_values(size_t* count, EnumeratorT fn) {
11✔
37
   if(Botan::any_null_pointers(count)) {
11✔
38
      return BOTAN_FFI_ERROR_NULL_POINTER;
39
   }
40

41
   *count = 0;
11✔
42
   for(;; ++(*count)) {
12✔
43
      const auto rc = fn(*count);
46✔
44
      switch(rc) {
23✔
45
         case BOTAN_FFI_ERROR_NO_VALUE:
46
         case BOTAN_FFI_ERROR_OUT_OF_RANGE:
47
            // hit the end of the enumeration
48
            return BOTAN_FFI_SUCCESS;
49
         case BOTAN_FFI_SUCCESS:
50
            // got a value, continue counting
51
            break;
52
         default:
53
            // unexpected error from enumerator function
54
            return rc;
55
      }
56
   }
57
}
58

59
}  // namespace
60

61
extern "C" {
62

63
using namespace Botan_FFI;
64

65
#if defined(BOTAN_HAS_X509_CERTIFICATES)
66

67
BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
32✔
68
BOTAN_FFI_DECLARE_STRUCT(botan_x509_general_name_struct, Botan::GeneralName, 0x563654FD);
198✔
69

70
#endif
71

72
int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path) {
31✔
73
   if(cert_obj == nullptr || cert_path == nullptr) {
31✔
74
      return BOTAN_FFI_ERROR_NULL_POINTER;
75
   }
76

77
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
78

79
   return ffi_guard_thunk(__func__, [=]() -> int {
31✔
80
      auto c = std::make_unique<Botan::X509_Certificate>(cert_path);
31✔
81
      return ffi_new_object(cert_obj, std::move(c));
31✔
82
   });
62✔
83

84
#else
85
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
86
#endif
87
}
88

89
int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert) {
1✔
90
   if(cert_obj == nullptr) {
1✔
91
      return BOTAN_FFI_ERROR_NULL_POINTER;
92
   }
93

94
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
95

96
   return ffi_guard_thunk(__func__, [=]() -> int {
1✔
97
      auto c = std::make_unique<Botan::X509_Certificate>(safe_get(cert));
1✔
98
      return ffi_new_object(cert_obj, std::move(c));
1✔
99
   });
2✔
100

101
#else
102
   BOTAN_UNUSED(cert);
103
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
104
#endif
105
}
106

107
int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert_bits[], size_t cert_bits_len) {
×
108
   if(cert_obj == nullptr || cert_bits == nullptr) {
×
109
      return BOTAN_FFI_ERROR_NULL_POINTER;
110
   }
111

112
#if defined(BOTAN_HAS_X509_CERTIFICATES)
113
   return ffi_guard_thunk(__func__, [=]() -> int {
×
114
      Botan::DataSource_Memory bits(cert_bits, cert_bits_len);
×
115
      auto c = std::make_unique<Botan::X509_Certificate>(bits);
×
116
      return ffi_new_object(cert_obj, std::move(c));
×
117
   });
×
118
#else
119
   BOTAN_UNUSED(cert_bits_len);
120
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
121
#endif
122
}
123
}
124

125
namespace {
126

127
int botan_x509_object_view_value(const Botan::X509_Object& object,
6✔
128
                                 botan_x509_value_type value_type,
129
                                 size_t index,
130
                                 botan_view_ctx ctx,
131
                                 botan_view_str_fn view_fn) {
132
   if(index != 0) {
6✔
133
      // As of now there are no multi-value generic string entries.
134
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
135
   }
136

137
   auto view = [=](const std::string& value) { return invoke_view_callback(view_fn, ctx, value); };
4✔
138

139
   switch(value_type) {
4✔
140
      case BOTAN_X509_PEM_ENCODING:
4✔
141
         return view(object.PEM_encode());
8✔
142
      default:
143
         return BOTAN_FFI_ERROR_INTERNAL_ERROR; /* called with unexpected (non-generic) value_type */
144
   }
145
}
146

147
int botan_x509_object_view_value(const Botan::X509_Object& object,
8✔
148
                                 botan_x509_value_type value_type,
149
                                 size_t index,
150
                                 botan_view_ctx ctx,
151
                                 botan_view_bin_fn view_fn) {
152
   if(index != 0) {
8✔
153
      // As of now there are no multi-value generic binary entries.
154
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
155
   }
156

157
   auto view = [=](std::span<const uint8_t> value) { return invoke_view_callback(view_fn, ctx, value); };
2✔
158

159
   switch(value_type) {
8✔
160
      case BOTAN_X509_TBS_DATA_BITS:
2✔
161
         return view(object.tbs_data());
6✔
162
      case BOTAN_X509_SIGNATURE_SCHEME_BITS:
2✔
163
         return view(object.signature_algorithm().BER_encode());
6✔
164
      case BOTAN_X509_SIGNATURE_BITS:
2✔
165
         return view(object.signature());
10✔
166
      case BOTAN_X509_DER_ENCODING:
2✔
167
         return view(object.BER_encode());
6✔
168
      default:
169
         return BOTAN_FFI_ERROR_INTERNAL_ERROR; /* called with unexpected (non-generic) value_type */
170
   }
171
}
172

173
}  // namespace
174

175
extern "C" {
176

177
int botan_x509_cert_view_binary_values(botan_x509_cert_t cert,
22✔
178
                                       botan_x509_value_type value_type,
179
                                       size_t index,
180
                                       botan_view_ctx ctx,
181
                                       botan_view_bin_fn view_fn) {
182
#if defined(BOTAN_HAS_X509_CERTIFICATES)
183
   if(index != 0) {
22✔
184
      // As of now there are no multi-value binary entries.
185
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
186
   }
187

188
   auto view = [=](std::span<const uint8_t> value) -> int {
18✔
189
      if(value.empty()) {
5✔
190
         return BOTAN_FFI_ERROR_NO_VALUE;
191
      } else {
192
         return invoke_view_callback(view_fn, ctx, value);
4✔
193
      }
194
   };
13✔
195

196
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) -> int {
39✔
197
      switch(value_type) {
198
         case BOTAN_X509_SERIAL_NUMBER:
199
            return view(c.serial_number());
200
         case BOTAN_X509_SUBJECT_DN_BITS:
201
            return view(c.raw_subject_dn());
202
         case BOTAN_X509_ISSUER_DN_BITS:
203
            return view(c.raw_issuer_dn());
204
         case BOTAN_X509_SUBJECT_KEY_IDENTIFIER:
205
            return view(c.subject_key_id());
206
         case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER:
207
            return view(c.authority_key_id());
208
         case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS:
209
            return view(c.subject_public_key_info());
210

211
         case BOTAN_X509_TBS_DATA_BITS:
212
         case BOTAN_X509_SIGNATURE_SCHEME_BITS:
213
         case BOTAN_X509_SIGNATURE_BITS:
214
         case BOTAN_X509_DER_ENCODING:
215
            return botan_x509_object_view_value(c, value_type, index, ctx, view_fn);
216

217
         case BOTAN_X509_PEM_ENCODING:
218
         case BOTAN_X509_CRL_DISTRIBUTION_URLS:
219
         case BOTAN_X509_OCSP_RESPONDER_URLS:
220
         case BOTAN_X509_CA_ISSUERS_URLS:
221
            return BOTAN_FFI_ERROR_NO_VALUE;
222
      }
223

224
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
225
   });
226
#else
227
   BOTAN_UNUSED(cert, value_type, index, ctx, view_fn);
228
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
229
#endif
230
}
231

232
int botan_x509_cert_view_binary_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count) {
5✔
233
   return enumerator_count_values(count, [=](size_t index) {
5✔
234
      return botan_x509_cert_view_binary_values(
10✔
235
         cert, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; });
236
   });
5✔
237
}
238

239
int botan_x509_cert_view_string_values(botan_x509_cert_t cert,
19✔
240
                                       botan_x509_value_type value_type,
241
                                       size_t index,
242
                                       botan_view_ctx ctx,
243
                                       botan_view_str_fn view_fn) {
244
#if defined(BOTAN_HAS_X509_CERTIFICATES)
245
   auto enumerate = [view_fn, ctx](auto values, size_t idx) -> int {
10✔
246
      if(idx >= values.size()) {
10✔
247
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
248
      } else {
249
         return invoke_view_callback(view_fn, ctx, values[idx]);
6✔
250
      }
251
   };
19✔
252

253
   auto enumerate_crl_distribution_points = [view_fn, ctx](const Botan::X509_Certificate& c, size_t idx) -> int {
25✔
254
      const auto* crl_dp_ext =
6✔
255
         c.v3_extensions().get_extension_object_as<Botan::Cert_Extension::CRL_Distribution_Points>();
12✔
256
      if(crl_dp_ext == nullptr) {
6✔
257
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;  // essentially an empty list
258
      }
259

260
      const auto& dps = crl_dp_ext->distribution_points();
5✔
261
      for(size_t i = idx; const auto& dp : dps) {
6✔
262
         const auto& uris = dp.point().uris();
5✔
263
         if(i >= uris.size()) {
5✔
264
            i -= uris.size();
1✔
265
            continue;
1✔
266
         }
267

268
         auto itr = uris.begin();
4✔
269
         std::advance(itr, i);
4✔
270
         return invoke_view_callback(view_fn, ctx, *itr);
8✔
271
      }
272

273
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
274
   };
19✔
275

276
   auto wrap_or_empty = [](std::string str) -> std::vector<std::string> {
23✔
277
      if(str.empty()) {
4✔
278
         return {};
1✔
279
      } else {
280
         return {std::move(str)};
6✔
281
      }
282
   };
3✔
283

284
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) -> int {
38✔
285
      switch(value_type) {
286
         case BOTAN_X509_CRL_DISTRIBUTION_URLS:
287
            return enumerate_crl_distribution_points(c, index);
288
         case BOTAN_X509_OCSP_RESPONDER_URLS:
289
            return enumerate(wrap_or_empty(c.ocsp_responder()), index);
290
         case BOTAN_X509_CA_ISSUERS_URLS:
291
            return enumerate(c.ca_issuers(), index);
292
         case BOTAN_X509_PEM_ENCODING:
293
            return botan_x509_object_view_value(c, value_type, index, ctx, view_fn);
294

295
         case BOTAN_X509_SERIAL_NUMBER:
296
         case BOTAN_X509_SUBJECT_DN_BITS:
297
         case BOTAN_X509_ISSUER_DN_BITS:
298
         case BOTAN_X509_SUBJECT_KEY_IDENTIFIER:
299
         case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER:
300
         case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS:
301
         case BOTAN_X509_TBS_DATA_BITS:
302
         case BOTAN_X509_SIGNATURE_SCHEME_BITS:
303
         case BOTAN_X509_SIGNATURE_BITS:
304
         case BOTAN_X509_DER_ENCODING:
305
            return BOTAN_FFI_ERROR_NO_VALUE;
306
      }
307

308
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
309
   });
310
#else
311
   BOTAN_UNUSED(cert, value_type, index, ctx, view_fn);
312
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
313
#endif
314
}
315

316
int botan_x509_cert_view_string_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count) {
4✔
317
   return enumerator_count_values(count, [=](size_t index) {
4✔
318
      return botan_x509_cert_view_string_values(
10✔
319
         cert, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; });
320
   });
4✔
321
}
322

323
int botan_x509_cert_is_ca(botan_x509_cert_t cert) {
2✔
324
#if defined(BOTAN_HAS_X509_CERTIFICATES)
325
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return c.is_CA_cert() ? BOTAN_FFI_SUCCESS : 1; });
4✔
326
#else
327
   BOTAN_UNUSED(cert);
328
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
329
#endif
330
}
331

332
int botan_x509_cert_get_path_length_constraint(botan_x509_cert_t cert, size_t* path_limit) {
2✔
333
#if defined(BOTAN_HAS_X509_CERTIFICATES)
334
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
4✔
335
      if(Botan::any_null_pointers(path_limit)) {
336
         return BOTAN_FFI_ERROR_NULL_POINTER;
337
      }
338

339
      if(const auto path_len = c.path_length_constraint()) {
340
         *path_limit = path_len.value();
341
         return BOTAN_FFI_SUCCESS;
342
      } else {
343
         return BOTAN_FFI_ERROR_NO_VALUE;
344
      }
345
   });
346
#else
347
   BOTAN_UNUSED(cert, path_limit);
348
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
349
#endif
350
}
351

352
int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key) {
2✔
353
   if(key == nullptr) {
2✔
354
      return BOTAN_FFI_ERROR_NULL_POINTER;
355
   }
356

357
   *key = nullptr;
2✔
358

359
#if defined(BOTAN_HAS_X509_CERTIFICATES)
360
   return ffi_guard_thunk(__func__, [=]() -> int {
2✔
361
      auto public_key = safe_get(cert).subject_public_key();
2✔
362
      return ffi_new_object(key, std::move(public_key));
2✔
363
   });
4✔
364
#else
365
   BOTAN_UNUSED(cert);
366
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
367
#endif
368
}
369

370
int botan_x509_cert_get_issuer_dn(
8✔
371
   botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) {
372
#if defined(BOTAN_HAS_X509_CERTIFICATES)
373
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
16✔
374
      auto issuer_info = c.issuer_info(key);
375
      if(index < issuer_info.size()) {
376
         // TODO(Botan4) change the type of out and remove this cast
377
         return write_str_output(reinterpret_cast<char*>(out), out_len, c.issuer_info(key).at(index));
378
      } else {
379
         return BOTAN_FFI_ERROR_BAD_PARAMETER;  // TODO(Botan4): use BOTAN_FFI_ERROR_OUT_OF_RANGE
380
      }
381
   });
382
#else
383
   BOTAN_UNUSED(cert, key, index, out, out_len);
384
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
385
#endif
386
}
387

388
int botan_x509_cert_get_issuer_dn_count(botan_x509_cert_t cert, const char* key, size_t* count) {
2✔
389
#if defined(BOTAN_HAS_X509_CERTIFICATES)
390
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
4✔
391
      if(Botan::any_null_pointers(count)) {
392
         return BOTAN_FFI_ERROR_NULL_POINTER;
393
      }
394

395
      *count = c.issuer_info(key).size();
396
      return BOTAN_FFI_SUCCESS;
397
   });
398
#else
399
   BOTAN_UNUSED(cert, key, count);
400
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
401
#endif
402
}
403

404
int botan_x509_cert_get_subject_dn(
8✔
405
   botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) {
406
#if defined(BOTAN_HAS_X509_CERTIFICATES)
407
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
16✔
408
      auto subject_info = c.subject_info(key);
409
      if(index < subject_info.size()) {
410
         // TODO(Botan4) change the type of out and remove this cast
411
         return write_str_output(reinterpret_cast<char*>(out), out_len, c.subject_info(key).at(index));
412
      } else {
413
         return BOTAN_FFI_ERROR_BAD_PARAMETER;  // TODO(Botan4): use BOTAN_FFI_ERROR_OUT_OF_RANGE
414
      }
415
   });
416
#else
417
   BOTAN_UNUSED(cert, key, index, out, out_len);
418
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
419
#endif
420
}
421

422
int botan_x509_cert_get_subject_dn_count(botan_x509_cert_t cert, const char* key, size_t* count) {
2✔
423
#if defined(BOTAN_HAS_X509_CERTIFICATES)
424
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
4✔
425
      if(Botan::any_null_pointers(count)) {
426
         return BOTAN_FFI_ERROR_NULL_POINTER;
427
      }
428

429
      *count = c.subject_info(key).size();
430
      return BOTAN_FFI_SUCCESS;
431
   });
432
#else
433
   BOTAN_UNUSED(cert, key, count);
434
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
435
#endif
436
}
437

438
int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len) {
2✔
439
   return copy_view_str(reinterpret_cast<uint8_t*>(out), out_len, botan_x509_cert_view_as_string, cert);
2✔
440
}
441

442
int botan_x509_cert_view_as_string(botan_x509_cert_t cert, botan_view_ctx ctx, botan_view_str_fn view) {
3✔
443
#if defined(BOTAN_HAS_X509_CERTIFICATES)
444
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return invoke_view_callback(view, ctx, c.to_string()); });
9✔
445
#else
446
   BOTAN_UNUSED(cert, ctx, view);
447
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
448
#endif
449
}
450

451
int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage) {
7✔
452
#if defined(BOTAN_HAS_X509_CERTIFICATES)
453
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
14✔
454
      const Botan::Key_Constraints k = static_cast<Botan::Key_Constraints>(key_usage);
455
      if(c.allowed_usage(k)) {
456
         return BOTAN_FFI_SUCCESS;
457
      }
458
      return 1;
459
   });
460
#else
461
   BOTAN_UNUSED(cert, key_usage);
462
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
463
#endif
464
}
465

466
int botan_x509_cert_allowed_extended_usage_str(botan_x509_cert_t cert, const char* oid) {
12✔
467
#if defined(BOTAN_HAS_X509_CERTIFICATES)
468
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int {
32✔
469
      if(Botan::any_null_pointers(oid)) {
470
         return BOTAN_FFI_ERROR_NULL_POINTER;
471
      }
472

473
      return c.has_ex_constraint(oid) ? BOTAN_FFI_SUCCESS : 1;
474
   });
475
#else
476
   BOTAN_UNUSED(cert, oid);
477
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
478
#endif
479
}
480

481
int botan_x509_cert_allowed_extended_usage_oid(botan_x509_cert_t cert, botan_asn1_oid_t oid) {
4✔
482
#if defined(BOTAN_HAS_X509_CERTIFICATES)
483
   return BOTAN_FFI_VISIT(
8✔
484
      cert, [=](const auto& c) -> int { return c.has_ex_constraint(safe_get(oid)) ? BOTAN_FFI_SUCCESS : 1; });
485
#else
486
   BOTAN_UNUSED(cert, oid);
487
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
488
#endif
489
}
490

491
int botan_x509_cert_destroy(botan_x509_cert_t cert) {
32✔
492
#if defined(BOTAN_HAS_X509_CERTIFICATES)
493
   return BOTAN_FFI_CHECKED_DELETE(cert);
32✔
494
#else
495
   BOTAN_UNUSED(cert);
496
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
497
#endif
498
}
499

500
int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len) {
3✔
501
#if defined(BOTAN_HAS_X509_CERTIFICATES)
502
   return BOTAN_FFI_VISIT(cert,
6✔
503
                          [=](const auto& c) { return write_str_output(out, out_len, c.not_before().to_string()); });
504
#else
505
   BOTAN_UNUSED(cert, out, out_len);
506
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
507
#endif
508
}
509

510
int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len) {
2✔
511
#if defined(BOTAN_HAS_X509_CERTIFICATES)
512
   return BOTAN_FFI_VISIT(cert,
4✔
513
                          [=](const auto& c) { return write_str_output(out, out_len, c.not_after().to_string()); });
514
#else
515
   BOTAN_UNUSED(cert, out, out_len);
516
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
517
#endif
518
}
519

520
int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch) {
2✔
521
#if defined(BOTAN_HAS_X509_CERTIFICATES)
522
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_before().time_since_epoch(); });
4✔
523
#else
524
   BOTAN_UNUSED(cert, time_since_epoch);
525
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
526
#endif
527
}
528

529
int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch) {
2✔
530
#if defined(BOTAN_HAS_X509_CERTIFICATES)
531
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_after().time_since_epoch(); });
4✔
532
#else
533
   BOTAN_UNUSED(cert, time_since_epoch);
534
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
535
#endif
536
}
537

538
int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
5✔
539
#if defined(BOTAN_HAS_X509_CERTIFICATES)
540
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.serial_number()); });
10✔
541
#else
542
   BOTAN_UNUSED(cert, out, out_len);
543
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
544
#endif
545
}
546

547
int botan_x509_cert_serial_number(botan_x509_cert_t cert, botan_mp_t* serial_number) {
1✔
548
#if defined(BOTAN_HAS_X509_CERTIFICATES)
549
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
2✔
550
      if(Botan::any_null_pointers(serial_number)) {
551
         return BOTAN_FFI_ERROR_NULL_POINTER;
552
      }
553

554
      auto serial_bn = Botan::BigInt::from_bytes(c.serial_number());
555
      return ffi_new_object(serial_number, std::make_unique<Botan::BigInt>(std::move(serial_bn)));
556
   });
557
#else
558
   BOTAN_UNUSED(cert, serial_number);
559
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
560
#endif
561
}
562

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

567
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) {
6✔
568
      return write_str_output(reinterpret_cast<char*>(out), out_len, c.fingerprint(hash));
569
   });
570
#else
571
   BOTAN_UNUSED(cert, hash, out, out_len);
572
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
573
#endif
574
}
575

576
int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
1✔
577
#if defined(BOTAN_HAS_X509_CERTIFICATES)
578
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.authority_key_id()); });
2✔
579
#else
580
   BOTAN_UNUSED(cert, out, out_len);
581
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
582
#endif
583
}
584

585
int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
3✔
586
#if defined(BOTAN_HAS_X509_CERTIFICATES)
587
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_vec_output(out, out_len, c.subject_key_id()); });
6✔
588
#else
589
   BOTAN_UNUSED(cert, out, out_len);
590
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
591
#endif
592
}
593

594
int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) {
2✔
595
   return copy_view_bin(out, out_len, botan_x509_cert_view_public_key_bits, cert);
2✔
596
}
597

598
int botan_x509_cert_view_public_key_bits(botan_x509_cert_t cert, botan_view_ctx ctx, botan_view_bin_fn view) {
3✔
599
#if defined(BOTAN_HAS_X509_CERTIFICATES)
600
   return BOTAN_FFI_VISIT(cert,
6✔
601
                          [=](const auto& c) { return invoke_view_callback(view, ctx, c.subject_public_key_bits()); });
602
#else
603
   BOTAN_UNUSED(cert, ctx, view);
604
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
605
#endif
606
}
607
}
608

609
namespace {
610

611
std::optional<botan_x509_general_name_types> to_botan_x509_general_name_types(Botan::GeneralName::NameType gn_type) {
301✔
612
   using Type = Botan::GeneralName::NameType;
301✔
613
   switch(gn_type) {
301✔
614
      case Type::Unknown:
1✔
615
         return std::nullopt;
1✔
616
      case Type::RFC822:
83✔
617
         return BOTAN_X509_EMAIL_ADDRESS;
83✔
618
      case Type::DNS:
73✔
619
         return BOTAN_X509_DNS_NAME;
73✔
620
      case Type::URI:
28✔
621
         return BOTAN_X509_URI;
28✔
622
      case Type::DN:
95✔
623
         return BOTAN_X509_DIRECTORY_NAME;
95✔
624
      case Type::IPv4:
21✔
625
         return BOTAN_X509_IP_ADDRESS;
21✔
626
      case Type::Other:
×
627
         return BOTAN_X509_OTHER_NAME;
×
628
   }
629

630
   BOTAN_ASSERT_UNREACHABLE();
×
631
}
632

633
}  // namespace
634

635
extern "C" {
636

637
int botan_x509_general_name_get_type(botan_x509_general_name_t name, unsigned int* type) {
198✔
638
#if defined(BOTAN_HAS_X509_CERTIFICATES)
639
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) {
396✔
640
      if(Botan::any_null_pointers(type)) {
641
         return BOTAN_FFI_ERROR_NULL_POINTER;
642
      }
643

644
      const auto mapped_type = to_botan_x509_general_name_types(n.type_code());
645
      if(!mapped_type.has_value()) {
646
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
647
      }
648

649
      *type = mapped_type.value();
650
      if(*type == BOTAN_X509_OTHER_NAME /* ... viewing of other-names not supported */) {
651
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
652
      }
653

654
      return BOTAN_FFI_SUCCESS;
655
   });
656
#else
657
   BOTAN_UNUSED(name, type);
658
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
659
#endif
660
}
661

662
int botan_x509_general_name_view_string_value(botan_x509_general_name_t name,
66✔
663
                                              botan_view_ctx ctx,
664
                                              botan_view_str_fn view) {
665
#if defined(BOTAN_HAS_X509_CERTIFICATES)
666
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int {
197✔
667
      const auto type = to_botan_x509_general_name_types(n.type_code());
668
      if(!type) {
669
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
670
      }
671

672
      if(type != BOTAN_X509_EMAIL_ADDRESS && type != BOTAN_X509_DNS_NAME && type != BOTAN_X509_URI &&
673
         type != BOTAN_X509_IP_ADDRESS) {
674
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
675
      }
676

677
      return invoke_view_callback(view, ctx, n.name());
678
   });
679
#else
680
   BOTAN_UNUSED(name, ctx, view);
681
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
682
#endif
683
}
684

685
int botan_x509_general_name_view_binary_value(botan_x509_general_name_t name,
37✔
686
                                              botan_view_ctx ctx,
687
                                              botan_view_bin_fn view) {
688
#if defined(BOTAN_HAS_X509_CERTIFICATES)
689
   return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int {
144✔
690
      const auto type = to_botan_x509_general_name_types(n.type_code());
691
      if(!type) {
692
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
693
      }
694

695
      if(type != BOTAN_X509_DIRECTORY_NAME && type != BOTAN_X509_IP_ADDRESS) {
696
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
697
      }
698

699
      return invoke_view_callback(view, ctx, n.binary_name());
700
   });
701
#else
702
   BOTAN_UNUSED(name, ctx, view);
703
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
704
#endif
705
}
706

707
int botan_x509_general_name_destroy(botan_x509_general_name_t name) {
198✔
708
#if defined(BOTAN_HAS_X509_CERTIFICATES)
709
   return BOTAN_FFI_CHECKED_DELETE(name);
198✔
710
#else
711
   BOTAN_UNUSED(name);
712
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
713
#endif
714
}
715

716
int botan_x509_cert_permitted_name_constraints(botan_x509_cert_t cert,
76✔
717
                                               size_t index,
718
                                               botan_x509_general_name_t* constraint) {
719
#if defined(BOTAN_HAS_X509_CERTIFICATES)
720
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
152✔
721
      if(Botan::any_null_pointers(constraint)) {
722
         return BOTAN_FFI_ERROR_NULL_POINTER;
723
      }
724

725
      const auto& constraints = c.name_constraints().permitted();
726
      if(index >= constraints.size()) {
727
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
728
      }
729

730
      return ffi_new_object(constraint, std::make_unique<Botan::GeneralName>(constraints[index].base()));
731
   });
732
#else
733
   BOTAN_UNUSED(cert, index, constraint);
734
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
735
#endif
736
}
737

738
int botan_x509_cert_permitted_name_constraints_count(botan_x509_cert_t cert, size_t* count) {
1✔
739
#if defined(BOTAN_HAS_X509_CERTIFICATES)
740
   if(Botan::any_null_pointers(count)) {
1✔
741
      return BOTAN_FFI_ERROR_NULL_POINTER;
742
   }
743

744
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *count = c.name_constraints().permitted().size(); });
2✔
745
#else
746
   BOTAN_UNUSED(cert, count);
747
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
748
#endif
749
}
750

751
int botan_x509_cert_excluded_name_constraints(botan_x509_cert_t cert,
4✔
752
                                              size_t index,
753
                                              botan_x509_general_name_t* constraint) {
754
#if defined(BOTAN_HAS_X509_CERTIFICATES)
755
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
8✔
756
      if(Botan::any_null_pointers(constraint)) {
757
         return BOTAN_FFI_ERROR_NULL_POINTER;
758
      }
759

760
      const auto& constraints = c.name_constraints().excluded();
761
      if(index >= constraints.size()) {
762
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
763
      }
764

765
      return ffi_new_object(constraint, std::make_unique<Botan::GeneralName>(constraints[index].base()));
766
   });
767
#else
768
   BOTAN_UNUSED(cert, index, constraint);
769
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
770
#endif
771
}
772

773
int botan_x509_cert_excluded_name_constraints_count(botan_x509_cert_t cert, size_t* count) {
1✔
774
#if defined(BOTAN_HAS_X509_CERTIFICATES)
775
   if(Botan::any_null_pointers(count)) {
1✔
776
      return BOTAN_FFI_ERROR_NULL_POINTER;
777
   }
778

779
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *count = c.name_constraints().excluded().size(); });
2✔
780
#else
781
   BOTAN_UNUSED(cert, count);
782
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
783
#endif
784
}
785
}
786

787
namespace {
788

789
/**
790
 * As specified in RFC 5280 Section 4.2.1.6. alternative names essentially are a
791
 * collection of GeneralNames. This allows mapping a single entry of @p altnames
792
 * to a GeneralName by its @p index. If the index is out of range, std::nullopt
793
 * is returned.
794
 *
795
 * NOTE: if the set of alternative name types handled here is extended,
796
 *       count_general_names_in() must be updated accordingly!
797
 */
798
std::optional<Botan::GeneralName> extract_general_name_at(const Botan::AlternativeName& altnames, size_t index) {
132✔
799
   if(index < altnames.email().size()) {
132✔
800
      auto itr = altnames.email().begin();
12✔
801
      std::advance(itr, index);
12✔
802
      return Botan::GeneralName::email(*itr);
36✔
803
   }
804
   index -= altnames.email().size();
120✔
805

806
   if(index < altnames.dns().size()) {
120✔
807
      auto itr = altnames.dns().begin();
36✔
808
      std::advance(itr, index);
36✔
809
      return Botan::GeneralName::dns(*itr);
108✔
810
   }
811
   index -= altnames.dns().size();
84✔
812

813
   if(index < altnames.directory_names().size()) {
84✔
814
      auto itr = altnames.directory_names().begin();
36✔
815
      std::advance(itr, index);
36✔
816
      return Botan::GeneralName::directory_name(*itr);
108✔
817
   }
818
   index -= altnames.directory_names().size();
48✔
819

820
   if(index < altnames.uris().size()) {
48✔
821
      auto itr = altnames.uris().begin();
24✔
822
      std::advance(itr, index);
24✔
823
      return Botan::GeneralName::uri(*itr);
72✔
824
   }
825
   index -= altnames.uris().size();
24✔
826

827
   if(index < altnames.ipv4_address().size()) {
24✔
828
      auto itr = altnames.ipv4_address().begin();
12✔
829
      std::advance(itr, index);
12✔
830
      return Botan::GeneralName::ipv4_address(*itr);
36✔
831
   }
832

833
   return std::nullopt;
12✔
834
}
835

836
/**
837
 * Counts the total number of GeneralNames contained in the given
838
 * AlternativeName @p alt_names.
839
 *
840
 * NOTE: if the set of alternative name types handled here is extended,
841
 *       extract_general_name_at() must be updated accordingly!
842
 */
843
size_t count_general_names_in(const Botan::AlternativeName& alt_names) {
12✔
844
   return alt_names.email().size() + alt_names.dns().size() + alt_names.directory_names().size() +
12✔
845
          alt_names.uris().size() + alt_names.ipv4_address().size();
12✔
846
}
847

848
}  // namespace
849

850
extern "C" {
851

852
int botan_x509_cert_subject_alternative_names(botan_x509_cert_t cert,
73✔
853
                                              size_t index,
854
                                              botan_x509_general_name_t* alt_name) {
855
#if defined(BOTAN_HAS_X509_CERTIFICATES)
856
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
152✔
857
      if(Botan::any_null_pointers(alt_name)) {
858
         return BOTAN_FFI_ERROR_NULL_POINTER;
859
      }
860

861
      if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.SubjectAlternativeName"))) {
862
         return BOTAN_FFI_ERROR_NO_VALUE;
863
      }
864

865
      if(auto name = extract_general_name_at(c.subject_alt_name(), index)) {
866
         return ffi_new_object(alt_name, std::make_unique<Botan::GeneralName>(std::move(name).value()));
867
      }
868

869
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
870
   });
871
#else
872
   BOTAN_UNUSED(cert, index, alt_name);
873
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
874
#endif
875
}
876

877
int botan_x509_cert_subject_alternative_names_count(botan_x509_cert_t cert, size_t* count) {
6✔
878
#if defined(BOTAN_HAS_X509_CERTIFICATES)
879
   if(Botan::any_null_pointers(count)) {
6✔
880
      return BOTAN_FFI_ERROR_NULL_POINTER;
881
   }
882

883
   return BOTAN_FFI_VISIT(
12✔
884
      cert, [=](const Botan::X509_Certificate& c) { *count = count_general_names_in(c.subject_alt_name()); });
885
#else
886
   BOTAN_UNUSED(cert, count);
887
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
888
#endif
889
}
890

891
int botan_x509_cert_issuer_alternative_names(botan_x509_cert_t cert,
61✔
892
                                             size_t index,
893
                                             botan_x509_general_name_t* alt_name) {
894
#if defined(BOTAN_HAS_X509_CERTIFICATES)
895
   return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) {
128✔
896
      if(Botan::any_null_pointers(alt_name)) {
897
         return BOTAN_FFI_ERROR_NULL_POINTER;
898
      }
899

900
      if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.IssuerAlternativeName"))) {
901
         return BOTAN_FFI_ERROR_NO_VALUE;
902
      }
903

904
      if(auto name = extract_general_name_at(c.issuer_alt_name(), index)) {
905
         return ffi_new_object(alt_name, std::make_unique<Botan::GeneralName>(std::move(name).value()));
906
      }
907

908
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
909
   });
910
#else
911
   BOTAN_UNUSED(cert, index, alt_name);
912
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
913
#endif
914
}
915

916
int botan_x509_cert_issuer_alternative_names_count(botan_x509_cert_t cert, size_t* count) {
6✔
917
#if defined(BOTAN_HAS_X509_CERTIFICATES)
918
   if(Botan::any_null_pointers(count)) {
6✔
919
      return BOTAN_FFI_ERROR_NULL_POINTER;
920
   }
921

922
   return BOTAN_FFI_VISIT(
12✔
923
      cert, [=](const Botan::X509_Certificate& c) { *count = count_general_names_in(c.issuer_alt_name()); });
924
#else
925
   BOTAN_UNUSED(cert, count);
926
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
927
#endif
928
}
929

930
int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname) {
6✔
931
   if(hostname == nullptr) {
6✔
932
      return BOTAN_FFI_ERROR_NULL_POINTER;
933
   }
934

935
#if defined(BOTAN_HAS_X509_CERTIFICATES)
936
   return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return c.matches_dns_name(hostname) ? 0 : -1; });
12✔
937
#else
938
   BOTAN_UNUSED(cert);
939
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
940
#endif
941
}
942

943
int botan_x509_cert_verify(int* result_code,
4✔
944
                           botan_x509_cert_t cert,
945
                           const botan_x509_cert_t* intermediates,
946
                           size_t intermediates_len,
947
                           const botan_x509_cert_t* trusted,
948
                           size_t trusted_len,
949
                           const char* trusted_path,
950
                           size_t required_strength,
951
                           const char* hostname_cstr,
952
                           uint64_t reference_time) {
953
   if(required_strength == 0) {
4✔
954
      required_strength = 110;
3✔
955
   }
956

957
#if defined(BOTAN_HAS_X509_CERTIFICATES)
958
   return ffi_guard_thunk(__func__, [=]() -> int {
4✔
959
      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
4✔
960
      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
4✔
961
      const auto validation_time = reference_time == 0
4✔
962
                                      ? std::chrono::system_clock::now()
4✔
963
                                      : std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
×
964

965
      std::vector<Botan::X509_Certificate> end_certs;
4✔
966
      end_certs.push_back(safe_get(cert));
4✔
967
      for(size_t i = 0; i != intermediates_len; ++i) {
9✔
968
         end_certs.push_back(safe_get(intermediates[i]));
5✔
969
      }
970

971
      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
4✔
972
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
4✔
973
      std::vector<Botan::Certificate_Store*> trusted_roots;
4✔
974

975
      if(trusted_path != nullptr && *trusted_path != 0) {
4✔
976
         trusted_from_path = std::make_unique<Botan::Certificate_Store_In_Memory>(trusted_path);
×
977
         trusted_roots.push_back(trusted_from_path.get());
×
978
      }
979

980
      if(trusted_len > 0) {
4✔
981
         trusted_extra = std::make_unique<Botan::Certificate_Store_In_Memory>();
8✔
982
         for(size_t i = 0; i != trusted_len; ++i) {
8✔
983
            trusted_extra->add_certificate(safe_get(trusted[i]));
4✔
984
         }
985
         trusted_roots.push_back(trusted_extra.get());
4✔
986
      }
987

988
      const Botan::Path_Validation_Restrictions restrictions(false, required_strength);
8✔
989

990
      auto validation_result =
4✔
991
         Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time);
4✔
992

993
      if(result_code != nullptr) {
4✔
994
         *result_code = static_cast<int>(validation_result.result());
4✔
995
      }
996

997
      if(validation_result.successful_validation()) {
4✔
998
         return 0;
999
      } else {
1000
         return 1;
3✔
1001
      }
1002
   });
4✔
1003
#else
1004
   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
1005
   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time);
1006
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1007
#endif
1008
}
1009

1010
const char* botan_x509_cert_validation_status(int code) {
11✔
1011
   if(code < 0) {
11✔
1012
      return nullptr;
1013
   }
1014

1015
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1016
   const Botan::Certificate_Status_Code sc = static_cast<Botan::Certificate_Status_Code>(code);
11✔
1017
   return Botan::to_string(sc);
11✔
1018
#else
1019
   return nullptr;
1020
#endif
1021
}
1022

1023
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1024

1025
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910);
8✔
1026
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_entry_struct, Botan::CRL_Entry, 0x4EAA5346);
1✔
1027

1028
#endif
1029

1030
int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path) {
7✔
1031
   if(crl_obj == nullptr || crl_path == nullptr) {
7✔
1032
      return BOTAN_FFI_ERROR_NULL_POINTER;
1033
   }
1034

1035
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1036

1037
   return ffi_guard_thunk(__func__, [=]() -> int {
7✔
1038
      auto c = std::make_unique<Botan::X509_CRL>(crl_path);
7✔
1039
      return ffi_new_object(crl_obj, std::move(c));
14✔
1040
   });
14✔
1041

1042
#else
1043
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1044
#endif
1045
}
1046

1047
int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len) {
1✔
1048
   if(crl_obj == nullptr || crl_bits == nullptr) {
1✔
1049
      return BOTAN_FFI_ERROR_NULL_POINTER;
1050
   }
1051

1052
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1053
   return ffi_guard_thunk(__func__, [=]() -> int {
1✔
1054
      Botan::DataSource_Memory bits(crl_bits, crl_bits_len);
1✔
1055
      auto c = std::make_unique<Botan::X509_CRL>(bits);
1✔
1056
      return ffi_new_object(crl_obj, std::move(c));
1✔
1057
   });
3✔
1058
#else
1059
   BOTAN_UNUSED(crl_bits_len);
1060
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1061
#endif
1062
}
1063

1064
int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) {
2✔
1065
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1066
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) {
4✔
1067
      if(Botan::any_null_pointers(time_since_epoch)) {
1068
         return BOTAN_FFI_ERROR_NULL_POINTER;
1069
      }
1070
      *time_since_epoch = c.this_update().time_since_epoch();
1071
      return BOTAN_FFI_SUCCESS;
1072
   });
1073
#else
1074
   BOTAN_UNUSED(crl, time_since_epoch);
1075
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1076
#endif
1077
}
1078

1079
int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) {
4✔
1080
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1081
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) {
8✔
1082
      const auto& time = c.next_update();
1083
      if(!time.time_is_set()) {
1084
         return BOTAN_FFI_ERROR_NO_VALUE;
1085
      }
1086

1087
      if(Botan::any_null_pointers(time_since_epoch)) {
1088
         return BOTAN_FFI_ERROR_NULL_POINTER;
1089
      }
1090

1091
      *time_since_epoch = c.next_update().time_since_epoch();
1092
      return BOTAN_FFI_SUCCESS;
1093
   });
1094
#else
1095
   BOTAN_UNUSED(crl, time_since_epoch);
1096
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1097
#endif
1098
}
1099

1100
int botan_x509_crl_destroy(botan_x509_crl_t crl) {
8✔
1101
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1102
   return BOTAN_FFI_CHECKED_DELETE(crl);
8✔
1103
#else
1104
   BOTAN_UNUSED(crl);
1105
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1106
#endif
1107
}
1108

1109
int botan_x509_crl_view_binary_values(botan_x509_crl_t crl_obj,
3✔
1110
                                      botan_x509_value_type value_type,
1111
                                      size_t index,
1112
                                      botan_view_ctx ctx,
1113
                                      botan_view_bin_fn view_fn) {
1114
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1115
   if(index != 0) {
3✔
1116
      // As of now there are no multi-value binary entries.
1117
      return BOTAN_FFI_ERROR_OUT_OF_RANGE;
1118
   }
1119

1120
   auto view = [=](std::span<const uint8_t> value) -> int {
4✔
1121
      if(value.empty()) {
1✔
1122
         return BOTAN_FFI_ERROR_NO_VALUE;
1123
      } else {
1124
         return invoke_view_callback(view_fn, ctx, value);
1✔
1125
      }
1126
   };
3✔
1127

1128
   return BOTAN_FFI_VISIT(crl_obj, [=](const Botan::X509_CRL& crl) -> int {
12✔
1129
      switch(value_type) {
1130
         case BOTAN_X509_SERIAL_NUMBER:
1131
            return view(Botan::store_be(crl.crl_number()));
1132
         case BOTAN_X509_ISSUER_DN_BITS:
1133
            return view(Botan::ASN1::put_in_sequence(crl.issuer_dn().get_bits()));
1134
         case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER:
1135
            return view(crl.authority_key_id());
1136

1137
         case BOTAN_X509_TBS_DATA_BITS:
1138
         case BOTAN_X509_SIGNATURE_SCHEME_BITS:
1139
         case BOTAN_X509_SIGNATURE_BITS:
1140
         case BOTAN_X509_DER_ENCODING:
1141
            return botan_x509_object_view_value(crl, value_type, index, ctx, view_fn);
1142

1143
         case BOTAN_X509_SUBJECT_DN_BITS:
1144
         case BOTAN_X509_SUBJECT_KEY_IDENTIFIER:
1145
         case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS:
1146
         case BOTAN_X509_PEM_ENCODING:
1147
         case BOTAN_X509_CRL_DISTRIBUTION_URLS:
1148
         case BOTAN_X509_OCSP_RESPONDER_URLS:
1149
         case BOTAN_X509_CA_ISSUERS_URLS:
1150
            return BOTAN_FFI_ERROR_NO_VALUE;
1151
      }
1152

1153
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
1154
   });
1155
#else
1156
   BOTAN_UNUSED(crl_obj, value_type, index, ctx, view_fn);
1157
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1158
#endif
1159
}
1160

1161
int botan_x509_crl_view_binary_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count) {
1✔
1162
   return enumerator_count_values(count, [=](size_t index) {
1✔
1163
      return botan_x509_crl_view_binary_values(
1✔
1164
         crl_obj, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; });
1165
   });
1✔
1166
}
1167

1168
int botan_x509_crl_view_string_values(botan_x509_crl_t crl_obj,
3✔
1169
                                      botan_x509_value_type value_type,
1170
                                      size_t index,
1171
                                      botan_view_ctx ctx,
1172
                                      botan_view_str_fn view) {
1173
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1174
   return BOTAN_FFI_VISIT(crl_obj, [=](const Botan::X509_CRL& crl) -> int {
6✔
1175
      switch(value_type) {
1176
         case BOTAN_X509_PEM_ENCODING:
1177
            return botan_x509_object_view_value(crl, value_type, index, ctx, view);
1178

1179
         case BOTAN_X509_SERIAL_NUMBER:
1180
         case BOTAN_X509_SUBJECT_DN_BITS:
1181
         case BOTAN_X509_ISSUER_DN_BITS:
1182
         case BOTAN_X509_SUBJECT_KEY_IDENTIFIER:
1183
         case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER:
1184
         case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS:
1185
         case BOTAN_X509_TBS_DATA_BITS:
1186
         case BOTAN_X509_SIGNATURE_SCHEME_BITS:
1187
         case BOTAN_X509_SIGNATURE_BITS:
1188
         case BOTAN_X509_DER_ENCODING:
1189
         case BOTAN_X509_CRL_DISTRIBUTION_URLS:
1190
         case BOTAN_X509_OCSP_RESPONDER_URLS:
1191
         case BOTAN_X509_CA_ISSUERS_URLS:
1192
            return BOTAN_FFI_ERROR_NO_VALUE;
1193
      }
1194

1195
      return BOTAN_FFI_ERROR_BAD_PARAMETER;
1196
   });
1197
#else
1198
   BOTAN_UNUSED(crl_obj, value_type, index, ctx, view);
1199
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1200
#endif
1201
}
1202

1203
int botan_x509_crl_view_string_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count) {
1✔
1204
   return enumerator_count_values(count, [=](size_t index) {
1✔
1205
      return botan_x509_crl_view_string_values(
2✔
1206
         crl_obj, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; });
1207
   });
1✔
1208
}
1209

1210
int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert) {
6✔
1211
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1212
   return BOTAN_FFI_VISIT(crl, [=](const auto& c) { return c.is_revoked(safe_get(cert)) ? 0 : -1; });
12✔
1213
#else
1214
   BOTAN_UNUSED(cert);
1215
   BOTAN_UNUSED(crl);
1216
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1217
#endif
1218
}
1219

1220
int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry) {
3✔
1221
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1222
   return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) -> int {
6✔
1223
      const auto& entries = c.get_revoked();
1224
      if(index >= entries.size()) {
1225
         return BOTAN_FFI_ERROR_OUT_OF_RANGE;
1226
      }
1227

1228
      if(Botan::any_null_pointers(entry)) {
1229
         return BOTAN_FFI_ERROR_NULL_POINTER;
1230
      }
1231

1232
      return ffi_new_object(entry, std::make_unique<Botan::CRL_Entry>(entries[index]));
1233
   });
1234
#else
1235
   BOTAN_UNUSED(crl, index, entry);
1236
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1237
#endif
1238
}
1239

1240
int botan_x509_crl_entries_count(botan_x509_crl_t crl, size_t* count) {
2✔
1241
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1242
   if(Botan::any_null_pointers(count)) {
2✔
1243
      return BOTAN_FFI_ERROR_NULL_POINTER;
1244
   }
1245

1246
   return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) { *count = c.get_revoked().size(); });
4✔
1247
#else
1248
   BOTAN_UNUSED(crl, count);
1249
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1250
#endif
1251
}
1252

1253
int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry) {
1✔
1254
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1255
   return BOTAN_FFI_CHECKED_DELETE(entry);
1✔
1256
#else
1257
   BOTAN_UNUSED(entry);
1258
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1259
#endif
1260
}
1261

1262
int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code) {
1✔
1263
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1264
   return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
2✔
1265
      if(Botan::any_null_pointers(reason_code)) {
1266
         return BOTAN_FFI_ERROR_NULL_POINTER;
1267
      }
1268

1269
      *reason_code = static_cast<int>(e.reason_code());
1270
      return BOTAN_FFI_SUCCESS;
1271
   });
1272
#else
1273
   BOTAN_UNUSED(entry, reason_code);
1274
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1275
#endif
1276
}
1277

1278
int botan_x509_crl_entry_serial_number(botan_x509_crl_entry_t entry, botan_mp_t* serial_number) {
1✔
1279
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1280
   return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
2✔
1281
      if(Botan::any_null_pointers(serial_number)) {
1282
         return BOTAN_FFI_ERROR_NULL_POINTER;
1283
      }
1284

1285
      auto serial_bn = Botan::BigInt::from_bytes(e.serial_number());
1286
      return ffi_new_object(serial_number, std::make_unique<Botan::BigInt>(std::move(serial_bn)));
1287
   });
1288
#else
1289
   BOTAN_UNUSED(entry, serial_number);
1290
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1291
#endif
1292
}
1293

1294
int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view) {
1✔
1295
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1296
   return BOTAN_FFI_VISIT(
2✔
1297
      entry, [=](const Botan::CRL_Entry& e) { return invoke_view_callback(view, ctx, e.serial_number()); });
1298
#else
1299
   BOTAN_UNUSED(entry, ctx, view);
1300
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1301
#endif
1302
}
1303

1304
int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch) {
1✔
1305
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1306
   return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
2✔
1307
      if(Botan::any_null_pointers(time_since_epoch)) {
1308
         return BOTAN_FFI_ERROR_NULL_POINTER;
1309
      }
1310

1311
      *time_since_epoch = e.expire_time().time_since_epoch();
1312
      return BOTAN_FFI_SUCCESS;
1313
   });
1314
#else
1315
   BOTAN_UNUSED(entry, time_since_epoch);
1316
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1317
#endif
1318
}
1319

1320
int botan_x509_cert_verify_with_crl(int* result_code,
12✔
1321
                                    botan_x509_cert_t cert,
1322
                                    const botan_x509_cert_t* intermediates,
1323
                                    size_t intermediates_len,
1324
                                    const botan_x509_cert_t* trusted,
1325
                                    size_t trusted_len,
1326
                                    const botan_x509_crl_t* crls,
1327
                                    size_t crls_len,
1328
                                    const char* trusted_path,
1329
                                    size_t required_strength,
1330
                                    const char* hostname_cstr,
1331
                                    uint64_t reference_time) {
1332
   if(required_strength == 0) {
12✔
1333
      required_strength = 110;
2✔
1334
   }
1335

1336
#if defined(BOTAN_HAS_X509_CERTIFICATES)
1337
   return ffi_guard_thunk(__func__, [=]() -> int {
12✔
1338
      const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
14✔
1339
      const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
12✔
1340
      const auto validation_time = reference_time == 0
12✔
1341
                                      ? std::chrono::system_clock::now()
12✔
1342
                                      : std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
1✔
1343

1344
      std::vector<Botan::X509_Certificate> end_certs;
12✔
1345
      end_certs.push_back(safe_get(cert));
12✔
1346
      for(size_t i = 0; i != intermediates_len; ++i) {
30✔
1347
         end_certs.push_back(safe_get(intermediates[i]));
18✔
1348
      }
1349

1350
      std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
12✔
1351
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
12✔
1352
      std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
12✔
1353
      std::vector<Botan::Certificate_Store*> trusted_roots;
12✔
1354

1355
      if(trusted_path != nullptr && *trusted_path != 0) {
12✔
1356
         trusted_from_path = std::make_unique<Botan::Certificate_Store_In_Memory>(trusted_path);
2✔
1357
         trusted_roots.push_back(trusted_from_path.get());
2✔
1358
      }
1359

1360
      if(trusted_len > 0) {
12✔
1361
         trusted_extra = std::make_unique<Botan::Certificate_Store_In_Memory>();
18✔
1362
         for(size_t i = 0; i != trusted_len; ++i) {
18✔
1363
            trusted_extra->add_certificate(safe_get(trusted[i]));
9✔
1364
         }
1365
         trusted_roots.push_back(trusted_extra.get());
9✔
1366
      }
1367

1368
      if(crls_len > 0) {
12✔
1369
         trusted_crls = std::make_unique<Botan::Certificate_Store_In_Memory>();
10✔
1370
         for(size_t i = 0; i != crls_len; ++i) {
13✔
1371
            trusted_crls->add_crl(safe_get(crls[i]));
8✔
1372
         }
1373
         trusted_roots.push_back(trusted_crls.get());
5✔
1374
      }
1375

1376
      const Botan::Path_Validation_Restrictions restrictions(false, required_strength);
24✔
1377

1378
      auto validation_result =
12✔
1379
         Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time);
12✔
1380

1381
      if(result_code != nullptr) {
12✔
1382
         *result_code = static_cast<int>(validation_result.result());
12✔
1383
      }
1384

1385
      if(validation_result.successful_validation()) {
12✔
1386
         return 0;
1387
      } else {
1388
         return 1;
8✔
1389
      }
1390
   });
14✔
1391
#else
1392
   BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
1393
   BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time, crls, crls_len);
1394
   return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
1395
#endif
1396
}
1397
}
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