/* * (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 #include namespace Botan_FFI { class BOTAN_UNSTABLE_API FFI_Error final : public Botan::Exception { public: FFI_Error(const std::string& what, int err_code) : Exception("FFI error", what), m_err_code(err_code) {} int error_code() const noexcept override { return m_err_code; } Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::InvalidArgument; } private: int m_err_code; }; 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* unsafe_get() const { 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, int rc = BOTAN_FFI_ERROR_EXCEPTION_THROWN); template T& safe_get(botan_struct* p) { if(!p) throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER); if(p->magic_ok() == false) throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT); if(T* t = p->unsafe_get()) return *t; throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT); } int ffi_guard_thunk(const char* func_name, std::function); template int apply_fn(botan_struct* o, const char* func_name, F func) { if(!o) return BOTAN_FFI_ERROR_NULL_POINTER; if(o->magic_ok() == false) return BOTAN_FFI_ERROR_INVALID_OBJECT; T* p = o->unsafe_get(); if(p == nullptr) return BOTAN_FFI_ERROR_INVALID_OBJECT; return ffi_guard_thunk(func_name, [&]() { return func(*p); }); } #define BOTAN_FFI_DO(T, obj, param, block) \ apply_fn(obj, __func__, \ [=](T& param) -> int { do { block } while(0); return BOTAN_FFI_SUCCESS; }) /* * Like BOTAN_FFI_DO but with no trailing return with the expectation * that the block always returns a value. This exists because otherwise * MSVC warns about the dead return after the block in FFI_DO. */ #define BOTAN_FFI_RETURNING(T, obj, param, block) \ apply_fn(obj, __func__, \ [=](T& param) -> int { do { block } while(0); }) 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_OBJECT; 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, __func__) inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len) { if(out_len == nullptr) return BOTAN_FFI_ERROR_NULL_POINTER; const size_t avail = *out_len; *out_len = buf_len; if((avail >= buf_len) && (out != nullptr)) { Botan::copy_mem(out, buf, buf_len); return BOTAN_FFI_SUCCESS; } else { if(out != nullptr) { 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