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

randombit / botan / 13481121531

23 Feb 2025 08:06AM UTC coverage: 91.696% (+0.01%) from 91.686%
13481121531

push

github

web-flow
Merge pull request #4704 from randombit/jack/ffi-tidy

FFI cleanups

95828 of 104506 relevant lines covered (91.7%)

11192831.52 hits per line

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

80.46
/src/lib/ffi/ffi.cpp
1
/*
2
* (C) 2015,2017 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/base64.h>
10
#include <botan/hex.h>
11
#include <botan/mem_ops.h>
12
#include <botan/version.h>
13
#include <botan/internal/ct_utils.h>
14
#include <botan/internal/ffi_util.h>
15
#include <cstdio>
16
#include <cstdlib>
17

18
#if defined(BOTAN_HAS_OS_UTILS)
19
   #include <botan/internal/os_utils.h>
20
#endif
21

22
namespace Botan_FFI {
23

24
namespace {
25

26
// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
27
thread_local std::string g_last_exception_what;
96,243✔
28

29
}  // namespace
30

31
int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc) {
15✔
32
   g_last_exception_what.assign(exn);
15✔
33

34
#if defined(BOTAN_HAS_OS_UTILS)
35
   std::string val;
15✔
36
   if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") == true && !val.empty()) {
15✔
37
      static_cast<void>(std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc));
×
38
   }
39
#endif
40

41
   return rc;
15✔
42
}
15✔
43

44
int botan_view_str_bounce_fn(botan_view_ctx vctx, const char* str, size_t len) {
68✔
45
   return botan_view_bin_bounce_fn(vctx, reinterpret_cast<const uint8_t*>(str), len);
68✔
46
}
47

48
int botan_view_bin_bounce_fn(botan_view_ctx vctx, const uint8_t* buf, size_t len) {
155✔
49
   if(vctx == nullptr || buf == nullptr) {
155✔
50
      return BOTAN_FFI_ERROR_NULL_POINTER;
51
   }
52

53
   botan_view_bounce_struct* ctx = static_cast<botan_view_bounce_struct*>(vctx);
155✔
54

55
   const size_t avail = *ctx->out_len;
155✔
56
   *ctx->out_len = len;
155✔
57

58
   if(avail < len || ctx->out_ptr == nullptr) {
155✔
59
      if(ctx->out_ptr) {
72✔
60
         Botan::clear_mem(ctx->out_ptr, avail);
×
61
      }
62
      return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
72✔
63
   } else {
64
      Botan::copy_mem(ctx->out_ptr, buf, len);
83✔
65
      return BOTAN_FFI_SUCCESS;
83✔
66
   }
67
}
68

69
namespace {
70

71
int ffi_map_error_type(Botan::ErrorType err) {
9✔
72
   switch(err) {
9✔
73
      case Botan::ErrorType::Unknown:
74
         return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
75

76
      case Botan::ErrorType::SystemError:
×
77
      case Botan::ErrorType::IoError:
×
78
      case Botan::ErrorType::Pkcs11Error:
×
79
      case Botan::ErrorType::CommonCryptoError:
×
80
      case Botan::ErrorType::ZlibError:
×
81
      case Botan::ErrorType::Bzip2Error:
×
82
      case Botan::ErrorType::LzmaError:
×
83
      case Botan::ErrorType::DatabaseError:
×
84
         return BOTAN_FFI_ERROR_SYSTEM_ERROR;
×
85

86
      case Botan::ErrorType::TPMError:
×
87
         return BOTAN_FFI_ERROR_TPM_ERROR;
×
88

89
      case Botan::ErrorType::NotImplemented:
90
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
91
      case Botan::ErrorType::OutOfMemory:
×
92
         return BOTAN_FFI_ERROR_OUT_OF_MEMORY;
×
93
      case Botan::ErrorType::InternalError:
×
94
         return BOTAN_FFI_ERROR_INTERNAL_ERROR;
×
95
      case Botan::ErrorType::InvalidObjectState:
2✔
96
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
2✔
97
      case Botan::ErrorType::KeyNotSet:
2✔
98
         return BOTAN_FFI_ERROR_KEY_NOT_SET;
2✔
99
      case Botan::ErrorType::InvalidArgument:
2✔
100
      case Botan::ErrorType::InvalidNonceLength:
2✔
101
         return BOTAN_FFI_ERROR_BAD_PARAMETER;
2✔
102

103
      case Botan::ErrorType::EncodingFailure:
1✔
104
      case Botan::ErrorType::DecodingFailure:
1✔
105
         return BOTAN_FFI_ERROR_INVALID_INPUT;
1✔
106

107
      case Botan::ErrorType::InvalidTag:
×
108
         return BOTAN_FFI_ERROR_BAD_MAC;
×
109

110
      case Botan::ErrorType::InvalidKeyLength:
×
111
         return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
×
112
      case Botan::ErrorType::LookupError:
113
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
114

115
      case Botan::ErrorType::HttpError:
×
116
         return BOTAN_FFI_ERROR_HTTP_ERROR;
×
117
      case Botan::ErrorType::TLSError:
×
118
         return BOTAN_FFI_ERROR_TLS_ERROR;
×
119
      case Botan::ErrorType::RoughtimeError:
×
120
         return BOTAN_FFI_ERROR_ROUGHTIME_ERROR;
×
121
   }
122

123
   return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
124
}
125

126
}  // namespace
127

128
int ffi_guard_thunk(const char* func_name, const std::function<int()>& thunk) {
96,223✔
129
   g_last_exception_what.clear();
96,223✔
130

131
   try {
96,223✔
132
      return thunk();
192,446✔
133
   } catch(std::bad_alloc&) {
15✔
134
      return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY);
×
135
   } catch(Botan_FFI::FFI_Error& e) {
6✔
136
      return ffi_error_exception_thrown(func_name, e.what(), e.error_code());
6✔
137
   } catch(Botan::Exception& e) {
15✔
138
      return ffi_error_exception_thrown(func_name, e.what(), ffi_map_error_type(e.error_type()));
9✔
139
   } catch(std::exception& e) {
9✔
140
      return ffi_error_exception_thrown(func_name, e.what());
×
141
   } catch(...) {
×
142
      return ffi_error_exception_thrown(func_name, "unknown exception");
×
143
   }
×
144
}
145

146
}  // namespace Botan_FFI
147

148
extern "C" {
149

150
using namespace Botan_FFI;
151

152
const char* botan_error_last_exception_message() {
5✔
153
   return g_last_exception_what.c_str();
5✔
154
}
155

156
const char* botan_error_description(int err) {
155✔
157
   switch(err) {
155✔
158
      case BOTAN_FFI_SUCCESS:
159
         return "OK";
160

161
      case BOTAN_FFI_INVALID_VERIFIER:
1✔
162
         return "Invalid verifier";
1✔
163

164
      case BOTAN_FFI_ERROR_INVALID_INPUT:
2✔
165
         return "Invalid input";
2✔
166

167
      case BOTAN_FFI_ERROR_BAD_MAC:
1✔
168
         return "Invalid authentication code";
1✔
169

170
      case BOTAN_FFI_ERROR_NO_VALUE:
2✔
171
         return "No value available";
2✔
172

173
      case BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE:
1✔
174
         return "Insufficient buffer space";
1✔
175

176
      case BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR:
1✔
177
         return "String conversion error";
1✔
178

179
      case BOTAN_FFI_ERROR_EXCEPTION_THROWN:
1✔
180
         return "Exception thrown";
1✔
181

182
      case BOTAN_FFI_ERROR_OUT_OF_MEMORY:
1✔
183
         return "Out of memory";
1✔
184

185
      case BOTAN_FFI_ERROR_SYSTEM_ERROR:
1✔
186
         return "Error while calling system API";
1✔
187

188
      case BOTAN_FFI_ERROR_INTERNAL_ERROR:
1✔
189
         return "Internal error";
1✔
190

191
      case BOTAN_FFI_ERROR_BAD_FLAG:
1✔
192
         return "Bad flag";
1✔
193

194
      case BOTAN_FFI_ERROR_NULL_POINTER:
1✔
195
         return "Null pointer argument";
1✔
196

197
      case BOTAN_FFI_ERROR_BAD_PARAMETER:
1✔
198
         return "Bad parameter";
1✔
199

200
      case BOTAN_FFI_ERROR_KEY_NOT_SET:
1✔
201
         return "Key not set on object";
1✔
202

203
      case BOTAN_FFI_ERROR_INVALID_KEY_LENGTH:
1✔
204
         return "Invalid key length";
1✔
205

206
      case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE:
1✔
207
         return "Invalid object state";
1✔
208

209
      case BOTAN_FFI_ERROR_NOT_IMPLEMENTED:
4✔
210
         return "Not implemented";
4✔
211

212
      case BOTAN_FFI_ERROR_INVALID_OBJECT:
1✔
213
         return "Invalid object handle";
1✔
214

215
      case BOTAN_FFI_ERROR_TLS_ERROR:
1✔
216
         return "TLS error";
1✔
217

218
      case BOTAN_FFI_ERROR_HTTP_ERROR:
1✔
219
         return "HTTP error";
1✔
220

221
      case BOTAN_FFI_ERROR_UNKNOWN_ERROR:
222
         return "Unknown error";
223

224
      default:
225
         return "Unknown error";
226
   }
227
}
228

229
/*
230
* Versioning
231
*/
232
uint32_t botan_ffi_api_version() {
4✔
233
   return BOTAN_HAS_FFI;
4✔
234
}
235

236
int botan_ffi_supports_api(uint32_t api_version) {
7✔
237
   // This is the API introduced in 3.8
238
   if(api_version == 20250506) {
7✔
239
      return BOTAN_FFI_SUCCESS;
240
   }
241

242
   // This is the API introduced in 3.4
243
   if(api_version == 20240408) {
5✔
244
      return BOTAN_FFI_SUCCESS;
245
   }
246

247
   // This is the API introduced in 3.2
248
   if(api_version == 20231009) {
5✔
249
      return BOTAN_FFI_SUCCESS;
250
   }
251

252
   // This is the API introduced in 3.1
253
   if(api_version == 20230711) {
5✔
254
      return BOTAN_FFI_SUCCESS;
255
   }
256

257
   // This is the API introduced in 3.0
258
   if(api_version == 20230403) {
5✔
259
      return BOTAN_FFI_SUCCESS;
260
   }
261

262
   // This is the API introduced in 2.18
263
   if(api_version == 20210220) {
5✔
264
      return BOTAN_FFI_SUCCESS;
265
   }
266

267
   // This is the API introduced in 2.13
268
   if(api_version == 20191214) {
5✔
269
      return BOTAN_FFI_SUCCESS;
270
   }
271

272
   // This is the API introduced in 2.8
273
   if(api_version == 20180713) {
5✔
274
      return BOTAN_FFI_SUCCESS;
275
   }
276

277
   // This is the API introduced in 2.3
278
   if(api_version == 20170815) {
4✔
279
      return BOTAN_FFI_SUCCESS;
280
   }
281

282
   // This is the API introduced in 2.1
283
   if(api_version == 20170327) {
3✔
284
      return BOTAN_FFI_SUCCESS;
285
   }
286

287
   // This is the API introduced in 2.0
288
   if(api_version == 20150515) {
2✔
289
      return BOTAN_FFI_SUCCESS;
1✔
290
   }
291

292
   // Something else:
293
   return -1;
294
}
295

296
const char* botan_version_string() {
2✔
297
   return Botan::version_cstr();
2✔
298
}
299

300
uint32_t botan_version_major() {
2✔
301
   return Botan::version_major();
2✔
302
}
303

304
uint32_t botan_version_minor() {
2✔
305
   return Botan::version_minor();
2✔
306
}
307

308
uint32_t botan_version_patch() {
1✔
309
   return Botan::version_patch();
1✔
310
}
311

312
uint32_t botan_version_datestamp() {
1✔
313
   return Botan::version_datestamp();
1✔
314
}
315

316
int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len) {
28✔
317
   auto same = Botan::CT::is_equal(x, y, len);
28✔
318
   // Return 0 if same or -1 otherwise
319
   return static_cast<int>(same.select(1, 0)) - 1;
28✔
320
}
321

322
int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len) {
×
323
   return botan_constant_time_compare(x, y, len);
×
324
}
325

326
int botan_scrub_mem(void* mem, size_t bytes) {
1✔
327
   Botan::secure_scrub_memory(mem, bytes);
1✔
328
   return BOTAN_FFI_SUCCESS;
1✔
329
}
330

331
int botan_hex_encode(const uint8_t* in, size_t len, char* out, uint32_t flags) {
3✔
332
   return ffi_guard_thunk(__func__, [=]() -> int {
3✔
333
      const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0;
3✔
334
      Botan::hex_encode(out, in, len, uppercase);
3✔
335
      return BOTAN_FFI_SUCCESS;
3✔
336
   });
3✔
337
}
338

339
int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len) {
2✔
340
   return ffi_guard_thunk(__func__, [=]() -> int {
2✔
341
      const std::vector<uint8_t> bin = Botan::hex_decode(hex_str, in_len);
2✔
342
      return Botan_FFI::write_vec_output(out, out_len, bin);
2✔
343
   });
4✔
344
}
345

346
int botan_base64_encode(const uint8_t* in, size_t len, char* out, size_t* out_len) {
2✔
347
   return ffi_guard_thunk(__func__, [=]() -> int {
2✔
348
      const std::string base64 = Botan::base64_encode(in, len);
2✔
349
      return Botan_FFI::write_str_output(out, out_len, base64);
2✔
350
   });
4✔
351
}
352

353
int botan_base64_decode(const char* base64_str, size_t in_len, uint8_t* out, size_t* out_len) {
2✔
354
   return ffi_guard_thunk(__func__, [=]() -> int {
2✔
355
      if(*out_len < Botan::base64_decode_max_output(in_len)) {
2✔
356
         *out_len = Botan::base64_decode_max_output(in_len);
1✔
357
         return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
1✔
358
      }
359

360
      *out_len = Botan::base64_decode(out, std::string(base64_str, in_len));
1✔
361
      return BOTAN_FFI_SUCCESS;
1✔
362
   });
2✔
363
}
364
}
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