Coverage Report

Created: 2025-08-28 06:21

/src/Botan-3.4.0/src/lib/ffi/ffi.cpp
Line
Count
Source (jump to first uncovered line)
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 <botan/internal/os_utils.h>
16
#include <cstdio>
17
#include <cstdlib>
18
19
namespace Botan_FFI {
20
21
// NOLINTNEXTLINE(*-avoid-non-const-global-variables)
22
thread_local std::string g_last_exception_what;
23
24
3.64k
int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc) {
25
3.64k
   g_last_exception_what.assign(exn);
26
27
3.64k
   std::string val;
28
3.64k
   if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") == true && !val.empty()) {
29
0
      static_cast<void>(std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc));
30
0
   }
31
3.64k
   return rc;
32
3.64k
}
33
34
0
int botan_view_str_bounce_fn(botan_view_ctx vctx, const char* str, size_t len) {
35
0
   return botan_view_bin_bounce_fn(vctx, reinterpret_cast<const uint8_t*>(str), len);
36
0
}
37
38
0
int botan_view_bin_bounce_fn(botan_view_ctx vctx, const uint8_t* buf, size_t len) {
39
0
   if(vctx == nullptr || buf == nullptr) {
40
0
      return BOTAN_FFI_ERROR_NULL_POINTER;
41
0
   }
42
43
0
   botan_view_bounce_struct* ctx = static_cast<botan_view_bounce_struct*>(vctx);
44
45
0
   const size_t avail = *ctx->out_len;
46
0
   *ctx->out_len = len;
47
48
0
   if(avail < len || ctx->out_ptr == nullptr) {
49
0
      if(ctx->out_ptr) {
50
0
         Botan::clear_mem(ctx->out_ptr, avail);
51
0
      }
52
0
      return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
53
0
   } else {
54
0
      Botan::copy_mem(ctx->out_ptr, buf, len);
55
0
      return BOTAN_FFI_SUCCESS;
56
0
   }
57
0
}
58
59
namespace {
60
61
3.64k
int ffi_map_error_type(Botan::ErrorType err) {
62
3.64k
   switch(err) {
63
0
      case Botan::ErrorType::Unknown:
64
0
         return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
65
66
0
      case Botan::ErrorType::SystemError:
67
0
      case Botan::ErrorType::IoError:
68
0
      case Botan::ErrorType::Pkcs11Error:
69
0
      case Botan::ErrorType::CommonCryptoError:
70
0
      case Botan::ErrorType::TPMError:
71
0
      case Botan::ErrorType::ZlibError:
72
0
      case Botan::ErrorType::Bzip2Error:
73
0
      case Botan::ErrorType::LzmaError:
74
0
      case Botan::ErrorType::DatabaseError:
75
0
         return BOTAN_FFI_ERROR_SYSTEM_ERROR;
76
77
0
      case Botan::ErrorType::NotImplemented:
78
0
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
79
0
      case Botan::ErrorType::OutOfMemory:
80
0
         return BOTAN_FFI_ERROR_OUT_OF_MEMORY;
81
0
      case Botan::ErrorType::InternalError:
82
0
         return BOTAN_FFI_ERROR_INTERNAL_ERROR;
83
0
      case Botan::ErrorType::InvalidObjectState:
84
0
         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
85
0
      case Botan::ErrorType::KeyNotSet:
86
0
         return BOTAN_FFI_ERROR_KEY_NOT_SET;
87
190
      case Botan::ErrorType::InvalidArgument:
88
190
      case Botan::ErrorType::InvalidNonceLength:
89
190
         return BOTAN_FFI_ERROR_BAD_PARAMETER;
90
91
0
      case Botan::ErrorType::EncodingFailure:
92
3.45k
      case Botan::ErrorType::DecodingFailure:
93
3.45k
         return BOTAN_FFI_ERROR_INVALID_INPUT;
94
95
0
      case Botan::ErrorType::InvalidTag:
96
0
         return BOTAN_FFI_ERROR_BAD_MAC;
97
98
0
      case Botan::ErrorType::InvalidKeyLength:
99
0
         return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
100
0
      case Botan::ErrorType::LookupError:
101
0
         return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
102
103
0
      case Botan::ErrorType::HttpError:
104
0
         return BOTAN_FFI_ERROR_HTTP_ERROR;
105
0
      case Botan::ErrorType::TLSError:
106
0
         return BOTAN_FFI_ERROR_TLS_ERROR;
107
0
      case Botan::ErrorType::RoughtimeError:
108
0
         return BOTAN_FFI_ERROR_ROUGHTIME_ERROR;
109
3.64k
   }
110
111
0
   return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
112
3.64k
}
113
114
}  // namespace
115
116
4.15M
int ffi_guard_thunk(const char* func_name, const std::function<int()>& thunk) {
117
4.15M
   g_last_exception_what.clear();
118
119
4.15M
   try {
120
4.15M
      return thunk();
121
4.15M
   } catch(std::bad_alloc&) {
122
0
      return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY);
123
0
   } catch(Botan_FFI::FFI_Error& e) {
124
0
      return ffi_error_exception_thrown(func_name, e.what(), e.error_code());
125
3.64k
   } catch(Botan::Exception& e) {
126
3.64k
      return ffi_error_exception_thrown(func_name, e.what(), ffi_map_error_type(e.error_type()));
127
3.64k
   } catch(std::exception& e) {
128
0
      return ffi_error_exception_thrown(func_name, e.what());
129
0
   } catch(...) {
130
0
      return ffi_error_exception_thrown(func_name, "unknown exception");
131
0
   }
132
133
0
   return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
134
4.15M
}
135
136
}  // namespace Botan_FFI
137
138
extern "C" {
139
140
using namespace Botan_FFI;
141
142
0
const char* botan_error_last_exception_message() {
143
0
   return g_last_exception_what.c_str();
144
0
}
145
146
0
const char* botan_error_description(int err) {
147
0
   switch(err) {
148
0
      case BOTAN_FFI_SUCCESS:
149
0
         return "OK";
150
151
0
      case BOTAN_FFI_INVALID_VERIFIER:
152
0
         return "Invalid verifier";
153
154
0
      case BOTAN_FFI_ERROR_INVALID_INPUT:
155
0
         return "Invalid input";
156
157
0
      case BOTAN_FFI_ERROR_BAD_MAC:
158
0
         return "Invalid authentication code";
159
160
0
      case BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE:
161
0
         return "Insufficient buffer space";
162
163
0
      case BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR:
164
0
         return "String conversion error";
165
166
0
      case BOTAN_FFI_ERROR_EXCEPTION_THROWN:
167
0
         return "Exception thrown";
168
169
0
      case BOTAN_FFI_ERROR_OUT_OF_MEMORY:
170
0
         return "Out of memory";
171
172
0
      case BOTAN_FFI_ERROR_SYSTEM_ERROR:
173
0
         return "Error while calling system API";
174
175
0
      case BOTAN_FFI_ERROR_INTERNAL_ERROR:
176
0
         return "Internal error";
177
178
0
      case BOTAN_FFI_ERROR_BAD_FLAG:
179
0
         return "Bad flag";
180
181
0
      case BOTAN_FFI_ERROR_NULL_POINTER:
182
0
         return "Null pointer argument";
183
184
0
      case BOTAN_FFI_ERROR_BAD_PARAMETER:
185
0
         return "Bad parameter";
186
187
0
      case BOTAN_FFI_ERROR_KEY_NOT_SET:
188
0
         return "Key not set on object";
189
190
0
      case BOTAN_FFI_ERROR_INVALID_KEY_LENGTH:
191
0
         return "Invalid key length";
192
193
0
      case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE:
194
0
         return "Invalid object state";
195
196
0
      case BOTAN_FFI_ERROR_NOT_IMPLEMENTED:
197
0
         return "Not implemented";
198
199
0
      case BOTAN_FFI_ERROR_INVALID_OBJECT:
200
0
         return "Invalid object handle";
201
202
0
      case BOTAN_FFI_ERROR_TLS_ERROR:
203
0
         return "TLS error";
204
205
0
      case BOTAN_FFI_ERROR_HTTP_ERROR:
206
0
         return "HTTP error";
207
208
0
      case BOTAN_FFI_ERROR_UNKNOWN_ERROR:
209
0
         return "Unknown error";
210
211
0
      default:
212
0
         return "Unknown error";
213
0
   }
214
0
}
215
216
/*
217
* Versioning
218
*/
219
0
uint32_t botan_ffi_api_version() {
220
0
   return BOTAN_HAS_FFI;
221
0
}
222
223
6.92k
int botan_ffi_supports_api(uint32_t api_version) {
224
   // This is the API introduced in 3.4
225
6.92k
   if(api_version == 20240408) {
226
0
      return BOTAN_FFI_SUCCESS;
227
0
   }
228
229
   // This is the API introduced in 3.2
230
6.92k
   if(api_version == 20231009) {
231
0
      return BOTAN_FFI_SUCCESS;
232
0
   }
233
234
   // This is the API introduced in 3.1
235
6.92k
   if(api_version == 20230711) {
236
0
      return BOTAN_FFI_SUCCESS;
237
0
   }
238
239
   // This is the API introduced in 3.0
240
6.92k
   if(api_version == 20230403) {
241
0
      return BOTAN_FFI_SUCCESS;
242
0
   }
243
244
   // This is the API introduced in 2.18
245
6.92k
   if(api_version == 20210220) {
246
0
      return BOTAN_FFI_SUCCESS;
247
0
   }
248
249
   // This is the API introduced in 2.13
250
6.92k
   if(api_version == 20191214) {
251
0
      return BOTAN_FFI_SUCCESS;
252
0
   }
253
254
   // This is the API introduced in 2.8
255
6.92k
   if(api_version == 20180713) {
256
6.92k
      return BOTAN_FFI_SUCCESS;
257
6.92k
   }
258
259
   // This is the API introduced in 2.3
260
0
   if(api_version == 20170815) {
261
0
      return BOTAN_FFI_SUCCESS;
262
0
   }
263
264
   // This is the API introduced in 2.1
265
0
   if(api_version == 20170327) {
266
0
      return BOTAN_FFI_SUCCESS;
267
0
   }
268
269
   // This is the API introduced in 2.0
270
0
   if(api_version == 20150515) {
271
0
      return BOTAN_FFI_SUCCESS;
272
0
   }
273
274
   // Something else:
275
0
   return -1;
276
0
}
277
278
0
const char* botan_version_string() {
279
0
   return Botan::version_cstr();
280
0
}
281
282
0
uint32_t botan_version_major() {
283
0
   return Botan::version_major();
284
0
}
285
286
0
uint32_t botan_version_minor() {
287
0
   return Botan::version_minor();
288
0
}
289
290
0
uint32_t botan_version_patch() {
291
0
   return Botan::version_patch();
292
0
}
293
294
0
uint32_t botan_version_datestamp() {
295
0
   return Botan::version_datestamp();
296
0
}
297
298
0
int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len) {
299
0
   auto same = Botan::CT::is_equal(x, y, len);
300
   // Return 0 if same or -1 otherwise
301
0
   return static_cast<int>(same.select(1, 0)) - 1;
302
0
}
303
304
0
int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len) {
305
0
   return botan_constant_time_compare(x, y, len);
306
0
}
307
308
18.0M
int botan_scrub_mem(void* mem, size_t bytes) {
309
18.0M
   Botan::secure_scrub_memory(mem, bytes);
310
18.0M
   return BOTAN_FFI_SUCCESS;
311
18.0M
}
312
313
795k
int botan_hex_encode(const uint8_t* in, size_t len, char* out, uint32_t flags) {
314
795k
   return ffi_guard_thunk(__func__, [=]() -> int {
315
795k
      const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0;
316
795k
      Botan::hex_encode(out, in, len, uppercase);
317
795k
      return BOTAN_FFI_SUCCESS;
318
795k
   });
319
795k
}
320
321
1.08M
int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len) {
322
1.08M
   return ffi_guard_thunk(__func__, [=]() -> int {
323
1.08M
      const std::vector<uint8_t> bin = Botan::hex_decode(hex_str, in_len);
324
1.08M
      return Botan_FFI::write_vec_output(out, out_len, bin);
325
1.08M
   });
326
1.08M
}
327
328
0
int botan_base64_encode(const uint8_t* in, size_t len, char* out, size_t* out_len) {
329
0
   return ffi_guard_thunk(__func__, [=]() -> int {
330
0
      const std::string base64 = Botan::base64_encode(in, len);
331
0
      return Botan_FFI::write_str_output(out, out_len, base64);
332
0
   });
333
0
}
334
335
0
int botan_base64_decode(const char* base64_str, size_t in_len, uint8_t* out, size_t* out_len) {
336
0
   return ffi_guard_thunk(__func__, [=]() -> int {
337
0
      if(*out_len < Botan::base64_decode_max_output(in_len)) {
338
0
         *out_len = Botan::base64_decode_max_output(in_len);
339
0
         return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
340
0
      }
341
342
0
      *out_len = Botan::base64_decode(out, std::string(base64_str, in_len));
343
0
      return BOTAN_FFI_SUCCESS;
344
0
   });
345
0
}
346
}