aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/ffi
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ffi')
-rw-r--r--src/lib/ffi/ffi.cpp1099
-rw-r--r--src/lib/ffi/ffi.h342
-rw-r--r--src/lib/ffi/info.txt12
3 files changed, 1453 insertions, 0 deletions
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
new file mode 100644
index 000000000..937423c69
--- /dev/null
+++ b/src/lib/ffi/ffi.cpp
@@ -0,0 +1,1099 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/system_rng.h>
+#include <botan/auto_rng.h>
+#include <botan/lookup.h>
+#include <botan/aead.h>
+#include <botan/hash.h>
+#include <botan/mac.h>
+#include <botan/pbkdf.h>
+#include <botan/version.h>
+#include <botan/pubkey.h>
+#include <botan/data_src.h>
+#include <botan/mem_ops.h>
+#include <cstring>
+#include <memory>
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+ #include <botan/bcrypt.h>
+#endif
+
+namespace {
+
+#define BOTAN_ASSERT_ARG_NON_NULL(p) \
+ do { if(!p) throw std::invalid_argument("Argument " #p " is null"); } while(0)
+
+template<typename T, uint32_t MAGIC>
+struct botan_struct
+ {
+ public:
+ botan_struct(T* obj) : m_magic(MAGIC), m_obj(obj) {}
+ ~botan_struct() { m_magic = 0; m_obj.reset(); }
+
+ T* get() const
+ {
+ BOTAN_ASSERT_EQUAL(m_magic, MAGIC, "Bad magic value - memory corruption?");
+ return m_obj.get();
+ }
+ private:
+ uint32_t m_magic = 0;
+ std::unique_ptr<T> m_obj;
+ };
+
+void log_exception(const char* func_name, const char* what)
+ {
+ printf("botan ffi %s: %s\n", func_name, what);
+ }
+
+template<typename T, uint32_t M>
+T& safe_get(botan_struct<T,M>* p)
+ {
+ if(!p)
+ throw std::runtime_error("Null pointer argument");
+ if(T* t = p->get())
+ return *t;
+ throw std::runtime_error("Invalid object pointer");
+ }
+
+template<typename T, uint32_t M, typename F>
+int apply_fn(botan_struct<T, M>* o, const char* func_name, F func)
+ {
+ try
+ {
+ if(!o)
+ throw std::runtime_error("Null object to " + std::string(func_name));
+ if(T* t = o->get())
+ return func(*t);
+ }
+ catch(std::exception& e)
+ {
+ log_exception(func_name, e.what());
+ return -1;
+ }
+ catch(...)
+ {
+ log_exception(func_name, "unknown exception type");
+ return -2;
+ }
+
+ return -1;
+ }
+
+template<typename Alloc>
+int write_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf)
+ {
+ Botan::clear_mem(out, *out_len);
+ const size_t avail = *out_len;
+ *out_len = buf.size();
+ if(avail >= buf.size())
+ {
+ Botan::copy_mem(out, &buf[0], buf.size());
+ return 0;
+ }
+ return -1;
+ }
+
+#define BOTAN_FFI_DO(T, obj, block) apply_fn(obj, BOTAN_CURRENT_FUNCTION, [=](T& obj) { do { block } while(0); return 0; })
+
+}
+
+extern "C" {
+
+struct botan_rng_struct : public botan_struct<Botan::RandomNumberGenerator, 0x4901F9C1>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_hash_struct : public botan_struct<Botan::HashFunction, 0x1F0A4F84>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_mac_struct : public botan_struct<Botan::MessageAuthenticationCode, 0xA06E8FC1>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_cipher_struct : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C>
+ {
+ using botan_struct::botan_struct;
+ Botan::secure_vector<uint8_t> m_buf;
+ };
+
+struct botan_pubkey_struct : public botan_struct<Botan::Public_Key, 0x2C286519>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_privkey_struct : public botan_struct<Botan::Private_Key, 0x7F96385E>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_pk_op_encrypt_struct : public botan_struct<Botan::PK_Encryptor, 0x891F3FC3>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_pk_op_decrypt_struct : public botan_struct<Botan::PK_Decryptor, 0x912F3C37>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_pk_op_sign_struct : public botan_struct<Botan::PK_Signer, 0x1AF0C39F>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_pk_op_verify_struct : public botan_struct<Botan::PK_Verifier, 0x2B91F936>
+ {
+ using botan_struct::botan_struct;
+ };
+
+struct botan_pk_op_ka_struct : public botan_struct<Botan::PK_Key_Agreement, 0x2939CAB1>
+ {
+ using botan_struct::botan_struct;
+ };
+
+/*
+* Versioning
+*/
+uint32_t botan_ffi_api_version()
+ {
+ return 20150210; // should match value in info.txt
+ }
+
+const char* botan_version_string()
+ {
+ return Botan::version_cstr();
+ }
+
+uint32_t botan_version_major() { return Botan::version_major(); }
+uint32_t botan_version_minor() { return Botan::version_minor(); }
+uint32_t botan_version_patch() { return Botan::version_patch(); }
+uint32_t botan_version_datestamp() { return Botan::version_datestamp(); }
+
+int botan_rng_init(botan_rng_t* rng_out, const char* rng_type)
+ {
+ // Just gives unique_ptr something to delete, really
+ class RNG_Wrapper : public Botan::RandomNumberGenerator
+ {
+ public:
+ RNG_Wrapper(Botan::RandomNumberGenerator& rng) : m_rng(rng) {}
+ void randomize(Botan::byte out[], size_t len) override { m_rng.randomize(out, len); }
+ bool is_seeded() const override { return m_rng.is_seeded(); }
+ void clear() override { m_rng.clear(); }
+ std::string name() const { return m_rng.name(); }
+ void reseed(size_t poll_bits = 256) { m_rng.reseed(poll_bits); }
+ void add_entropy(const Botan::byte in[], size_t len) { m_rng.add_entropy(in, len); }
+ private:
+ Botan::RandomNumberGenerator& m_rng;
+ };
+
+ try
+ {
+ BOTAN_ASSERT_ARG_NON_NULL(rng_out);
+
+ if(rng_type == nullptr || *rng_type == 0)
+ rng_type = "system";
+
+ const std::string rng_type_s(rng_type);
+
+ std::unique_ptr<Botan::RandomNumberGenerator> rng;
+
+ if(rng_type_s == "system")
+ rng.reset(new RNG_Wrapper(Botan::system_rng()));
+ else if(rng_type_s == "user")
+ rng.reset(new Botan::AutoSeeded_RNG);
+
+ if(rng)
+ {
+ *rng_out = new botan_rng_struct(rng.release());
+ return 0;
+ }
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -1;
+ }
+
+int botan_rng_destroy(botan_rng_t rng)
+ {
+ delete rng;
+ return 0;
+ }
+
+int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, { rng.randomize(out, out_len); });
+ }
+
+int botan_rng_reseed(botan_rng_t rng, size_t bits)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, { rng.reseed(bits); });
+ }
+
+int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
+ {
+ try
+ {
+ if(!hash || !hash_name || flags != 0)
+ return -1;
+
+ if(auto h = Botan::get_hash_function(hash_name))
+ {
+ *hash = new botan_hash_struct(h);
+ return 0;
+ }
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -2;
+ }
+
+int botan_hash_destroy(botan_hash_t hash)
+ {
+ delete hash;
+ return 0;
+ }
+
+int botan_hash_output_length(botan_hash_t hash, size_t* out)
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, { *out = hash.output_length(); });
+ }
+
+int botan_hash_clear(botan_hash_t hash)
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, { hash.clear(); });
+ }
+
+int botan_hash_update(botan_hash_t hash, const uint8_t* buf, size_t len)
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, { hash.update(buf, len); });
+ }
+
+int botan_hash_final(botan_hash_t hash, uint8_t out[])
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, { hash.final(out); });
+ }
+
+int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags)
+ {
+ try
+ {
+ if(!mac || !mac_name || flags != 0)
+ return -1;
+
+ if(auto m = Botan::get_mac(mac_name))
+ {
+ *mac = new botan_mac_struct(m);
+ return 0;
+ }
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -2;
+ }
+
+int botan_mac_destroy(botan_mac_t mac)
+ {
+ delete mac;
+ return 0;
+ }
+
+int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, { mac.set_key(key, key_len); });
+ }
+
+int botan_mac_output_length(botan_mac_t mac, size_t* out)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, { *out = mac.output_length(); });
+ }
+
+int botan_mac_clear(botan_mac_t mac)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, { mac.clear(); });
+ }
+
+int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, { mac.update(buf, len); });
+ }
+
+int botan_mac_final(botan_mac_t mac, uint8_t out[])
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, { mac.final(out); });
+ }
+
+int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags)
+ {
+ try
+ {
+ Botan::Cipher_Dir dir = (flags & 0) ? Botan::DECRYPTION : Botan::ENCRYPTION;
+ std::unique_ptr<Botan::Cipher_Mode> mode(Botan::get_cipher_mode(cipher_name, dir));
+ if(!mode)
+ return -1;
+ *cipher = new botan_cipher_struct(mode.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -1;
+ }
+
+int botan_cipher_destroy(botan_cipher_t cipher)
+ {
+ delete cipher;
+ return 0;
+ }
+
+int botan_cipher_clear(botan_cipher_t cipher)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { cipher.clear(); });
+ }
+
+int botan_cipher_set_key(botan_cipher_t cipher,
+ const uint8_t* key, size_t key_len)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { cipher.set_key(key, key_len); });
+ }
+
+int botan_cipher_start(botan_cipher_t cipher_obj,
+ const uint8_t* nonce, size_t nonce_len)
+ {
+ try
+ {
+ Botan::Cipher_Mode& cipher = safe_get(cipher_obj);
+ BOTAN_ASSERT(cipher.start(nonce, nonce_len).empty(), "Ciphers have no prefix");
+ cipher_obj->m_buf.reserve(cipher.update_granularity());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_cipher_update(botan_cipher_t cipher_obj,
+ uint32_t flags,
+ uint8_t output[],
+ size_t output_size,
+ size_t* output_written,
+ const uint8_t input[],
+ size_t input_size,
+ size_t* input_consumed)
+ {
+ using namespace Botan;
+
+ try
+ {
+ Cipher_Mode& cipher = safe_get(cipher_obj);
+ secure_vector<uint8_t>& mbuf = cipher_obj->m_buf;
+
+ const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL);
+
+ if(final_input)
+ {
+ mbuf.assign(input, input + input_size);
+ *input_consumed = input_size;
+
+ try
+ {
+ cipher.finish(mbuf);
+ }
+ catch(Integrity_Failure& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ return -2;
+ }
+
+ *output_written = mbuf.size();
+
+ if(mbuf.size() <= output_size)
+ {
+ copy_mem(output, &mbuf[0], mbuf.size());
+ mbuf.clear();
+ return 0;
+ }
+
+ return -1;
+ }
+
+ if(input_size == 0)
+ {
+ // Currently must take entire buffer in this case
+ *output_written = mbuf.size();
+ if(output_size >= mbuf.size())
+ {
+ copy_mem(output, &mbuf[0], mbuf.size());
+ mbuf.clear();
+ return 0;
+ }
+
+ return -1;
+ }
+
+ const size_t ud = cipher.update_granularity();
+ BOTAN_ASSERT(cipher.update_granularity() > cipher.minimum_final_size(), "logic error");
+
+#if 0
+ // Avoiding double copy:
+ if(Online_Cipher_Mode* ocm = dynamic_cast<Online_Cipher_Mode*>(&cipher))
+ {
+ const size_t taken = round_down(input_size, ud);
+ *input_consumed = taken;
+ *output_size = taken;
+ copy_mem(&output[0], input, taken);
+ ocm->update_in_place(output, taken);
+ return 0;
+ }
+#endif
+
+ mbuf.resize(ud);
+ size_t taken = 0, written = 0;
+
+ while(input_size >= ud && output_size >= ud)
+ {
+ copy_mem(&mbuf[0], input, ud);
+ cipher.update(mbuf);
+
+ input_size -= ud;
+ input += ud;
+ taken += ud;
+
+ output_size -= ud;
+ output += ud;
+ written += ud;
+ }
+
+ *output_written = written;
+ *input_consumed = taken;
+
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_cipher_set_associated_data(botan_cipher_t cipher,
+ const uint8_t* ad,
+ size_t ad_len)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, {
+ if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(&cipher))
+ {
+ aead->set_associated_data(ad, ad_len);
+ return 0;
+ }
+ return -1;
+ });
+ }
+
+int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { return cipher.valid_nonce_length(nl) ? 1 : 0; });
+ }
+
+int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { *nl = cipher.default_nonce_length(); });
+ }
+
+int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, { *tl = cipher.tag_size(); });
+ }
+
+int botan_pbkdf(const char* pbkdf_algo, uint8_t out[], size_t out_len,
+ const char* pass, const uint8_t salt[], size_t salt_len,
+ size_t iterations)
+ {
+ try
+ {
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::get_pbkdf(pbkdf_algo));
+
+ auto r = pbkdf->derive_key(out_len, pass, salt, salt_len, iterations).bits_of();
+
+ if(r.size() != out_len)
+ throw std::runtime_error(std::string(pbkdf_algo) + " produced " +
+ std::to_string(r.size()) + " asked for " +
+ std::to_string(out_len));
+
+ Botan::copy_mem(out, &r[0], out_len);
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pbkdf_timed(const char* pbkdf_algo,
+ uint8_t out[], size_t out_len,
+ const char* password,
+ const uint8_t salt[], size_t salt_len,
+ size_t ms_to_run,
+ size_t* iterations_used)
+ {
+ try
+ {
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::get_pbkdf(pbkdf_algo));
+
+ auto r = pbkdf->derive_key(out_len, password, salt, salt_len,
+ std::chrono::milliseconds(ms_to_run),
+ *iterations_used).bits_of();
+
+ if(r.size() != out_len)
+ throw std::runtime_error(std::string(pbkdf_algo) + " produced " +
+ std::to_string(r.size()) + " asked for " +
+ std::to_string(out_len));
+
+ Botan::copy_mem(out, &r[0], out_len);
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_kdf(const char* kdf_algo,
+ uint8_t out[], size_t out_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len)
+ {
+ try
+ {
+ std::unique_ptr<Botan::KDF> kdf(Botan::get_kdf(kdf_algo));
+ auto r = kdf->derive_key(out_len, secret, secret_len, salt, salt_len);
+ if(r.size() != out_len)
+ throw std::runtime_error(std::string(kdf_algo) + " produced " +
+ std::to_string(r.size()) + " asked for " +
+ std::to_string(out_len));
+ Botan::copy_mem(out, &r[0], out_len);
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+#if defined(BOTAN_HAS_BCRYPT)
+int botan_bcrypt_generate(char* out, size_t out_len, const char* pass,
+ botan_rng_t rng_obj, size_t wf)
+ {
+ try
+ {
+ BOTAN_ASSERT_ARG_NON_NULL(out);
+ BOTAN_ASSERT_ARG_NON_NULL(pass);
+
+ if(wf < 2 || wf > 30)
+ throw std::runtime_error("Bad bcrypt work factor " + std::to_string(wf));
+
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ std::memset(out, 0, out_len);
+ const std::string c = Botan::generate_bcrypt(pass, rng, wf);
+ if(out_len <= c.size())
+ return ENOMEM;
+ std::memcpy(out, c.c_str(), c.size());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -1;
+ }
+
+int botan_bcrypt_is_valid(const char* pass, const char* hash)
+ {
+ try
+ {
+ if(Botan::check_bcrypt(pass, hash))
+ return 0; // success
+ return 1;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+ catch(...)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, "unknown");
+ }
+
+ return -1;
+ }
+
+#endif
+
+int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng_obj, size_t n_bits)
+ {
+ try
+ {
+ *key = nullptr;
+
+#if defined(BOTAN_HAS_RSA)
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ std::unique_ptr<Botan::RSA_PrivateKey> rsa(new Botan::RSA_PrivateKey(rng, n_bits));
+ *key = new botan_privkey_struct(rsa.release());
+ return 0;
+#endif
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+
+int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng_obj, const char* params)
+ {
+ try
+ {
+#if defined(BOTAN_HAS_ECDSA)
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ Botan::EC_Group grp(params);
+ std::unique_ptr<Botan::ECDSA_PrivateKey> ecdsa(new Botan::ECDSA_PrivateKey(rng, grp));
+ *key = new botan_privkey_struct(ecdsa.release());
+ return 0;
+#endif
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng_obj, const char* params)
+ {
+ try
+ {
+#if defined(BOTAN_HAS_ECDH)
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ Botan::EC_Group grp(params);
+ std::unique_ptr<Botan::ECDH_PrivateKey> ecdh(new Botan::ECDH_PrivateKey(rng, grp));
+ *key = new botan_privkey_struct(ecdh.release());
+ return 0;
+#endif
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
+ const uint8_t bits[], size_t len,
+ const char* password)
+ {
+ try
+ {
+ Botan::DataSource_Memory src(bits, len);
+
+ if(password == nullptr)
+ password = "";
+
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ std::unique_ptr<Botan::PKCS8_PrivateKey> pkcs8;
+ pkcs8.reset(Botan::PKCS8::load_key(src, rng, password));
+
+ if(pkcs8)
+ {
+ *key = new botan_privkey_struct(pkcs8.release());
+ return 0;
+ }
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ *key = nullptr;
+ return -1;
+ }
+
+int botan_privkey_destroy(botan_privkey_t key)
+ {
+ delete key;
+ return 0;
+ }
+
+int botan_pubkey_destroy(botan_privkey_t key)
+ {
+ delete key;
+ return 0;
+ }
+
+int botan_privkey_export_pubkey(botan_pubkey_t* pubout, botan_privkey_t key_obj)
+ {
+ try
+ {
+ std::unique_ptr<Botan::Public_Key> pubkey(
+ Botan::X509::load_key(
+ Botan::X509::BER_encode(safe_get(key_obj))));
+ *pubout = new botan_pubkey_struct(pubkey.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
+ {
+ return apply_fn(key, BOTAN_CURRENT_FUNCTION,
+ [out,out_len](Botan::Public_Key& k)
+ {
+ const std::string name = k.algo_name();
+ const size_t avail = *out_len;
+ *out_len = name.size() + 1;
+ if(avail > 1 + name.size())
+ {
+ Botan::copy_mem(out, name.data(), name.size());
+ out[name.size()] = 0;
+ return 0;
+ }
+ return -1;
+ });
+ }
+
+int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, {
+ return write_output(out, out_len, Botan::X509::BER_encode(key));
+ });
+ }
+
+int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, {
+ return write_output(out, out_len, Botan::PKCS8::BER_encode(key));
+ });
+ }
+
+int botan_privkey_export_encrypted(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* passphrase,
+ const char* encryption_algo,
+ uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, {
+ auto ber = Botan::PKCS8::BER_encode(key, safe_get(rng_obj), passphrase,
+ std::chrono::milliseconds(300),
+ encryption_algo);
+ return write_output(out, out_len, ber);
+ });
+ }
+
+int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, { *estimate = key.estimated_strength(); });
+ }
+
+int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash_fn,
+ uint8_t out[], size_t* out_len)
+ {
+ return apply_fn(key, BOTAN_CURRENT_FUNCTION,
+ [hash_fn,out,out_len](Botan::Public_Key& k)
+ {
+ std::unique_ptr<Botan::HashFunction> h(Botan::get_hash(hash_fn));
+ auto z = h->process(k.x509_subject_public_key());
+ *out_len = std::min(z.size(), *out_len);
+ Botan::copy_mem(out, &z[0], *out_len);
+ return 0;
+ });
+ *out_len = 0;
+ return -1;
+ }
+
+int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+ botan_pubkey_t key_obj,
+ const char* padding,
+ uint32_t flags)
+ {
+ try
+ {
+ BOTAN_ASSERT_NONNULL(op);
+
+ if(flags != 0)
+ return -2;
+
+ std::unique_ptr<Botan::PK_Encryptor> pk(new Botan::PK_Encryptor_EME(safe_get(key_obj), padding));
+ *op = new botan_pk_op_encrypt_struct(pk.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op)
+ {
+ delete op;
+ return 0;
+ }
+
+int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+ botan_rng_t rng_obj,
+ uint8_t out[], size_t* out_len,
+ const uint8_t plaintext[], size_t plaintext_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Encryptor, op, {
+ return write_output(out, out_len, op.encrypt(plaintext, plaintext_len, safe_get(rng_obj)));
+ });
+ }
+
+/*
+* Public Key Decryption
+*/
+int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+ botan_privkey_t key_obj,
+ const char* padding,
+ uint32_t flags)
+ {
+ try
+ {
+ BOTAN_ASSERT_NONNULL(op);
+
+ if(flags != 0)
+ return -2;
+
+ std::unique_ptr<Botan::PK_Decryptor> pk(new Botan::PK_Decryptor_EME(safe_get(key_obj), padding));
+ *op = new botan_pk_op_decrypt_struct(pk.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op)
+ {
+ delete op;
+ return 0;
+ }
+
+int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+ uint8_t out[], size_t* out_len,
+ uint8_t ciphertext[], size_t ciphertext_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Decryptor, op, {
+ return write_output(out, out_len, op.decrypt(ciphertext, ciphertext_len));
+ });
+ }
+
+/*
+* Signature Generation
+*/
+int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+ botan_privkey_t key_obj,
+ const char* hash,
+ uint32_t flags)
+ {
+ try
+ {
+ BOTAN_ASSERT_NONNULL(op);
+
+ if(flags != 0)
+ return -2;
+
+ std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), hash));
+ *op = new botan_pk_op_sign_struct(pk.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pk_op_sign_destroy(botan_pk_op_sign_t op)
+ {
+ delete op;
+ return 0;
+ }
+
+int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Signer, op, { op.update(in, in_len); });
+ }
+
+int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng_obj, uint8_t out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Signer, op, {
+ return write_output(out, out_len, op.signature(safe_get(rng_obj)));
+ });
+ }
+
+int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+ botan_pubkey_t key_obj,
+ const char* hash,
+ uint32_t flags)
+ {
+ try
+ {
+ BOTAN_ASSERT_NONNULL(op);
+
+ if(flags != 0)
+ return -2;
+
+ std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash));
+ *op = new botan_pk_op_verify_struct(pk.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pk_op_verify_destroy(botan_pk_op_verify_t op)
+ {
+ delete op;
+ return 0;
+ }
+
+int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Verifier, op, { op.update(in, in_len); });
+ }
+
+int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Verifier, op, {
+ const bool legit = op.check_signature(sig, sig_len);
+
+ if(legit)
+ return 0;
+ else
+ return 1;
+ });
+ }
+
+int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+ botan_privkey_t key_obj,
+ const char* kdf,
+ uint32_t flags)
+ {
+ try
+ {
+ BOTAN_ASSERT_NONNULL(op);
+
+ if(flags != 0)
+ return -2;
+
+ std::unique_ptr<Botan::PK_Key_Agreement> pk(new Botan::PK_Key_Agreement(safe_get(key_obj), kdf));
+ *op = new botan_pk_op_ka_struct(pk.release());
+ return 0;
+ }
+ catch(std::exception& e)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, e.what());
+ }
+
+ return -1;
+ }
+
+int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op)
+ {
+ delete op;
+ return 0;
+ }
+
+int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, {
+ auto k = op.derive_key(*out_len, other_key, other_key_len, salt, salt_len).bits_of();
+ return write_output(out, out_len, k);
+ });
+ }
+
+}
+
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
new file mode 100644
index 000000000..cda2c1480
--- /dev/null
+++ b/src/lib/ffi/ffi.h
@@ -0,0 +1,342 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_H__
+#define BOTAN_FFI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <botan/build.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+* TODO:
+* - Better error reporting
+* - User callback for exception logging
+* - Doxygen comments for all functions/params
+* - X.509 certs and PKIX path validation goo
+* - TLS
+*/
+
+/*
+* Versioning
+*/
+BOTAN_DLL uint32_t botan_ffi_api_version();
+
+BOTAN_DLL const char* botan_version_string();
+BOTAN_DLL uint32_t botan_version_major();
+BOTAN_DLL uint32_t botan_version_minor();
+BOTAN_DLL uint32_t botan_version_patch();
+BOTAN_DLL uint32_t botan_version_datestamp();
+
+/*
+* Error handling
+*/
+//const char* botan_error_description(int err);
+
+/*
+* Utility
+*/
+BOTAN_DLL int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len);
+
+/*
+* RNG
+*/
+typedef struct botan_rng_struct* botan_rng_t;
+
+BOTAN_DLL int botan_rng_init(botan_rng_t* rng, const char* rng_type);
+BOTAN_DLL int botan_rng_destroy(botan_rng_t rng);
+
+BOTAN_DLL int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len);
+BOTAN_DLL int botan_rng_reseed(botan_rng_t rng, size_t bits);
+
+/*
+* Hashing
+*/
+typedef struct botan_hash_struct* botan_hash_t;
+
+BOTAN_DLL int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags);
+BOTAN_DLL int botan_hash_destroy(botan_hash_t hash);
+BOTAN_DLL int botan_hash_clear(botan_hash_t hash);
+
+BOTAN_DLL int botan_hash_update(botan_hash_t hash, const uint8_t* in, size_t in_len);
+BOTAN_DLL int botan_hash_final(botan_hash_t hash, uint8_t out[]);
+BOTAN_DLL int botan_hash_output_length(botan_hash_t hash, size_t* output_length);
+
+/*
+* Message Authentication
+*/
+typedef struct botan_mac_struct* botan_mac_t;
+
+BOTAN_DLL int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags);
+BOTAN_DLL int botan_mac_destroy(botan_mac_t mac);
+BOTAN_DLL int botan_mac_clear(botan_mac_t hash);
+
+BOTAN_DLL int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len);
+BOTAN_DLL int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len);
+BOTAN_DLL int botan_mac_final(botan_mac_t mac, uint8_t out[]);
+BOTAN_DLL int botan_mac_output_length(botan_mac_t mac, size_t* output_length);
+
+/*
+* Cipher modes
+*/
+typedef struct botan_cipher_struct* botan_cipher_t;
+
+BOTAN_DLL int botan_cipher_init(botan_cipher_t* cipher, const char* name, uint32_t flags);
+BOTAN_DLL int botan_cipher_destroy(botan_cipher_t cipher);
+BOTAN_DLL int botan_cipher_clear(botan_cipher_t hash);
+
+BOTAN_DLL int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl);
+BOTAN_DLL int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_size);
+BOTAN_DLL int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl);
+
+BOTAN_DLL int botan_cipher_set_key(botan_cipher_t cipher,
+ const uint8_t* key, size_t key_len);
+
+BOTAN_DLL int botan_cipher_set_associated_data(botan_cipher_t cipher,
+ const uint8_t* ad, size_t ad_len);
+
+BOTAN_DLL int botan_cipher_start(botan_cipher_t cipher,
+ const uint8_t* nonce, size_t nonce_len);
+
+#define BOTAN_CIPHER_UPDATE_FLAG_FINAL (1U << 0)
+
+BOTAN_DLL int botan_cipher_update(botan_cipher_t cipher,
+ uint32_t flags,
+ uint8_t output[],
+ size_t output_size,
+ size_t* output_written,
+ const uint8_t input_bytes[],
+ size_t input_size,
+ size_t* input_consumed);
+
+
+/*
+* PBKDF
+*/
+BOTAN_DLL int botan_pbkdf(const char* pbkdf_algo,
+ uint8_t out[], size_t out_len,
+ const char* password,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations);
+
+BOTAN_DLL int botan_pbkdf_timed(const char* pbkdf_algo,
+ uint8_t out[], size_t out_len,
+ const char* password,
+ const uint8_t salt[], size_t salt_len,
+ size_t milliseconds_to_run,
+ size_t* out_iterations_used);
+
+/*
+* KDF
+*/
+BOTAN_DLL int botan_kdf(const char* kdf_algo,
+ uint8_t out[], size_t out_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len);
+
+/*
+* Bcrypt
+*/
+#if defined(BOTAN_HAS_BCRYPT)
+
+BOTAN_DLL int botan_bcrypt_generate(char* out, size_t out_len,
+ const char* pass,
+ botan_rng_t rng,
+ size_t work_factor);
+
+/**
+* Returns 0 if if this password/hash combination is valid
+* Returns 1 if the combination is not valid
+* Returns -1 on error
+*/
+BOTAN_DLL int botan_bcrypt_is_valid(const char* pass, const char* hash);
+
+#endif
+
+/*
+* Public/private key creation, import, ...
+*/
+typedef struct botan_pubkey_struct* botan_pubkey_t;
+typedef struct botan_privkey_struct* botan_privkey_t;
+
+BOTAN_DLL int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits);
+//BOTAN_DLL int botan_privkey_create_dsa(botan_privkey_t* key, botan_rng_t rng, size_t p_bits, size_t q_bits);
+//BOTAN_DLL int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, size_t p_bits);
+BOTAN_DLL int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params);
+BOTAN_DLL int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params);
+//BOTAN_DLL int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t);
+
+/*
+* Input currently assumed to be PKCS #8 structure;
+*/
+BOTAN_DLL int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng,
+ const uint8_t bits[], size_t len,
+ const char* password);
+BOTAN_DLL int botan_pubkey_load(botan_privkey_t* key,
+ const uint8_t bits[], size_t len,
+ const char* password);
+
+BOTAN_DLL int botan_privkey_export_pubkey(botan_pubkey_t* out, botan_privkey_t in);
+
+BOTAN_DLL int botan_pubkey_destroy(botan_privkey_t key);
+BOTAN_DLL int botan_privkey_destroy(botan_privkey_t key);
+
+#define BOTAN_PRIVKEY_EXPORT_FLAG_DER 0
+#define BOTAN_PRIVKEY_EXPORT_FLAG_PEM 1
+
+/*
+* On input *out_len is number of bytes in out[]
+* On output *out_len is number of bytes written (or required)
+* If out is not big enough no output is written, *out_len is set and 1 is returned
+* Returns 0 on success and sets
+* If some other error occurs a negative integer is returned.
+*/
+BOTAN_DLL int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags);
+BOTAN_DLL int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags);
+
+/*
+* Set encryption_algo to NULL to have the library choose a default (recommended)
+*/
+BOTAN_DLL int botan_privkey_export_encrypted(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng,
+ const char* passphrase,
+ const char* encryption_algo,
+ uint32_t flags);
+
+BOTAN_DLL int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len);
+
+BOTAN_DLL int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate);
+
+BOTAN_DLL int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash,
+ uint8_t out[], size_t* out_len);
+
+/*
+* Public Key Encryption
+*/
+typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t;
+
+BOTAN_DLL int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+ botan_pubkey_t key,
+ const char* padding,
+ uint32_t flags);
+
+BOTAN_DLL int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op);
+
+BOTAN_DLL int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+ botan_rng_t rng,
+ uint8_t out[], size_t* out_len,
+ const uint8_t plaintext[], size_t plaintext_len);
+
+/*
+* Public Key Decryption
+*/
+typedef struct botan_pk_op_decrypt_struct* botan_pk_op_decrypt_t;
+
+BOTAN_DLL int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+ botan_privkey_t key,
+ const char* padding,
+ uint32_t flags);
+BOTAN_DLL int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op);
+
+BOTAN_DLL int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+ uint8_t out[], size_t* out_len,
+ uint8_t ciphertext[], size_t ciphertext_len);
+
+/*
+* Signature Generation
+*/
+typedef struct botan_pk_op_sign_struct* botan_pk_op_sign_t;
+
+BOTAN_DLL int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+ botan_privkey_t key,
+ const char* hash_and_padding,
+ uint32_t flags);
+BOTAN_DLL int botan_pk_op_sign_destroy(botan_pk_op_sign_t op);
+
+BOTAN_DLL int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len);
+BOTAN_DLL int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng,
+ uint8_t sig[], size_t* sig_len);
+
+/*
+* Signature Verification
+*/
+typedef struct botan_pk_op_verify_struct* botan_pk_op_verify_t;
+
+BOTAN_DLL int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+ botan_pubkey_t key,
+ const char* hash_and_padding,
+ uint32_t flags);
+BOTAN_DLL int botan_pk_op_verify_destroy(botan_pk_op_verify_t op);
+
+BOTAN_DLL int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len);
+BOTAN_DLL int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len);
+
+/*
+* Key Agreement
+*/
+typedef struct botan_pk_op_ka_struct* botan_pk_op_ka_t;
+
+BOTAN_DLL int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+ botan_privkey_t key,
+ const char* kdf,
+ uint32_t flags);
+BOTAN_DLL int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op);
+
+BOTAN_DLL int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len);
+
+/*
+* TLS (not yet implemented)
+*/
+#if defined(BOTAN_HAS_TLS) && 0
+
+typedef struct botan_tls_session_struct* botan_tls_session_t;
+
+// TODO: getters on session_t
+
+typedef struct botan_tls_channel_struct* botan_tls_channel_t;
+
+typedef void (*botan_tls_channel_output_fn)(void, const uin8_t*, size_t);
+typedef void (*botan_tls_channel_data_cb)(void, const uin8_t*, size_t);
+typedef void (*botan_tls_channel_alert_cb)(void, u16bit, const char*);
+typedef void (*botan_tls_channel_session_established)(void, botan_tls_session_t);
+
+BOTAN_DLL int botan_tls_channel_init_client(botan_tls_channel_t* channel,
+ botan_tls_channel_output_fn output_fn,
+ botan_tls_channel_data_cb data_cb,
+ botan_tls_channel_alert_cb alert_cb,
+ botan_tls_channel_session_established session_cb,
+ const char* server_name);
+
+BOTAN_DLL int botan_tls_channel_init_server(botan_tls_channel_t* channel,
+ botan_tls_channel_output_fn output_fn,
+ botan_tls_channel_data_cb data_cb,
+ botan_tls_channel_alert_cb alert_cb,
+ botan_tls_channel_session_established session_cb);
+
+BOTAN_DLL int botan_tls_channel_received_data(botan_tls_channel_t chan,
+ const uint8_t input[], size_t len);
+
+BOTAN_DLL int botan_tls_channel_send(botan_tls_channel_t chan,
+ const uint8_t input[], size_t len);
+
+BOTAN_DLL int botan_tls_channel_send_alert(botan_tls_channel_t chan,
+ uint16_t alert, bool fatal);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt
new file mode 100644
index 000000000..94a804ba0
--- /dev/null
+++ b/src/lib/ffi/info.txt
@@ -0,0 +1,12 @@
+define FFI 20150210
+
+<requires>
+aead
+filters
+kdf
+pbkdf
+pubkey
+auto_rng
+system_rng
+</requires>
+