/* * (C) 2015,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_FFI_UTILS_H_ #define BOTAN_FFI_UTILS_H_ #include #include #include #include #include namespace Botan_FFI { #define BOTAN_ASSERT_ARG_NON_NULL(p) \ do { if(!p) throw Botan::Invalid_Argument("Argument " #p " is null"); } while(0) class FFI_Error final : public Botan::Exception { public: explicit FFI_Error(const std::string& what) : Exception("FFI error", what) {} }; template struct botan_struct { public: botan_struct(T* obj) : m_magic(MAGIC), m_obj(obj) {} virtual ~botan_struct() { m_magic = 0; m_obj.reset(); } bool magic_ok() const { return (m_magic == MAGIC); } T* get() const { if(magic_ok() == false) throw FFI_Error("Bad magic " + std::to_string(m_magic) + " in ffi object expected " + std::to_string(MAGIC)); return m_obj.get(); } private: uint32_t m_magic = 0; std::unique_ptr m_obj; }; #define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \ struct NAME final : public Botan_FFI::botan_struct { explicit NAME(TYPE* x) : botan_struct(x) {} } // Declared in ffi.cpp int ffi_error_exception_thrown(const char* func_name, const char* exn); template T& safe_get(botan_struct* p) { if(!p) throw FFI_Error("Null pointer argument"); if(T* t = p->get()) return *t; throw FFI_Error("Invalid object pointer"); } template const T& safe_get(const botan_struct* p) { if(!p) throw FFI_Error("Null pointer argument"); if(const T* t = p->get()) return *t; throw FFI_Error("Invalid object pointer"); } template int ffi_guard_thunk(const char* func_name, Thunk thunk) { try { return thunk(); } catch(std::bad_alloc) { return ffi_error_exception_thrown(func_name, "bad_alloc"); } catch(std::exception& e) { return ffi_error_exception_thrown(func_name, e.what()); } catch(...) { return ffi_error_exception_thrown(func_name, "unknown exception"); } return BOTAN_FFI_ERROR_UNKNOWN_ERROR; } template int apply_fn(botan_struct* o, const char* func_name, F func) { try { if(!o) throw FFI_Error("Null object to " + std::string(func_name)); if(T* t = o->get()) return func(*t); } catch(std::bad_alloc) { return ffi_error_exception_thrown(func_name, "bad_alloc"); } catch(std::exception& e) { return ffi_error_exception_thrown(func_name, e.what()); } catch(...) { return ffi_error_exception_thrown(func_name, "unknown exception"); } return BOTAN_FFI_ERROR_UNKNOWN_ERROR; } #define BOTAN_FFI_DO(T, obj, param, block) \ apply_fn(obj, BOTAN_CURRENT_FUNCTION, \ [=](T& param) -> int { do { block } while(0); return BOTAN_FFI_SUCCESS; }) template int ffi_delete_object(botan_struct* obj, const char* func_name) { try { if(obj == nullptr) return BOTAN_FFI_SUCCESS; // ignore delete of null objects if(obj->magic_ok() == false) return BOTAN_FFI_ERROR_INVALID_INPUT; delete obj; return BOTAN_FFI_SUCCESS; } catch(std::exception& e) { return ffi_error_exception_thrown(func_name, e.what()); } catch(...) { return ffi_error_exception_thrown(func_name, "unknown exception"); } } #define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, BOTAN_CURRENT_FUNCTION) inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len) { const size_t avail = *out_len; *out_len = buf_len; if(avail >= buf_len) { Botan::copy_mem(out, buf, buf_len); return BOTAN_FFI_SUCCESS; } else { Botan::clear_mem(out, avail); return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; } } template int write_vec_output(uint8_t out[], size_t* out_len, const std::vector& buf) { return write_output(out, out_len, buf.data(), buf.size()); } inline int write_str_output(uint8_t out[], size_t* out_len, const std::string& str) { return write_output(out, out_len, Botan::cast_char_ptr_to_uint8(str.data()), str.size() + 1); } inline int write_str_output(char out[], size_t* out_len, const std::string& str) { return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str); } inline int write_str_output(char out[], size_t* out_len, const std::vector& str_vec) { return write_output(Botan::cast_char_ptr_to_uint8(out), out_len, str_vec.data(), str_vec.size()); } } #endif