diff options
author | lloyd <[email protected]> | 2015-02-16 20:12:38 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2015-02-16 20:12:38 +0000 |
commit | 3b9a0c1535e40f8f9fc4cfbc734144ee229df65d (patch) | |
tree | 30c1d4363b4c85561204d26344f40de3e78f6d9d | |
parent | 85caef829c9eeb7c224ad3b2e3ffbcfe981c2428 (diff) |
Add new module `ffi` which provides a plain C interface, plus a new
ctypes Python wrapper that uses it. The API is intentionally designed
to have a very simple ABI (extern "C", all structs are opaque, no
memory ownership passing the FFI boundary, limited set of simple types
as args) so the ctypes wrapper is quite simple.
Currently ffi provides ciphers, hashes, MACs, RNGs, PBKDF, KDF,
bcrypt, and most public key operations.
Remove the old boost.python wrapper and all the build code for it.
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 |