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