diff options
33 files changed, 2140 insertions, 1137 deletions
diff --git a/configure.py b/configure.py index f0a589abd..0279361d8 100755 --- a/configure.py +++ b/configure.py @@ -51,6 +51,13 @@ def is_official_release(): # Assume a release date implies official release return (botan_version.release_datestamp > 20130000) +def maintainer_mode_default(): + if is_official_release(): + return False + if os.getenv('GO_ENVIRONMENT_NAME') == 'Botan': + return True # running under CI + return False + def get_vc_revision(): def get_vc_revision(cmdlist): @@ -151,11 +158,8 @@ class BuildConfigurationInformation(object): self.app_sources = list(find_sources_in(self.src_dir, 'cmd')) self.test_sources = list(find_sources_in(self.src_dir, 'tests')) - self.python_sources = list(find_sources_in(self.src_dir, 'python')) - self.boost_python = options.boost_python self.python_dir = os.path.join(options.src_dir, 'python') - self.pyobject_dir = os.path.join(self.build_dir, 'python') def build_doc_commands(): @@ -193,9 +197,6 @@ class BuildConfigurationInformation(object): if options.with_doxygen: yield os.path.join(self.doc_output_dir, 'doxygen') - if self.boost_python: - yield self.pyobject_dir - self.build_dirs = list(build_dirs()) def pkg_config_file(self): @@ -284,7 +285,7 @@ def process_command_line(args): help='disallow use of assembler') build_group.add_option('--enable-debug', dest='debug_build', - action='store_true', default=not is_official_release(), + action='store_true', default=False, help='enable debug build (default %default)') build_group.add_option('--disable-debug', dest='debug_build', action='store_false', help=optparse.SUPPRESS_HELP) @@ -343,8 +344,8 @@ def process_command_line(args): build_group.add_option('--maintainer-mode', dest='maintainer_mode', action='store_true', - default=not is_official_release(), - help="Enable extra warnings") + default=maintainer_mode_default(), + help="Maintainer mode build") build_group.add_option('--release-mode', dest='maintainer_mode', action='store_false', @@ -356,19 +357,10 @@ def process_command_line(args): wrapper_group = optparse.OptionGroup(parser, 'Python FFI options') - wrapper_group.add_option('--with-boost-python', dest='boost_python', - default=False, action='store_true', - help='enable Boost.Python wrapper') - - wrapper_group.add_option('--without-boost-python', - dest='boost_python', - action='store_false', - help=optparse.SUPPRESS_HELP) - wrapper_group.add_option('--with-python-version', dest='python_version', metavar='N.M', default='.'.join(map(str, sys.version_info[0:2])), - help='specify Python to build against (eg %default)') + help='specify Python version (def %default)') mods_group = optparse.OptionGroup(parser, 'Module selection') @@ -1306,16 +1298,6 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): build_commands(build_config.test_sources, build_config.testobj_dir, 'TEST')), - 'python_obj_dir': build_config.pyobject_dir, - - 'python_objs': makefile_list( - objectfile_list(build_config.python_sources, - build_config.pyobject_dir)), - - 'python_build_cmds': '\n'.join( - build_commands(build_config.python_sources, - build_config.pyobject_dir, 'PYTHON')), - 'ar_command': cc.ar_command or osinfo.ar_command, 'ranlib_command': osinfo.ranlib_command(), 'install_cmd_exec': osinfo.install_cmd_exec, @@ -1345,11 +1327,6 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): else: vars["dso_in"] = "" - if options.boost_python: - vars["python_in"] = process_template('src/build-data/makefile/python.in', vars) - else: - vars["python_in"] = "" - return vars """ @@ -1843,7 +1820,7 @@ def main(argv = None): options = process_command_line(argv[1:]) def log_level(): - if options.verbose: + if options.verbose or options.maintainer_mode: return logging.DEBUG if options.quiet: return logging.WARNING diff --git a/doc/manual/contents.rst b/doc/manual/contents.rst index 598510578..83c36e71f 100644 --- a/doc/manual/contents.rst +++ b/doc/manual/contents.rst @@ -25,4 +25,5 @@ Contents srp fpe versions + ffi python diff --git a/doc/manual/ffi.rst b/doc/manual/ffi.rst new file mode 100644 index 000000000..bf6483295 --- /dev/null +++ b/doc/manual/ffi.rst @@ -0,0 +1,103 @@ + +FFI Interface +======================================== + +.. versionadded:: 1.11.14 + +Botan's ffi module provides a C API intended to be easily usable with +other language's foreign function interface (FFI) libraries. For +instance the Python module using the FFI interface needs only the +ctypes module (included in default Python) and works with + +Versioning +---------------------------------------- + +.. cpp:function:: uint32_t botan_ffi_api_version() + Returns the FFI version + +.. cpp:function:: const char* botan_version_string() + Returns a free-from version string + +.. cpp:function:: uint32_t botan_version_major() + Returns the major version of the library +.. cpp:function:: uint32_t botan_version_minor() + Returns the minor version of the library +.. cpp:function:: uint32_t botan_version_patch() + Returns the patch version of the library + +.. cpp:function:: uint32_t botan_version_datestamp() + Returns the date this version was released as an integer, or 0 + if an unreleased version + +Hash Functions +---------------------------------------- + +.. cpp:type:: typedef struct botan_hash_struct* botan_hash_t + An opaque data type for a hash. Don't mess with it. + +.. cpp:function:: botan_hash_t botan_hash_init(const char* hash, uint32_t flags) + Creates a hash of the given name. Returns null on failure. Flags should + always be zero in this version of the API. + +.. cpp:function:: int botan_hash_destroy(botan_hash_t hash) + Destroy the object created by botan_hash_init + +.. cpp:function:: int botan_hash_clear(botan_hash_t hash) + Reset the state of this object back to clean, as if no input has + been supplied + +.. cpp:function:: size_t botan_hash_output_length(botan_hash_t hash) + Return the output length of the hash + +.. cpp:function:: int botan_hash_update(botan_hash_t hash, const uint8_t* input, size_t len) + Add input to the hash computation +.. cpp:function:: int botan_hash_final(botan_hash_t hash, uint8_t out[]) + Finalize the hash and place the output in out. Exactly + botan_hash_output_length() bytes will be written. + +Authentication Codes +---------------------------------------- +.. cpp:type:: typedef struct botan_mac_struct* botan_mac_t + An opaque data type for a MAC. Don't mess with it, but do remember + to set a random key first. + +.. cpp:function:: botan_mac_t botan_mac_init(const char* mac, uint32_t flags) +.. cpp:function:: int botan_mac_destroy(botan_mac_t mac) +.. cpp:function:: int botan_mac_clear(botan_mac_t hash) + +.. cpp:function:: int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len) +.. cpp:function:: int botan_mac_update(botan_mac_t mac, uint8_t buf[], size_t len) +.. cpp:function:: int botan_mac_final(botan_mac_t mac, uint8_t out[], size_t* out_len) +.. cpp:function:: size_t botan_mac_output_length(botan_mac_t mac) + +Ciphers +---------------------------------------- +.. cpp:type:: typedef struct botan_cipher_struct* botan_cipher_t + An opaque data type for a MAC. Don't mess with it, but do remember + to set a random key first. And please use an AEAD. + +.. cpp:function:: botan_cipher_t botan_cipher_init(const char* cipher_name, uint32_t flags) + Create a cipher object from a name such as "AES-256/GCM" or "Serpent/OCB". + + Flags is a bitfield + The low bit of flags specifies if encrypt or decrypt + +.. cpp:function:: int botan_cipher_destroy(botan_cipher_t cipher) +.. cpp:function:: int botan_cipher_clear(botan_cipher_t hash) + +.. cpp:function:: int botan_cipher_set_key(botan_cipher_t cipher, + const uint8_t* key, size_t key_len) + +.. cpp:function:: int botan_cipher_set_associated_data(botan_cipher_t cipher, + const uint8_t* ad, + size_t ad_len) + +.. cpp:function:: int botan_cipher_start(botan_cipher_t cipher, + const uint8_t* nonce, size_t nonce_len) + +.. cpp:function:: int botan_cipher_is_authenticated(botan_cipher_t cipher) +.. cpp:function:: size_t botan_cipher_tag_size(botan_cipher_t cipher) +.. cpp:function:: int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl) + +.. cpp:function:: size_t botan_cipher_default_nonce_length(botan_cipher_t cipher) + diff --git a/doc/manual/python.rst b/doc/manual/python.rst index b8fd59b9a..f851cbaca 100644 --- a/doc/manual/python.rst +++ b/doc/manual/python.rst @@ -2,20 +2,9 @@ Python Binding ======================================== -.. highlight:: python - -.. note:: - - The Python binding should be considered alpha software, and the - interfaces may change in the future. - -Botan includes a binding for Python, implemented using Boost.Python. +.. versionadded:: 1.11.14 -As you can see, it is not currently documented, though there are a few -examples under `src/scripts/examples`, such as RSA: - -.. literalinclude:: ../../src/scripts/examples/rsa.py - -and EAX encryption using a passphrase: +.. highlight:: python -.. literalinclude:: ../../src/scripts/examples/cipher.py +The Python binding is based on the `ffi` module of botan and the +`ctypes` module of the Python standard library. diff --git a/doc/relnotes/1_11_14.rst b/doc/relnotes/1_11_14.rst index 505e26224..07bdd4f59 100644 --- a/doc/relnotes/1_11_14.rst +++ b/doc/relnotes/1_11_14.rst @@ -5,6 +5,12 @@ Version 1.11.14, Not Yet Released removed and no form of initialization is required to use the library. LibraryInitializer remains as a stub. +* The new `ffi` submodule provides a simple C API/ABI for a number of + useful operations (hashing, ciphers, public key operations, etc) + which is easily accessed using the FFI modules included in many + languages. A new Python wrapper using the Python `ctypes` module + is available. The old Boost.Python wrapper has been removed. + * OCB mode, which provides a fast and constant time AEAD mode without requiring hardware support, is now supported in TLS, following draft-zauner-tls-aes-ocb-01. Because this specification is not yet diff --git a/src/build-data/makefile/gmake.in b/src/build-data/makefile/gmake.in index 86d2d3569..cba9e2939 100644 --- a/src/build-data/makefile/gmake.in +++ b/src/build-data/makefile/gmake.in @@ -37,8 +37,6 @@ $(STATIC_LIB): $(LIBOBJS) $(AR) $(STATIC_LIB) $(LIBOBJS) $(RANLIB) $(STATIC_LIB) -%{python_in} - # Fake Targets .PHONY = clean distclean docs website install diff --git a/src/build-data/makefile/nmake.in b/src/build-data/makefile/nmake.in index 8359a1eb6..83192b365 100644 --- a/src/build-data/makefile/nmake.in +++ b/src/build-data/makefile/nmake.in @@ -48,8 +48,6 @@ $(BOTAN_LIB): $(LIBOBJS) $(LIB_LINK_CMD) /Fe$(LIBNAME) $(LIBOBJS) $(LIB_LINKS_TO) !Endif -%{python_in} - # Fake Targets SPHINX_CONFIG = %{sphinx_config_dir} diff --git a/src/lib/base/buf_comp.h b/src/lib/base/buf_comp.h index 5d11fdb73..564da2262 100644 --- a/src/lib/base/buf_comp.h +++ b/src/lib/base/buf_comp.h @@ -101,6 +101,13 @@ class BOTAN_DLL Buffered_Computation return output; } + template<typename Alloc> + void final(std::vector<byte, Alloc>& out) + { + out.resize(output_length()); + final_result(&out[0]); + } + /** * Update and finalize computation. Does the same as calling update() * and final() consecutively. 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> + diff --git a/src/lib/misc/hkdf/hkdf.cpp b/src/lib/kdf/hkdf/hkdf.cpp index 49fa7e256..28f97cadb 100644 --- a/src/lib/misc/hkdf/hkdf.cpp +++ b/src/lib/kdf/hkdf/hkdf.cpp @@ -11,7 +11,12 @@ namespace Botan { std::string HKDF::name() const { - return "HKDF(" + m_prf->name() + ")"; + const std::string prf = m_prf->name(); + const std::string ext = m_extractor->name(); + + if(prf == ext) + return "HKDF(" + prf + ")"; + return "HKDF(" + ext + "," + prf + ")"; } void HKDF::clear() @@ -50,7 +55,7 @@ void HKDF::expand(byte output[], size_t output_len, m_prf->update(T); m_prf->update(info, info_len); m_prf->update(counter++); - T = m_prf->final(); + m_prf->final(T); const size_t to_write = std::min(T.size(), output_len); copy_mem(&output[0], &T[0], to_write); diff --git a/src/lib/misc/hkdf/hkdf.h b/src/lib/kdf/hkdf/hkdf.h index 6bc68796b..f1ae61453 100644 --- a/src/lib/misc/hkdf/hkdf.h +++ b/src/lib/kdf/hkdf/hkdf.h @@ -10,6 +10,7 @@ #include <botan/mac.h> #include <botan/hash.h> +#include <botan/kdf.h> namespace Botan { diff --git a/src/lib/misc/hkdf/info.txt b/src/lib/kdf/hkdf/info.txt index 7389e5bb1..7389e5bb1 100644 --- a/src/lib/misc/hkdf/info.txt +++ b/src/lib/kdf/hkdf/info.txt diff --git a/src/lib/misc/pbes2/pbes2.cpp b/src/lib/misc/pbes2/pbes2.cpp index 17f14170d..e46286906 100644 --- a/src/lib/misc/pbes2/pbes2.cpp +++ b/src/lib/misc/pbes2/pbes2.cpp @@ -80,7 +80,7 @@ pbes2_encrypt(const secure_vector<byte>& key_bits, if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); - std::unique_ptr<Keyed_Transform> enc(get_cipher_mode(cipher, ENCRYPTION)); + std::unique_ptr<Cipher_Mode> enc(get_cipher_mode(cipher, ENCRYPTION)); PKCS5_PBKDF2 pbkdf(Algo_Registry<MessageAuthenticationCode>::global_registry().make(prf)); @@ -153,7 +153,7 @@ pbes2_decrypt(const secure_vector<byte>& key_bits, const std::string prf = OIDS::lookup(prf_algo.oid); PKCS5_PBKDF2 pbkdf(Algo_Registry<MessageAuthenticationCode>::global_registry().make(prf)); - std::unique_ptr<Keyed_Transform> dec(get_cipher_mode(cipher, DECRYPTION)); + std::unique_ptr<Cipher_Mode> dec(get_cipher_mode(cipher, DECRYPTION)); if(key_length == 0) key_length = dec->key_spec().maximum_keylength(); diff --git a/src/lib/pbkdf/pbkdf2/pbkdf2.cpp b/src/lib/pbkdf/pbkdf2/pbkdf2.cpp index fedf036a3..146dd15b0 100644 --- a/src/lib/pbkdf/pbkdf2/pbkdf2.cpp +++ b/src/lib/pbkdf/pbkdf2/pbkdf2.cpp @@ -96,7 +96,7 @@ PKCS5_PBKDF2::key_derivation(size_t key_len, avoids confusion, and likely some broken implementations break on getting completely randomly distributed values */ - if(iterations % 10000 == 0) + if(iterations % 1000 == 0) { auto time_taken = std::chrono::high_resolution_clock::now() - start; auto usec_taken = std::chrono::duration_cast<std::chrono::microseconds>(time_taken); diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index 82797094a..cea065a66 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -324,7 +324,7 @@ bool PK_Verifier::validate_signature(const secure_vector<byte>& msg, /* * PK_Key_Agreement Constructor */ -PK_Key_Agreement::PK_Key_Agreement(const PK_Key_Agreement_Key& key, +PK_Key_Agreement::PK_Key_Agreement(const Private_Key& key, const std::string& kdf_name) { m_op.reset(get_pk_op<PK_Ops::Key_Agreement>(key, kdf_name)); diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 55afbf0fa..7341cb9c8 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -381,8 +381,7 @@ class BOTAN_DLL PK_Key_Agreement * @param key the key to use * @param kdf name of the KDF to use (or 'Raw' for no KDF) */ - PK_Key_Agreement(const PK_Key_Agreement_Key& key, - const std::string& kdf); + PK_Key_Agreement(const Private_Key& key, const std::string& kdf); private: std::unique_ptr<PK_Ops::Key_Agreement> m_op; std::unique_ptr<KDF> m_kdf; diff --git a/src/lib/tls/tls_exceptn.h b/src/lib/tls/tls_exceptn.h index ae601b60b..509226094 100644 --- a/src/lib/tls/tls_exceptn.h +++ b/src/lib/tls/tls_exceptn.h @@ -21,14 +21,14 @@ namespace TLS { class BOTAN_DLL TLS_Exception : public Exception { public: - Alert::Type type() const noexcept { return alert_type; } + Alert::Type type() const { return m_alert_type; } TLS_Exception(Alert::Type type, const std::string& err_msg = "Unknown error") : - Exception(err_msg), alert_type(type) {} + Exception(err_msg), m_alert_type(type) {} private: - Alert::Type alert_type; + Alert::Type m_alert_type; }; /** diff --git a/src/python/__init__.py b/src/python/__init__.py deleted file mode 100644 index f98b5a0ec..000000000 --- a/src/python/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from _botan import * diff --git a/src/python/botan.py b/src/python/botan.py new file mode 100755 index 000000000..8414308ec --- /dev/null +++ b/src/python/botan.py @@ -0,0 +1,517 @@ +#!/usr/bin/python + +""" +Python wrapper of the botan crypto library +http://botan.randombit.net + +(C) 2015 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import sys +from ctypes import * + +""" +Module initialization +""" +botan = CDLL('libbotan-1.11.so') + +expected_api_rev = 20150210 +botan_api_rev = botan.botan_ffi_api_version() + +if botan_api_rev != expected_api_rev: + raise Exception("Bad botan API rev got %d expected %d" % (botan_api_rev, expected_api_rev)) + +""" +Versions +""" +def version_major(): + return botan.botan_version_major() + +def version_minor(): + return botan.botan_version_minor() + +def version_patch(): + return botan.botan_version_patch() + +def version_string(): + botan.botan_version_string.restype = c_char_p + return botan.botan_version_string() + +""" +RNG +""" +class rng(object): + # Can also use type "system" + def __init__(self, rng_type = 'user'): + botan.botan_rng_init.argtypes = [c_void_p, c_char_p] + self.rng = c_void_p(0) + rc = botan.botan_rng_init(byref(self.rng), rng_type) + if rc != 0 or self.rng is None: + raise Exception("No rng " + algo + " for you!") + + def __del__(self): + botan.botan_rng_destroy.argtypes = [c_void_p] + botan.botan_rng_destroy(self.rng) + + def reseed(self, bits = 256): + botan.botan_rng_reseed.argtypes = [c_void_p, c_size_t] + botan.botan_rng_reseed(self.rng, bits) + + def get(self, length): + botan.botan_rng_get.argtypes = [c_void_p, POINTER(c_char), c_size_t] + out = create_string_buffer(length) + l = c_size_t(length) + rc = botan.botan_rng_get(self.rng, out, l) + return str(out.raw) + +""" +Hash function +""" +class hash_function(object): + def __init__(self, algo): + botan.botan_hash_init.argtypes = [c_void_p, c_char_p, c_uint32] + flags = 0 # always zero in this API version + self.hash = c_void_p(0) + rc = botan.botan_hash_init(byref(self.hash), algo, flags) + if rc != 0 or self.hash is None: + raise Exception("No hash " + algo + " for you!") + + def __del__(self): + botan.botan_hash_destroy.argtypes = [c_void_p] + botan.botan_hash_destroy(self.hash) + + def clear(self): + botan.botan_hash_clear.argtypes = [c_void_p] + return botan.botan_hash_clear(self.hash) + + def output_length(self): + botan.botan_hash_output_length.argtypes = [c_void_p,POINTER(c_size_t)] + l = c_size_t(0) + rc = botan.botan_hash_output_length(self.hash, byref(l)) + return l.value + + def update(self, x): + botan.botan_hash_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_hash_update(self.hash, x, len(x)) + + def final(self): + botan.botan_hash_final.argtypes = [c_void_p, POINTER(c_char)] + out = create_string_buffer(self.output_length()) + botan.botan_hash_final(self.hash, out) + return str(out.raw) + +""" +Message authentication codes +""" +class message_authentication_code(object): + def __init__(self, algo): + botan.botan_mac_init.argtypes = [c_void_p, c_char_p, c_uint32] + flags = 0 # always zero in this API version + self.mac = c_void_p(0) + rc = botan.botan_mac_init(byref(self.mac), algo, flags) + if rc != 0 or self.hash is None: + raise Exception("No mac " + algo + " for you!") + + def __del__(self): + botan.botan_mac_destroy.argtypes = [c_void_p] + botan.botan_mac_destroy(self.mac) + + def clear(self): + botan.botan_mac_clear.argtypes = [c_void_p] + return botan.botan_mac_clear(self.mac) + + def output_length(self): + botan.botan_mac_output_length.argtypes = [c_void_p, POINTER(c_size_t)] + l = c_size_t(0) + rc = botan.botan_mac_output_length(self.mac, byref(l)) + return l.value + + def set_key(self, key): + botan.botan_mac_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t] + return botan.botan_mac_set_key(self.mac, k, len(k)) + + def update(self, x): + botan.botan_mac_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_mac_update(self.mac, x, len(x)) + + def final(self): + botan.botan_mac_final.argtypes = [c_void_p, POINTER(c_char)] + out = create_string_buffer(self.output_length()) + botan.botan_mac_final(self.mac, out) + return str(out.raw) + +class cipher(object): + def __init__(self, algo, encrypt = True): + botan.botan_cipher_init.argtypes = [c_void_p,c_char_p, c_uint32] + flags = 0 if encrypt else 1 + self.cipher = c_void_p(0) + rc = botan.botan_cipher_init(byref(self.cipher), algo, flags) + if rc != 0 or self.cipher is None: + raise Exception("No cipher " + algo + " for you!") + + def __del__(self): + botan.botan_cipher_destroy.argtypes = [c_void_p] + botan.botan_cipher_destroy(self.cipher) + + def tag_length(self): + botan.botan_cipher_tag_length.argtypes = [c_void_p,POINTER(c_size_t)] + l = c_size_t(0) + botan.botan_cipher_tag_size(self.cipher, byref(l)) + return l.value + + def default_nonce_length(self): + botan.botan_cipher_default_nonce_length.argtypes = [c_void_p, POINTER(c_size_t)] + l = c_size_t(0) + botan.botan_cipher_default_nonce_length(self.cipher, byref(l)) + return l.value + + def update_granularity(self): + botan.botan_cipher_update_granularity.argtypes = [c_void_p, POINTER(c_size_t)] + l = c_size_t(0) + botan.botan_cipher_update_granularity(self.cipher, byref(l)) + return l.value + + def tag_length(self): + botan.botan_cipher_get_tag_length.argtypes = [c_void_p, POINTER(c_size_t)] + l = c_size_t(0) + botan.botan_cipher_get_tag_length(self.cipher, byref(l)) + return l.value + + def is_authenticated(self): + return self.tag_length() > 0 + + def valid_nonce_length(self, nonce_len): + botan.botan_cipher_valid_nonce_length.argtypes = [c_void_p, c_size_t] + rc = botan.botan_cipher_valid_nonce_length(self.cipher, nonce_len) + if rc < 0: + raise Exception('Error calling valid_nonce_length') + return True if rc == 1 else False + + def clear(self): + botan.botan_cipher_clear.argtypes = [c_void_p] + botan.botan_cipher_clear(self.cipher) + + def set_key(self, key): + botan.botan_cipher_set_key.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_cipher_set_key(self.cipher, key, len(key)) + + + def start(self, nonce): + botan.botan_cipher_start.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_cipher_start(self.cipher, nonce, len(nonce)) + + def _update(self, txt, final): + botan.botan_cipher_update.argtypes = [c_void_p, c_uint32, + POINTER(c_char), c_size_t, POINTER(c_size_t), + POINTER(c_char), c_size_t, POINTER(c_size_t)] + + inp = txt if txt else '' + inp_sz = c_size_t(len(inp)) + inp_consumed = c_size_t(0) + out = create_string_buffer(inp_sz.value + (self.tag_length() if final else 0)) + out_sz = c_size_t(len(out)) + out_written = c_size_t(0) + flags = c_uint32(1 if final else 0) + + botan.botan_cipher_update(self.cipher, flags, + out, out_sz, byref(out_written), + inp, inp_sz, byref(inp_consumed)) + + # buffering not supported yet + assert inp_consumed.value == inp_sz.value + return out.raw[0:out_written.value] + + def update(self, txt): + return self._update(txt, False) + + def finish(self, txt = None): + return self._update(txt, True) + + +""" +Bcrypt +TODO: might not be enabled - handle that gracefully! +""" +def generate_bcrypt(passwd, rng, work_factor = 10): + botan.botan_bcrypt_generate.argtypes = [POINTER(c_char), c_size_t, c_char_p, c_void_p, c_size_t] + out = create_string_buffer(61) + rc = botan.botan_bcrypt_generate(out, sizeof(out), passwd, rng.rng, c_size_t(work_factor)) + + if rc != 0: + raise Exception('botan bcrypt failed, error %s' % (rc)) + return str(out.raw) + +def check_bcrypt(passwd, bcrypt): + rc = botan.botan_bcrypt_is_valid(passwd, bcrypt) + return (rc == 0) + +""" +PBKDF +""" +def pbkdf(algo, password, out_len, iterations, salt): + botan.botan_pbkdf.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p, c_void_p, c_size_t, c_size_t] + out_buf = create_string_buffer(out_len) + botan.botan_pbkdf(algo, out_buf, out_len, password, salt, len(salt), iterations) + return out_buf.raw + +def pbkdf_timed(algo, password, out_len, rng, ms_to_run, salt_len = 12): + botan.botan_pbkdf_timed.argtypes = [c_char_p, POINTER(c_char), c_size_t, c_char_p, + c_void_p, c_size_t, c_size_t, POINTER(c_size_t)] + out_buf = create_string_buffer(out_len) + salt = rng.get(salt_len) + iterations = c_size_t(0) + botan.botan_pbkdf_timed(algo, out_buf, out_len, password, salt, len(salt), ms_to_run, byref(iterations)) + return (salt,iterations.value,out_buf.raw) + +""" +KDF +""" +def kdf(algo, secret, out_len, salt): + botan.botan_kdf.argtypes = [c_char_p, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t, POINTER(c_char), c_size_t] + out_buf = create_string_buffer(out_len) + out_sz = c_size_t(out_len) + botan.botan_kdf(algo, out_buf, out_sz, secret, len(secret), salt, len(salt)) + return out_buf.raw[0:out_sz.value] + +""" +Public and private keys +""" +class public_key(object): + def __init__(self, obj = c_void_p(0)): + self.pubkey = obj + + def __del__(self): + botan.botan_pubkey_destroy.argtypes = [c_void_p] + botan.botan_pubkey_destroy(self.pubkey) + + def fingerprint(self, hash = 'SHA-256'): + botan.botan_pubkey_fingerprint.argtypes = [c_void_p, c_char_p, + POINTER(c_char), POINTER(c_size_t)] + + n = hash_function(hash).output_length() + buf = create_string_buffer(n) + buf_len = c_size_t(n) + botan.botan_pubkey_fingerprint(self.pubkey, hash, buf, byref(buf_len)) + return buf[0:buf_len.value].encode('hex') + +class private_key(object): + def __init__(self, alg, param, rng): + botan.botan_privkey_create_rsa.argtypes = [c_void_p, c_void_p, c_size_t] + botan.botan_privkey_create_ecdsa.argtypes = [c_void_p, c_void_p, c_char_p] + botan.botan_privkey_create_ecdh.argtypes = [c_void_p, c_void_p, c_char_p] + + self.privkey = c_void_p(0) + if alg == 'rsa': + botan.botan_privkey_create_rsa(byref(self.privkey), rng.rng, param) + elif alg == 'ecdsa': + botan.botan_privkey_create_ecdsa(byref(self.privkey), rng.rng, param) + elif alg == 'ecdh': + botan.botan_privkey_create_ecdh(byref(self.privkey), rng.rng, param) + else: + raise Exception('Unknown public key algo ' + alg) + + if self.privkey is None: + raise Exception('Error creating ' + alg + ' key') + + def __del__(self): + botan.botan_privkey_destroy.argtypes = [c_void_p] + botan.botan_privkey_destroy(self.privkey) + + def get_public_key(self): + botan.botan_privkey_export_pubkey.argtypes = [c_void_p, c_void_p] + + pub = c_void_p(0) + botan.botan_privkey_export_pubkey(byref(pub), self.privkey) + return public_key(pub) + + def export(self): + botan.botan_privkey_export.argtypes = [c_void_p,POINTER(c_char),c_void_p] + + n = 4096 + buf = create_string_buffer(n) + buf_len = c_size_t(n) + + rc = botan.botan_privkey_export(self.privkey, buf, byref(buf_len)) + if rc != 0: + buf = create_string_buffer(buf_len.value) + botan.botan_privkey_export(self.privkey, buf, byref(buf_len)) + return buf[0:buf_len.value] + + +class pk_op_encrypt(object): + def __init__(self, key, padding, rng): + botan.botan_pk_op_encrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] + self.op = c_void_p(0) + flags = 0 # always zero in this ABI + botan.botan_pk_op_encrypt_create(byref(self.op), key.pubkey, padding, flags) + if not self.op: + raise Exception("No pk op for you") + + def __del__(self): + botan.botan_pk_op_encrypt_destroy.argtypes = [c_void_p] + botan.botan_pk_op_encrypt_destroy(self.op) + + def encrypt(self, msg, rng): + botan.botan_pk_op_encrypt.argtypes = [c_void_p, c_void_p, + POINTER(c_char), POINTER(c_size_t), + POINTER(c_char), c_size_t] + + outbuf_sz = c_size_t(4096) #?!?! + outbuf = create_string_buffer(outbuf_sz.value) + botan.botan_pk_op_encrypt(self.op, rng.rng, outbuf, byref(outbuf_sz), msg, len(msg)) + return outbuf.raw[0:outbuf_sz.value] + +class pk_op_decrypt(object): + def __init__(self, key, padding): + botan.botan_pk_op_decrypt_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] + self.op = c_void_p(0) + flags = 0 # always zero in this ABI + botan.botan_pk_op_decrypt_create(byref(self.op), key.privkey, padding, flags) + if not self.op: + raise Exception("No pk op for you") + + def __del__(self): + botan.botan_pk_op_decrypt_destroy.argtypes = [c_void_p] + botan.botan_pk_op_decrypt_destroy(self.op) + + def decrypt(self, msg): + botan.botan_pk_op_decrypt.argtypes = [c_void_p, + POINTER(c_char), POINTER(c_size_t), + POINTER(c_char), c_size_t] + + outbuf_sz = c_size_t(4096) #?!?! + outbuf = create_string_buffer(outbuf_sz.value) + botan.botan_pk_op_decrypt(self.op, outbuf, byref(outbuf_sz), msg, len(msg)) + return outbuf.raw[0:outbuf_sz.value] + +class pk_op_sign(object): + def __init__(self, key, padding): + botan.botan_pk_op_sign_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] + self.op = c_void_p(0) + flags = 0 # always zero in this ABI + botan.botan_pk_op_sign_create(byref(self.op), key.privkey, padding, flags) + if not self.op: + raise Exception("No pk op for you") + + def __del__(self): + botan.botan_pk_op_sign_destroy.argtypes = [c_void_p] + botan.botan_pk_op_sign_destroy(self.op) + + def update(self, msg): + botan.botan_pk_op_sign_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_pk_op_sign_update(self.op, msg, len(msg)) + + def finish(self, rng): + botan.botan_pk_op_sign_finish.argtypes = [c_void_p, c_void_p, POINTER(c_char), POINTER(c_size_t)] + outbuf_sz = c_size_t(4096) #?!?! + outbuf = create_string_buffer(outbuf_sz.value) + botan.botan_pk_op_sign_finish(self.op, rng.rng, outbuf, byref(outbuf_sz)) + return outbuf.raw[0:outbuf_sz.value] + +class pk_op_verify(object): + def __init__(self, key, padding): + botan.botan_pk_op_verify_create.argtypes = [c_void_p, c_void_p, c_char_p, c_uint32] + self.op = c_void_p(0) + flags = 0 # always zero in this ABI + botan.botan_pk_op_verify_create(byref(self.op), key.pubkey, padding, flags) + if not self.op: + raise Exception("No pk op for you") + + def __del__(self): + botan.botan_pk_op_verify_destroy.argtypes = [c_void_p] + botan.botan_pk_op_verify_destroy(self.op) + + def update(self, msg): + botan.botan_pk_op_verify_update.argtypes = [c_void_p, POINTER(c_char), c_size_t] + botan.botan_pk_op_verify_update(self.op, msg, len(msg)) + + def check_signature(self, signature): + botan.botan_pk_op_verify_finish.argtypes = [c_void_p, POINTER(c_char), c_size_t] + rc = botan.botan_pk_op_verify_finish(self.op, signature, len(signature)) + if rc == 0: + return True + return False + +""" +Tests and examples +""" +def test(): + print version_string() + print version_major(), version_minor(), version_patch() + + + print kdf('KDF2(SHA-1)', '701F3480DFE95F57941F804B1B2413EF'.decode('hex'), 7, '55A4E9DD5F4CA2EF82'.decode('hex')).encode('hex') + + print pbkdf('PBKDF2(SHA-1)', '', 32, 10000, '0001020304050607'.decode('hex')).encode('hex').upper() + print '59B2B1143B4CB1059EC58D9722FB1C72471E0D85C6F7543BA5228526375B0127' + + r = rng("user") + (salt,iterations,psk) = pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, r, 200, 12) + print salt.encode('hex'), iterations + print 'x', psk.encode('hex') + print 'y', pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt).encode('hex') + + print r.get(42).encode('hex'), r.get(13).encode('hex'), r.get(9).encode('hex') + + h = hash_function('MD5') + assert h.output_length() == 16 + h.update('h') + h.update('i') + print "md5", h.final().encode('hex') + + gcm = cipher('AES-128/GCM') + gcm.set_key('00000000000000000000000000000000'.decode('hex')) + gcm.start('000000000000000000000000'.decode('hex')) + gcm.update('') + gcm.update('') + print 'gcm', gcm.finish('00000000000000000000000000000000'.decode('hex')).encode('hex') + + rsapriv = private_key('rsa', 1536, r) + + dec = pk_op_decrypt(rsapriv, "EME1(SHA-256)") + + rsapub = rsapriv.get_public_key() + print rsapub.fingerprint("SHA-1") + + enc = pk_op_encrypt(rsapub, "EME1(SHA-256)", r) + + ctext = enc.encrypt('foof', r) + print ctext.encode('hex') + print dec.decrypt(ctext) + + signer = pk_op_sign(rsapriv, 'EMSA4(SHA-384)') + + signer.update('mess') + signer.update('age') + sig = signer.finish(r) + + r.reseed(200) + print sig.encode('hex') + + verify = pk_op_verify(rsapub, 'EMSA4(SHA-384)') + + verify.update('mess') + verify.update('age') + print "correct sig accepted?", verify.check_signature(sig) + + verify.update('mess of things') + verify.update('age') + print "bad sig accepted?", verify.check_signature(sig) + + key = private_key('ecdsa', 'secp256r1', r) + blob = key.export() + + #f = open('key.ber','wb') + #f.write(blob) + #f.close() + + +def main(args = None): + if args is None: + args = sys.argv + test() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/python/core.cpp b/src/python/core.cpp deleted file mode 100644 index cb395ee60..000000000 --- a/src/python/core.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* -* Boost.Python module definition -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/pipe.h> -#include <botan/lookup.h> -#include <botan/cryptobox.h> -#include <botan/pbkdf2.h> -#include <botan/hmac.h> -using namespace Botan; - -#include "python_botan.h" - -class Py_Cipher - { - public: - Py_Cipher(std::string algo_name, std::string direction, - std::string key); - - std::string cipher_noiv(const std::string& text); - - std::string cipher(const std::string& text, - const std::string& iv); - - std::string name() const { return algo_name; } - private: - std::string algo_name; - Keyed_Filter* filter; - Pipe pipe; - }; - -std::string Py_Cipher::cipher(const std::string& input, - const std::string& iv_str) - { - if(iv_str.size()) - { - const byte* iv_bytes = reinterpret_cast<const byte*>(iv_str.data()); - u32bit iv_len = iv_str.size(); - filter->set_iv(InitializationVector(iv_bytes, iv_len)); - } - - pipe.process_msg(input); - return pipe.read_all_as_string(Pipe::LAST_MESSAGE); - } - -// For IV-less algorithms -std::string Py_Cipher::cipher_noiv(const std::string& input) - { - pipe.process_msg(input); - return pipe.read_all_as_string(Pipe::LAST_MESSAGE); - } - -Py_Cipher::Py_Cipher(std::string algo_name, - std::string direction, - std::string key_str) - { - const byte* key_bytes = reinterpret_cast<const byte*>(key_str.data()); - u32bit key_len = key_str.size(); - - Cipher_Dir dir; - - if(direction == "encrypt") - dir = ENCRYPTION; - else if(direction == "decrypt") - dir = DECRYPTION; - else - throw std::invalid_argument("Bad cipher direction " + direction); - - filter = get_cipher(algo_name, dir); - filter->set_key(SymmetricKey(key_bytes, key_len)); - pipe.append(filter); - } - -class Py_HashFunction - { - public: - Py_HashFunction(const std::string& algo_name) - { - hash = get_hash(algo_name); - } - - ~Py_HashFunction() { delete hash; } - - void update(const std::string& input) - { - hash->update(input); - } - - std::string final() - { - std::string out(output_length(), 0); - hash->final(reinterpret_cast<byte*>(&out[0])); - return out; - } - - std::string name() const - { - return hash->name(); - } - - u32bit output_length() const - { - return hash->output_length(); - } - - private: - HashFunction* hash; - }; - -class Py_MAC - { - public: - - Py_MAC(const std::string& name, const std::string& key_str) - { - mac = get_mac(name); - - mac->set_key(reinterpret_cast<const byte*>(key_str.data()), - key_str.size()); - } - - ~Py_MAC() { delete mac; } - - u32bit output_length() const { return mac->output_length(); } - - std::string name() const { return mac->name(); } - - void update(const std::string& in) { mac->update(in); } - - std::string final() - { - std::string out(output_length(), 0); - mac->final(reinterpret_cast<byte*>(&out[0])); - return out; - } - private: - MessageAuthenticationCode* mac; - }; - -std::string cryptobox_encrypt(const std::string& in, - const std::string& passphrase, - Python_RandomNumberGenerator& rng) - { - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - - return CryptoBox::encrypt(in_bytes, in.size(), - passphrase, rng.get_underlying_rng()); - } - -std::string cryptobox_decrypt(const std::string& in, - const std::string& passphrase) - { - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - - return CryptoBox::decrypt(in_bytes, in.size(), - passphrase); - } - -std::string python_pbkdf2(const std::string& passphrase, - const std::string& salt, - u32bit iterations, - u32bit output_size, - const std::string& hash_fn) - { - PKCS5_PBKDF2 pbkdf2(new HMAC(get_hash(hash_fn))); - - return make_string( - pbkdf2.derive_key(output_size, - passphrase, - reinterpret_cast<const byte*>(salt.data()), - salt.size(), - iterations).bits_of()); - } - -std::string python_kdf2(const std::string& param, - const std::string& masterkey, - u32bit outputlength) - { - std::unique_ptr<KDF> kdf(get_kdf("KDF2(SHA-1)")); - - return make_string( - kdf->derive_key(outputlength, - reinterpret_cast<const byte*>(masterkey.data()), - masterkey.length(), - param)); - } - -BOOST_PYTHON_MODULE(_botan) - { - python::class_<Python_RandomNumberGenerator>("RandomNumberGenerator") - .def(python::init<>()) - .def("__str__", &Python_RandomNumberGenerator::name) - .def("name", &Python_RandomNumberGenerator::name) - .def("reseed", &Python_RandomNumberGenerator::reseed) - .def("add_entropy", &Python_RandomNumberGenerator::add_entropy) - .def("gen_random_byte", &Python_RandomNumberGenerator::gen_random_byte) - .def("gen_random", &Python_RandomNumberGenerator::gen_random); - - python::class_<Py_Cipher, boost::noncopyable> - ("Cipher", python::init<std::string, std::string, std::string>()) - .def("name", &Py_Cipher::name) - .def("cipher", &Py_Cipher::cipher) - .def("cipher", &Py_Cipher::cipher_noiv); - - python::class_<Py_HashFunction, boost::noncopyable> - ("HashFunction", python::init<std::string>()) - .def("update", &Py_HashFunction::update) - .def("final", &Py_HashFunction::final) - .def("name", &Py_HashFunction::name) - .def("output_length", &Py_HashFunction::output_length); - - python::class_<Py_MAC, boost::noncopyable> - ("MAC", python::init<std::string, std::string>()) - .def("update", &Py_MAC::update) - .def("final", &Py_MAC::final) - .def("name", &Py_MAC::name) - .def("output_length", &Py_MAC::output_length); - - python::def("cryptobox_encrypt", cryptobox_encrypt); - python::def("cryptobox_decrypt", cryptobox_decrypt); - python::def("pbkdf2", python_pbkdf2); - python::def("derive_key", python_kdf2); - - export_filters(); - export_rsa(); - export_x509(); - } diff --git a/src/python/filter.cpp b/src/python/filter.cpp deleted file mode 100644 index dc02a05e1..000000000 --- a/src/python/filter.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* -* Boost.Python module definition -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <boost/python.hpp> -using namespace boost::python; - -#include <botan/pipe.h> -#include <botan/lookup.h> -using namespace Botan; - -class Py_Filter : public Filter - { - public: - virtual void write_str(const std::string&) = 0; - - std::string name() const { return "Py_Filter_FIXME"; } - - void write(const byte data[], size_t length) - { - write_str(std::string((const char*)data, length)); - } - - void send_str(const std::string& str) - { - send((const byte*)str.data(), str.length()); - } - }; - -class FilterWrapper : public Py_Filter, public wrapper<Py_Filter> - { - public: - void start_msg() - { - if(override start_msg = this->get_override("start_msg")) - start_msg(); - } - - void end_msg() - { - if(override end_msg = this->get_override("end_msg")) - end_msg(); - } - - void default_start_msg() {} - void default_end_msg() {} - - virtual void write_str(const std::string& str) - { - this->get_override("write")(str); - } - }; - -Filter* return_or_raise(Filter* filter, const std::string& name) - { - if(filter) - return filter; - throw Invalid_Argument("Filter " + name + " could not be found"); - } - -Filter* make_filter1(const std::string& name) - { - Filter* filter = 0; - - if(have_hash(name)) filter = new Hash_Filter(name); - else if(name == "Hex_Encoder") filter = new Hex_Encoder; - else if(name == "Hex_Decoder") filter = new Hex_Decoder; - else if(name == "Base64_Encoder") filter = new Base64_Encoder; - else if(name == "Base64_Decoder") filter = new Base64_Decoder; - - return return_or_raise(filter, name); - } - -Filter* make_filter2(const std::string& name, - const SymmetricKey& key) - { - Filter* filter = 0; - - if(have_mac(name)) - filter = new MAC_Filter(name, key); - else if(have_stream_cipher(name)) - filter = new StreamCipher_Filter(name, key); - - return return_or_raise(filter, name); - } - -// FIXME: add new wrapper for Keyed_Filter here -Filter* make_filter3(const std::string& name, - const SymmetricKey& key, - Cipher_Dir direction) - { - return return_or_raise( - get_cipher(name, key, direction), - name); - } - -Filter* make_filter4(const std::string& name, - const SymmetricKey& key, - const InitializationVector& iv, - Cipher_Dir direction) - { - return return_or_raise( - get_cipher(name, key, iv, direction), - name); - } - -void append_filter(Pipe& pipe, std::auto_ptr<Filter> filter) - { - pipe.append(filter.get()); - filter.release(); - } - -void prepend_filter(Pipe& pipe, std::auto_ptr<Filter> filter) - { - pipe.prepend(filter.get()); - filter.release(); - } - -void do_send(std::auto_ptr<FilterWrapper> filter, const std::string& data) - { - filter->send_str(data); - } - -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(rallas_ovls, read_all_as_string, 0, 1) - -void export_filters() - { - class_<Filter, std::auto_ptr<Filter>, boost::noncopyable> - ("__Internal_FilterObj", no_init); - - def("make_filter", make_filter1, - return_value_policy<manage_new_object>()); - def("make_filter", make_filter2, - return_value_policy<manage_new_object>()); - def("make_filter", make_filter3, - return_value_policy<manage_new_object>()); - def("make_filter", make_filter4, - return_value_policy<manage_new_object>()); - - // This might not work - Pipe will delete the filter, but Python - // might have allocated the space with malloc() or who-knows-what -> bad - class_<FilterWrapper, std::auto_ptr<FilterWrapper>, - bases<Filter>, boost::noncopyable> - ("FilterObj") - .def("write", pure_virtual(&Py_Filter::write_str)) - .def("send", &do_send) - .def("start_msg", &Filter::start_msg, &FilterWrapper::default_start_msg) - .def("end_msg", &Filter::end_msg, &FilterWrapper::default_end_msg); - - implicitly_convertible<std::auto_ptr<FilterWrapper>, - std::auto_ptr<Filter> >(); - - void (Pipe::*pipe_write_str)(const std::string&) = &Pipe::write; - void (Pipe::*pipe_process_str)(const std::string&) = &Pipe::process_msg; - - class_<Pipe, boost::noncopyable>("PipeObj") - .def(init<>()) - /* - .def_readonly("LAST_MESSAGE", &Pipe::LAST_MESSAGE) - .def_readonly("DEFAULT_MESSAGE", &Pipe::DEFAULT_MESSAGE) - */ - .add_property("default_msg", &Pipe::default_msg, &Pipe::set_default_msg) - .add_property("msg_count", &Pipe::message_count) - .def("append", append_filter) - .def("prepend", prepend_filter) - .def("reset", &Pipe::reset) - .def("pop", &Pipe::pop) - .def("end_of_data", &Pipe::end_of_data) - .def("start_msg", &Pipe::start_msg) - .def("end_msg", &Pipe::end_msg) - .def("write", pipe_write_str) - .def("process_msg", pipe_process_str) - .def("read_all", &Pipe::read_all_as_string, rallas_ovls()); - } diff --git a/src/python/python_botan.h b/src/python/python_botan.h deleted file mode 100644 index c4ee6a9e0..000000000 --- a/src/python/python_botan.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -* (C) 2009 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_BOOST_PYTHON_COMMON_H__ -#define BOTAN_BOOST_PYTHON_COMMON_H__ - -#include <botan/exceptn.h> -#include <botan/parsing.h> -#include <botan/secmem.h> -using namespace Botan; - -#include <boost/python.hpp> -namespace python = boost::python; - -extern void export_filters(); -extern void export_rsa(); -extern void export_x509(); - -class Bad_Size : public Exception - { - public: - Bad_Size(u32bit got, u32bit expected) : - Exception("Bad size detected in Python/C++ conversion layer: got " + - std::to_string(got) + " bytes, expected " + - std::to_string(expected)) - {} - }; - -inline std::string make_string(const byte input[], u32bit length) - { - return std::string((const char*)input, length); - } - -template<typename Alloc> -inline std::string make_string(const std::vector<byte, Alloc>& in) - { - return make_string(&in[0], in.size()); - } - -inline void string2binary(const std::string& from, byte to[], u32bit expected) - { - if(from.size() != expected) - throw Bad_Size(from.size(), expected); - std::memcpy(to, from.data(), expected); - } - -template<typename T> -inline python::object get_owner(T* me) - { - return python::object( - python::handle<>( - python::borrowed(python::detail::wrapper_base_::get_owner(*me)))); - } - -class Python_RandomNumberGenerator - { - public: - Python_RandomNumberGenerator() - { rng = RandomNumberGenerator::make_rng(); } - ~Python_RandomNumberGenerator() { delete rng; } - - std::string name() const { return rng->name(); } - - void reseed() { rng->reseed(192); } - - int gen_random_byte() { return rng->next_byte(); } - - std::string gen_random(int n) - { - std::string s(n, 0); - rng->randomize(reinterpret_cast<byte*>(&s[0]), n); - return s; - } - - void add_entropy(const std::string& in) - { rng->add_entropy(reinterpret_cast<const byte*>(in.c_str()), in.length()); } - - RandomNumberGenerator& get_underlying_rng() { return *rng; } - private: - RandomNumberGenerator* rng; - }; - -#endif diff --git a/src/python/rsa.cpp b/src/python/rsa.cpp deleted file mode 100644 index 4ba19991c..000000000 --- a/src/python/rsa.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* -* Boost.Python module definition -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/rsa.h> -#include <botan/pubkey.h> -#include <botan/x509_key.h> -using namespace Botan; - -#include "python_botan.h" -#include <sstream> - -std::string bigint2str(const BigInt& n) - { - std::ostringstream out; - out << n; - return out.str(); - } - -class Py_RSA_PrivateKey - { - public: - Py_RSA_PrivateKey(std::string pem_str, - Python_RandomNumberGenerator& rng, - std::string pass); - Py_RSA_PrivateKey(std::string pem_str, - Python_RandomNumberGenerator& rng); - - Py_RSA_PrivateKey(u32bit bits, Python_RandomNumberGenerator& rng); - ~Py_RSA_PrivateKey() { delete rsa_key; } - - std::string to_string() const - { - return PKCS8::PEM_encode(*rsa_key); - } - - std::string to_ber() const - { - secure_vector<byte> bits = PKCS8::BER_encode(*rsa_key); - return std::string(reinterpret_cast<const char*>(&bits[0]), bits.size()); - } - - std::string get_N() const { return bigint2str(get_bigint_N()); } - std::string get_E() const { return bigint2str(get_bigint_E()); } - - const BigInt& get_bigint_N() const { return rsa_key->get_n(); } - const BigInt& get_bigint_E() const { return rsa_key->get_e(); } - - std::string decrypt(const std::string& in, - const std::string& padding); - - std::string sign(const std::string& in, - const std::string& padding, - Python_RandomNumberGenerator& rng); - private: - RSA_PrivateKey* rsa_key; - }; - -std::string Py_RSA_PrivateKey::decrypt(const std::string& in, - const std::string& padding) - { - PK_Decryptor_EME dec(*rsa_key, padding); - - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - - return make_string(dec.decrypt(in_bytes, in.size())); - } - -std::string Py_RSA_PrivateKey::sign(const std::string& in, - const std::string& padding, - Python_RandomNumberGenerator& rng) - { - PK_Signer sign(*rsa_key, padding); - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - sign.update(in_bytes, in.size()); - return make_string(sign.signature(rng.get_underlying_rng())); - } - -Py_RSA_PrivateKey::Py_RSA_PrivateKey(u32bit bits, - Python_RandomNumberGenerator& rng) - { - rsa_key = new RSA_PrivateKey(rng.get_underlying_rng(), bits); - } - -Py_RSA_PrivateKey::Py_RSA_PrivateKey(std::string pem_str, - Python_RandomNumberGenerator& rng) - { - DataSource_Memory in(pem_str); - - Private_Key* pkcs8_key = - PKCS8::load_key(in, - rng.get_underlying_rng()); - - rsa_key = dynamic_cast<RSA_PrivateKey*>(pkcs8_key); - - if(!rsa_key) - throw std::invalid_argument("Key is not an RSA key"); - } - -Py_RSA_PrivateKey::Py_RSA_PrivateKey(std::string pem_str, - Python_RandomNumberGenerator& rng, - std::string passphrase) - { - DataSource_Memory in(pem_str); - - Private_Key* pkcs8_key = - PKCS8::load_key(in, - rng.get_underlying_rng(), - passphrase); - - rsa_key = dynamic_cast<RSA_PrivateKey*>(pkcs8_key); - - if(!rsa_key) - throw std::invalid_argument("Key is not an RSA key"); - } - -class Py_RSA_PublicKey - { - public: - Py_RSA_PublicKey(std::string pem_str); - Py_RSA_PublicKey(const Py_RSA_PrivateKey&); - ~Py_RSA_PublicKey() { delete rsa_key; } - - std::string get_N() const { return bigint2str(get_bigint_N()); } - std::string get_E() const { return bigint2str(get_bigint_E()); } - - const BigInt& get_bigint_N() const { return rsa_key->get_n(); } - const BigInt& get_bigint_E() const { return rsa_key->get_e(); } - - std::string to_string() const - { - return X509::PEM_encode(*rsa_key); - } - - std::string to_ber() const - { - std::vector<byte> bits = X509::BER_encode(*rsa_key); - - return std::string(reinterpret_cast<const char*>(&bits[0]), - bits.size()); - } - - std::string encrypt(const std::string& in, - const std::string& padding, - Python_RandomNumberGenerator& rng); - - bool verify(const std::string& in, - const std::string& padding, - const std::string& signature); - private: - RSA_PublicKey* rsa_key; - }; - -Py_RSA_PublicKey::Py_RSA_PublicKey(const Py_RSA_PrivateKey& priv) - { - rsa_key = new RSA_PublicKey(priv.get_bigint_N(), priv.get_bigint_E()); - } - -Py_RSA_PublicKey::Py_RSA_PublicKey(std::string pem_str) - { - DataSource_Memory in(pem_str); - Public_Key* x509_key = X509::load_key(in); - - rsa_key = dynamic_cast<RSA_PublicKey*>(x509_key); - - if(!rsa_key) - throw std::invalid_argument("Key is not an RSA key"); - } - -std::string Py_RSA_PublicKey::encrypt(const std::string& in, - const std::string& padding, - Python_RandomNumberGenerator& rng) - { - PK_Encryptor_EME enc(*rsa_key, padding); - - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - - return make_string(enc.encrypt(in_bytes, in.size(), - rng.get_underlying_rng())); - } - -bool Py_RSA_PublicKey::verify(const std::string& in, - const std::string& signature, - const std::string& padding) - { - PK_Verifier ver(*rsa_key, padding); - - const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); - const byte* sig_bytes = reinterpret_cast<const byte*>(signature.data()); - - ver.update(in_bytes, in.size()); - return ver.check_signature(sig_bytes, signature.size()); - } - -void export_rsa() - { - python::class_<Py_RSA_PublicKey> - ("RSA_PublicKey", python::init<std::string>()) - .def(python::init<const Py_RSA_PrivateKey&>()) - .def("to_string", &Py_RSA_PublicKey::to_string) - .def("to_ber", &Py_RSA_PublicKey::to_ber) - .def("encrypt", &Py_RSA_PublicKey::encrypt) - .def("verify", &Py_RSA_PublicKey::verify) - .def("get_N", &Py_RSA_PublicKey::get_N) - .def("get_E", &Py_RSA_PublicKey::get_E); - - python::class_<Py_RSA_PrivateKey> - ("RSA_PrivateKey", python::init<std::string, Python_RandomNumberGenerator&, std::string>()) - .def(python::init<std::string, Python_RandomNumberGenerator&>()) - .def(python::init<u32bit, Python_RandomNumberGenerator&>()) - .def("to_string", &Py_RSA_PrivateKey::to_string) - .def("to_ber", &Py_RSA_PrivateKey::to_ber) - .def("decrypt", &Py_RSA_PrivateKey::decrypt) - .def("sign", &Py_RSA_PrivateKey::sign) - .def("get_N", &Py_RSA_PrivateKey::get_N) - .def("get_E", &Py_RSA_PrivateKey::get_E); - } diff --git a/src/python/x509.cpp b/src/python/x509.cpp deleted file mode 100644 index 48cfca2c7..000000000 --- a/src/python/x509.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -* Boost.Python module definition -* (C) 2009 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/oids.h> -#include <botan/pipe.h> -#include <botan/filters.h> -#include <botan/x509cert.h> -#include <botan/x509_crl.h> -using namespace Botan; - -#include <boost/python.hpp> -namespace python = boost::python; - -template<typename T> -class vector_to_list - { - public: - static PyObject* convert(const std::vector<T>& in) - { - python::list out; - typename std::vector<T>::const_iterator i = in.begin(); - while(i != in.end()) - { - out.append(*i); - ++i; - } - return python::incref(out.ptr()); - } - - vector_to_list() - { - python::to_python_converter<std::vector<T>, vector_to_list<T> >(); - } - }; - -template<typename T> -class memvec_to_hexstr - { - public: - static PyObject* convert(const T& in) - { - Pipe pipe(new Hex_Encoder); - pipe.process_msg(in); - std::string result = pipe.read_all_as_string(); - return python::incref(python::str(result).ptr()); - } - - memvec_to_hexstr() - { - python::to_python_converter<T, memvec_to_hexstr<T> >(); - } - }; - -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(add_cert_ols, add_cert, 1, 2) -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(validate_cert_ols, validate_cert, 1, 2) - -void export_x509() - { - vector_to_list<std::string>(); - vector_to_list<X509_Certificate>(); - memvec_to_hexstr<std::vector<byte> >(); - - python::class_<X509_Certificate> - ("X509_Certificate", python::init<std::string>()) - .def(python::self == python::self) - .def(python::self != python::self) - .add_property("version", &X509_Certificate::x509_version) - .add_property("is_CA", &X509_Certificate::is_CA_cert) - .add_property("self_signed", &X509_Certificate::is_self_signed) - .add_property("pathlimit", &X509_Certificate::path_limit) - .add_property("as_pem", &X509_Object::PEM_encode) - .def("start_time", &X509_Certificate::start_time) - .def("end_time", &X509_Certificate::end_time) - .def("subject_info", &X509_Certificate::subject_info) - .def("issuer_info", &X509_Certificate::issuer_info) - .def("ex_constraints", &X509_Certificate::ex_constraints) - .def("policies", &X509_Certificate::policies) - .def("subject_key_id", &X509_Certificate::subject_key_id) - .def("authority_key_id", &X509_Certificate::authority_key_id); - - python::class_<X509_CRL> - ("X509_CRL", python::init<std::string>()) - .add_property("as_pem", &X509_Object::PEM_encode); - } diff --git a/src/scripts/examples/cipher.py b/src/scripts/examples/cipher.py deleted file mode 100755 index 1be2759ae..000000000 --- a/src/scripts/examples/cipher.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -import botan -import sys - -def encrypt(input, passphrase): - rng = botan.RandomNumberGenerator() - - # Use as both EAX IV and PBKDF2 salt - salt = rng.gen_random(10) - - iterations = 10000 - output_size = 16 - - key = botan.pbkdf2(passphrase, salt, iterations, output_size, "SHA-1") - - encryptor = botan.Cipher("AES-128/EAX", "encrypt", key) - - ciphertext = encryptor.cipher(input, salt) - return (ciphertext, salt) - -def decrypt(input, salt, passphrase): - iterations = 10000 - output_size = 16 - - key = botan.pbkdf2(passphrase, salt, iterations, output_size, "SHA-1") - - decryptor = botan.Cipher("AES-128/EAX", "decrypt", key) - - return decryptor.cipher(input, salt) - -def main(args = None): - if args is None: - args = sys.argv - - passphrase = args[1] - input = ''.join(open(args[2]).readlines()) - - (ciphertext, salt) = encrypt(input, passphrase) - - print decrypt(ciphertext, salt, passphrase) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/src/scripts/examples/cryptobox.py b/src/scripts/examples/cryptobox.py deleted file mode 100755 index f76ed6bc3..000000000 --- a/src/scripts/examples/cryptobox.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python - -import sys -import botan - -def main(args = None): - if args is None: - args = sys.argv - - if len(args) != 3: - raise Exception("Usage: <password> <input>"); - - password = args[1] - input = ''.join(open(args[2]).readlines()) - - rng = botan.RandomNumberGenerator() - - ciphertext = botan.cryptobox_encrypt(input, password, rng) - - print ciphertext - - plaintext = '' - - try: - plaintext = botan.cryptobox_decrypt(ciphertext, password + 'FAIL') - except Exception, e: - print "Good news: bad password caused exception: " - print e - - plaintext = botan.cryptobox_decrypt(ciphertext, password) - - print "Original input was: " - print plaintext - -if __name__ == '__main__': - sys.exit(main()) diff --git a/src/scripts/examples/nisttest.py b/src/scripts/examples/nisttest.py deleted file mode 100755 index 1260b1226..000000000 --- a/src/scripts/examples/nisttest.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python - -import sys, os, botan -from os.path import join; - -def validate(ca_certs, certs, crls, ee_certs): - store = botan.X509_Store() - for cert in certs: - if cert not in ee_certs: - store.add_cert(botan.X509_Certificate(cert), cert in ca_certs) - - for crl in crls: - r = store.add_crl(botan.X509_CRL(crl)) - if r != botan.verify_result.verified: - return r - - for ee in ee_certs: - r = store.validate(botan.X509_Certificate(ee)) - if r != botan.verify_result.verified: - return r - - return botan.verify_result.verified - -def run_test(files, rootdir, testname, expected): - crls = [join(rootdir,x) for x in files if x.endswith(".crl")] - certs = [join(rootdir,x) for x in files if x.endswith(".crt")] - end_entity = [x for x in certs if x.find("end.crt") != -1] - ca_certs = [x for x in certs if x.find("root.crt") != -1] - - print "%s..." % testname, - - result = validate(ca_certs, certs, crls, end_entity) - result = repr(result).replace('botan._botan.verify_result.', '') - - if result != expected: - print "FAILED: got %s, expected %s" % (result, expected) - else: - print "passed" - -def main(): - def load_results(file): - results = {} - for line in open(file, 'r'): - line = line[0:line.find('#')].strip() - if line: - test,result = line.split(' ') - results[test] = result - return results - - results = load_results('results.vec') - - for root, dirs, files in os.walk('../../checks/nist_tests/tests'): - if files: - thistest = root[root.rfind('/')+1:] - if thistest in results: - run_test(files, root, thistest, results[thistest]) - else: - print "%s... skipping - no expected result set" % thistest - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/scripts/examples/results.vec b/src/scripts/examples/results.vec deleted file mode 100644 index 7a3824001..000000000 --- a/src/scripts/examples/results.vec +++ /dev/null @@ -1,60 +0,0 @@ -# This is the file of expected results for nisttest.py -test01 verified -test02 signature_error -test03 signature_error -test04 verified -test05 cert_not_yet_valid -test06 cert_not_yet_valid -test07 verified -test08 cert_not_yet_valid -test09 cert_has_expired -test10 cert_has_expired -test11 cert_has_expired -test12 verified -test13 cert_issuer_not_found -test14 cert_issuer_not_found -test15 verified -test16 verified -test17 verified -test18 verified -# changed; should be no_revocation_data_available, but I don't want to -# force people to use CRLs -test19 verified -test20 cert_is_revoked -test21 cert_is_revoked -test22 ca_cert_not_for_cert_issuer -test23 ca_cert_not_for_cert_issuer -test24 verified -test25 ca_cert_not_for_cert_issuer -test26 verified -test27 verified -test28 ca_cert_not_for_cert_issuer -test29 ca_cert_not_for_cert_issuer -test30 verified -test31 ca_cert_not_for_crl_issuer -test32 ca_cert_not_for_crl_issuer -test33 verified -test54 cert_chain_too_long -test55 cert_chain_too_long -test56 verified -test57 verified -test58 cert_chain_too_long -test59 cert_chain_too_long -test60 cert_chain_too_long -test61 cert_chain_too_long -test62 verified -test63 verified -test64 signature_error -# changed; I have no idea why this test is supposed to fail -test65 verified -test66 crl_issuer_not_found -# changed; one of the CRLs has an unknown creator, so we fail -# prior to getting to the end-entity check -test67 crl_issuer_not_found -test68 cert_is_revoked -test69 cert_is_revoked -test70 cert_is_revoked -test71 cert_is_revoked -test72 crl_has_expired -test73 crl_has_expired -test74 verified diff --git a/src/scripts/examples/rng_test.py b/src/scripts/examples/rng_test.py deleted file mode 100755 index 06c79b84e..000000000 --- a/src/scripts/examples/rng_test.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python - -import botan - -rng = botan.RandomNumberGenerator() - -print "name", rng.name() - -rng.add_entropy("blah") - -print "random 16", rng.gen_random(16).encode("hex") -print "random 32", rng.gen_random(32).encode("base64"), - -rng.reseed() - -for i in range(0, 10): - print rng.gen_random_byte(), -print - -rng.add_entropy("blah") - -print "random 16", rng.gen_random(16).encode("hex") diff --git a/src/scripts/examples/rsa.py b/src/scripts/examples/rsa.py deleted file mode 100755 index 998b72b7b..000000000 --- a/src/scripts/examples/rsa.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python - -import botan - -def make_into_c_array(ber): - output = 'static unsigned char key_data[%d] = {\n\t' % (len(ber)) - - for (idx,c) in zip(range(len(ber)), ber): - if idx != 0 and idx % 8 == 0: - output += "\n\t" - output += "0x%s, " % (c.encode('hex')) - - output += "\n};\n" - - return output - -rng = botan.RandomNumberGenerator() - -rsa_priv = botan.RSA_PrivateKey(1024, rng) - -print rsa_priv.to_string() -print int(rsa_priv.get_N()) -print int(rsa_priv.get_E()) - -rsa_pub = botan.RSA_PublicKey(rsa_priv) - -print make_into_c_array(rsa_pub.to_ber()) -#print make_into_c_array(rsa_priv.to_ber()) - -key = rng.gen_random(20) - -ciphertext = rsa_pub.encrypt(key, 'EME1(SHA-1)', rng) - -print ciphertext.encode('hex') - -plaintext = rsa_priv.decrypt(ciphertext, 'EME1(SHA-1)') - -print plaintext == key - -signature = rsa_priv.sign(key, 'EMSA4(SHA-256)', rng) - -print rsa_pub.verify(key, signature, 'EMSA4(SHA-256)') - -# Corrupt the signature, make sure it doesn't verify -signature = signature.replace(signature[0], '0') - -print rsa_pub.verify(key, signature, 'EMSA4(SHA-256)') diff --git a/src/tests/data/pbkdf/pbkdf2.vec b/src/tests/data/pbkdf/pbkdf2.vec index 88c2f894e..6a027721a 100644 --- a/src/tests/data/pbkdf/pbkdf2.vec +++ b/src/tests/data/pbkdf/pbkdf2.vec @@ -59,6 +59,27 @@ Passphrase = gwrxpqxumsdsmbmhfhmfdcvlcvngzkig OutputLen = 64 Output = 4C9DB7BA24955225D5B845F65EF24EF1B0C6E86F2E39C8DDAA4B8ABD26082D1F350381FADEAEB560DC447AFC68A6B47E6EA1E7412F6CF7B2D82342FCCD11D3B4 +[PBKDF2(SHA-256)] +Salt = 0001020304050607 +Iterations = 10000 +Passphrase = xyz +OutputLen = 48 +Output = DEFD2987FA26A4672F4D16D98398432AD95E896BF619F6A6B8D4ED1FAF98E8B531B39FFB66966D0E115A6CD8E70B72D0 + +[PBKDF2(SHA-384)] +Salt = 0001020304050607 +Iterations = 10000 +Passphrase = xyz +OutputLen = 48 +Output = 47A3AE920B24EDAA2BB53155808554B13FAB58DF62B81F043D9812E9F2881164DF20BBFFA54E5EE2489FA183B6718A74 + +[PBKDF2(SHA-512)] +Salt = 0001020304050607 +Iterations = 10000 +Passphrase = xyz +OutputLen = 48 +Output = DAF8A734327745EB63D19054DBD4018A682CEF11086A1BFB63FDBC16158C2F8B0742802F36AEF1B1DF92ACCBEA5D31A5 + [PBKDF2(CMAC(Blowfish))] Salt = 24A1A50B17D63EE8394B69FC70887F4F94883D68 Iterations = 5 |