diff options
-rw-r--r-- | src/lib/ffi/ffi.cpp | 387 | ||||
-rw-r--r-- | src/lib/ffi/ffi.h | 120 | ||||
-rw-r--r-- | src/lib/ffi/info.txt | 3 | ||||
-rwxr-xr-x | src/python/botan2.py | 15 | ||||
-rw-r--r-- | src/tests/test_ffi.cpp | 233 |
5 files changed, 731 insertions, 27 deletions
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index 9c5b837fb..75b10502c 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -1,5 +1,5 @@ /* -* (C) 2015 Jack Lloyd +* (C) 2015,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -21,6 +21,10 @@ #include <botan/mem_ops.h> #include <botan/x509_key.h> #include <botan/pk_algs.h> +#include <botan/bigint.h> +#include <botan/reducer.h> +#include <botan/numthry.h> +#include <botan/divide.h> #include <cstring> #include <memory> @@ -94,7 +98,7 @@ void log_exception(const char* func_name, const char* what) int ffi_error_exception_thrown(const char* exn) { - printf("exception %s\n", exn); + fprintf(stderr, "exception %s\n", exn); return BOTAN_FFI_ERROR_EXCEPTION_THROWN; } @@ -108,6 +112,16 @@ T& safe_get(botan_struct<T,M>* p) throw FFI_Error("Invalid object pointer"); } +template<typename T, uint32_t M> +const T& safe_get(const botan_struct<T,M>* p) + { + if(!p) + throw FFI_Error("Null pointer argument"); + if(const T* t = p->get()) + return *t; + throw FFI_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) { @@ -167,6 +181,13 @@ inline int write_str_output(char out[], size_t* out_len, const std::string& str) return write_str_output(reinterpret_cast<uint8_t*>(out), out_len, str); } +inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec) + { + return write_output(reinterpret_cast<uint8_t*>(out), out_len, + reinterpret_cast<const uint8_t*>(str_vec.data()), + str_vec.size()); + } + #define BOTAN_FFI_DO(T, obj, param, block) apply_fn(obj, BOTAN_CURRENT_FUNCTION, [=](T& param) -> int { do { block } while(0); return 0; }) } @@ -183,6 +204,7 @@ struct botan_cipher_struct : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C> }; BOTAN_FFI_DECLARE_STRUCT(botan_rng_struct, Botan::RandomNumberGenerator, 0x4901F9C1); +BOTAN_FFI_DECLARE_STRUCT(botan_mp_struct, Botan::BigInt, 0xC828B9D2); BOTAN_FFI_DECLARE_STRUCT(botan_hash_struct, Botan::HashFunction, 0x1F0A4F84); BOTAN_FFI_DECLARE_STRUCT(botan_mac_struct, Botan::MessageAuthenticationCode, 0xA06E8FC1); BOTAN_FFI_DECLARE_STRUCT(botan_pubkey_struct, Botan::Public_Key, 0x2C286519); @@ -195,6 +217,7 @@ BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_ka_struct, Botan::PK_Key_Agreement, 0x2939C BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937); + #if defined(BOTAN_HAS_TLS) BOTAN_FFI_DECLARE_STRUCT(botan_tls_channel_struct, Botan::TLS::Channel, 0x0212FE99); #endif @@ -301,6 +324,203 @@ int botan_rng_reseed(botan_rng_t rng, size_t bits) return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); }); } +int botan_mp_init(botan_mp_t* mp) + { + *mp = new botan_mp_struct(new Botan::BigInt); + return 0; + } + +int botan_mp_set_from_int(botan_mp_t mp, int initial_value) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { + if(initial_value >= 0) + { + bn = Botan::BigInt(static_cast<uint64_t>(initial_value)); + } + else + { + bn = Botan::BigInt(static_cast<uint64_t>(-initial_value)); + bn.flip_sign(); + } + }); + } + +int botan_mp_set_from_str(botan_mp_t mp, const char* str) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn = Botan::BigInt(str); }); + } + +int botan_mp_set_from_mp(botan_mp_t dest, botan_mp_t source) + { + return BOTAN_FFI_DO(Botan::BigInt, dest, bn, { bn = safe_get(source); }); + } + +int botan_mp_is_negative(botan_mp_t mp) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_negative() ? 1 : 0; }); + } + +int botan_mp_flip_sign(botan_mp_t mp) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.flip_sign(); }); + } + +int botan_mp_from_bin(botan_mp_t mp, const uint8_t bin[], size_t bin_len) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_decode(bin, bin_len); }); + } + +int botan_mp_to_hex(botan_mp_t mp, char* out) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { + std::vector<uint8_t> hex = Botan::BigInt::encode(bn, Botan::BigInt::Hexadecimal); + std::memcpy(out, hex.data(), hex.size()); + out[hex.size()] = 0; // null terminate + }); + } + +int botan_mp_to_str(botan_mp_t mp, uint8_t digit_base, char* out, size_t* out_len) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { + Botan::BigInt::Base base; + if(digit_base == 0 || digit_base == 10) + base = Botan::BigInt::Decimal; + else if(digit_base == 16) + base = Botan::BigInt::Hexadecimal; + else + throw FFI_Error("botan_mp_to_str invalid digit base"); + + std::vector<uint8_t> hex = Botan::BigInt::encode(bn, base); + hex.push_back(0); // null terminator + write_str_output(out, out_len, hex); + }); + } + +int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[]) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_encode(vec); }); + } + +int botan_mp_destroy(botan_mp_t mp) + { + delete mp; + return 0; + } + +int botan_mp_add(botan_mp_t result, botan_mp_t x, botan_mp_t y) + { + return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) + safe_get(y); }); + } + +int botan_mp_sub(botan_mp_t result, botan_mp_t x, botan_mp_t y) + { + return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) - safe_get(y); }); + } + +int botan_mp_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y) + { + return BOTAN_FFI_DO(Botan::BigInt, result, res, { res = safe_get(x) * safe_get(y); }); + } + +int botan_mp_div(botan_mp_t quotient, + botan_mp_t remainder, + botan_mp_t x, botan_mp_t y) + { + return BOTAN_FFI_DO(Botan::BigInt, quotient, q, { + Botan::BigInt r; + Botan::divide(safe_get(x), safe_get(y), q, r); + safe_get(remainder) = r; + }); + } + +int botan_mp_equal(botan_mp_t x_w, botan_mp_t y_w) + { + return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { return x == safe_get(y_w); }); + } + +int botan_mp_cmp(int* result, botan_mp_t x_w, botan_mp_t y_w) + { + return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { *result = x.cmp(safe_get(y_w)); }); + } + +int botan_mp_swap(botan_mp_t x_w, botan_mp_t y_w) + { + return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { x.swap(safe_get(y_w)); }); + } + +// Return (base^exponent) % modulus +int botan_mp_powmod(botan_mp_t out, botan_mp_t base, botan_mp_t exponent, botan_mp_t modulus) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, + { o = Botan::power_mod(safe_get(base), safe_get(exponent), safe_get(modulus)); }); + } + +int botan_mp_lshift(botan_mp_t out, botan_mp_t in, size_t shift) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) << shift; }); + } + +int botan_mp_rshift(botan_mp_t out, botan_mp_t in, size_t shift) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) >> shift; }); + } + +int botan_mp_mod_inverse(botan_mp_t out, botan_mp_t in, botan_mp_t modulus) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = Botan::inverse_mod(safe_get(in), safe_get(modulus)); }); + } + +int botan_mp_mod_mul(botan_mp_t out, botan_mp_t x, botan_mp_t y, botan_mp_t modulus) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, { + Botan::Modular_Reducer reducer(safe_get(modulus)); + o = reducer.multiply(safe_get(x), safe_get(y)); + }); + } + +int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits) + { + return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { + safe_get(rand_out).randomize(r, bits); }); + } + +int botan_mp_rand_range(botan_mp_t rand_out, + botan_rng_t rng, + botan_mp_t lower, + botan_mp_t upper) + { + return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { + safe_get(rand_out) = Botan::BigInt::random_integer(r, safe_get(lower), safe_get(upper)); }); + } + +int botan_mp_gcd(botan_mp_t out, botan_mp_t x, botan_mp_t y) + { + return BOTAN_FFI_DO(Botan::BigInt, out, o, { + o = Botan::gcd(safe_get(x), safe_get(y)); }); + } + +int botan_mp_is_prime(botan_mp_t mp, botan_rng_t rng, size_t test_prob) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, n, + { return (Botan::is_prime(n, safe_get(rng), test_prob)) ? 1 : 0; }); + } + +int botan_mp_bit_set(botan_mp_t mp, size_t bit) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, n, { return (n.get_bit(bit)); }); + } + +int botan_mp_num_bits(botan_mp_t mp, size_t* bits) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bits = n.bits(); }); + } + +int botan_mp_num_bytes(botan_mp_t mp, size_t* bytes) + { + return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bytes = n.bytes(); }); + } + + int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags) { try @@ -801,7 +1021,7 @@ int botan_privkey_create_rsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, size int botan_privkey_create_ecdsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str) - { + { try { if(key_obj == nullptr || rng_obj == nullptr || param_str == nullptr || *param_str == 0) @@ -919,6 +1139,163 @@ int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj, return -1; } +int botan_privkey_load_rsa(botan_privkey_t* key, + botan_mp_t p, botan_mp_t q, botan_mp_t d) + { + *key = nullptr; + +#if defined(BOTAN_HAS_RSA) + try + { + *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(safe_get(p), + safe_get(q), + safe_get(d))); + return 0; + } + catch(std::exception& e) + { + log_exception(BOTAN_CURRENT_FUNCTION, e.what()); + } + return -1; +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_load_rsa(botan_pubkey_t* key, + botan_mp_t n, botan_mp_t e) + { + *key = nullptr; + +#if defined(BOTAN_HAS_RSA) + try + { + *key = new botan_pubkey_struct(new Botan::RSA_PublicKey(safe_get(n), safe_get(e))); + return 0; + } + catch(std::exception& exn) + { + log_exception(BOTAN_CURRENT_FUNCTION, exn.what()); + } + + return -1; +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k)) + { + safe_get(p) = rsa->get_p(); + } + else + throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_p"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k)) + { + safe_get(q) = rsa->get_q(); + } + else + throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_q"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k)) + { + safe_get(n) = rsa->get_n(); + } + else + throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_n"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k)) + { + safe_get(e) = rsa->get_e(); + } + else + throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_e"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<Botan::RSA_PrivateKey*>(&k)) + { + safe_get(d) = rsa->get_e(); + } + else + throw FFI_Error("Passed non-RSA key to botan_privkey_rsa_get_d"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Public_Key, key, k, { + if(const Botan::RSA_PublicKey* rsa = dynamic_cast<Botan::RSA_PublicKey*>(&k)) + { + safe_get(e) = rsa->get_e(); + } + else + throw FFI_Error("Passed non-RSA key to botan_pubkey_rsa_get_e"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t key) + { +#if defined(BOTAN_HAS_RSA) + return BOTAN_FFI_DO(Botan::Public_Key, key, k, { + if(const Botan::RSA_PublicKey* rsa = dynamic_cast<Botan::RSA_PublicKey*>(&k)) + { + safe_get(n) = rsa->get_n(); + } + else + throw FFI_Error("Passed non-RSA key to botan_pubkey_rsa_get_n"); + }); +#else + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + int botan_privkey_destroy(botan_privkey_t key) { delete key; @@ -959,14 +1336,14 @@ int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags) const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS); return BOTAN_FFI_DO(Botan::Public_Key, key, k, - { return (k.check_key(safe_get(rng), strong) == true) ? 0 : 1; }); + { return (k.check_key(safe_get(rng), strong) == true) ? 0 : -1; }); } int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags) { const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS); return BOTAN_FFI_DO(Botan::Private_Key, key, k, - { return (k.check_key(safe_get(rng), strong) == true) ? 0 : 1; }); + { return (k.check_key(safe_get(rng), strong) == true) ? 0 : -1; }); } int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 8ac9f3c82..2cb8d38bd 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1,6 +1,6 @@ /* * FFI (C89 API) -* (C) 2015 Jack Lloyd +* (C) 2015,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -158,6 +158,7 @@ BOTAN_DLL int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len); * @return 0 on success, 1 on failure */ BOTAN_DLL int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags); + // TODO: botan_hex_decode // TODO: botan_base64_encode // TODO: botan_base64_decode @@ -171,7 +172,8 @@ typedef struct botan_rng_struct* botan_rng_t; * Initialize a random number generator object * @param rng rng object * @param rng_type type of the rng, possible values: -* "system" or nullptr: System_RNG, "user": AutoSeeded_RNG +* "system": System_RNG, "user": AutoSeeded_RNG +* Set rng_type to null or empty string to let the library choose * * TODO: replace rng_type with simple flags? */ @@ -182,7 +184,7 @@ BOTAN_DLL int botan_rng_init(botan_rng_t* rng, const char* rng_type); * @param rng rng object * @param out output buffer of size out_len * @param out_len number of requested bytes -* @return 0 on success, -1 on failure +* @return 0 on success, negative on failure * * TODO: better name */ @@ -279,7 +281,7 @@ typedef struct botan_mac_struct* botan_mac_t; * @param mac mac object * @param mac_name name of the hash function, e.g., "HMAC(SHA-384)" * @param flags should be 0 in current API revision, all other uses are reserved -* and return -1 +* and return a negative value (error code) * @return 0 on success, a negative value on failure */ BOTAN_DLL int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags); @@ -453,6 +455,92 @@ BOTAN_DLL int botan_bcrypt_generate(uint8_t* out, size_t* out_len, size_t work_factor, uint32_t flags); +/* +* Multiple precision integers +*/ +typedef struct botan_mp_struct* botan_mp_t; + +BOTAN_DLL int botan_mp_init(botan_mp_t* mp); +BOTAN_DLL int botan_mp_destroy(botan_mp_t mp); + +// writes botan_mp_num_bytes(mp)*2 + 1 bytes to out[] +BOTAN_DLL int botan_mp_to_hex(botan_mp_t mp, char* out); +BOTAN_DLL int botan_mp_to_str(botan_mp_t mp, uint8_t base, char* out, size_t* out_len); + +BOTAN_DLL int botan_mp_set_from_int(botan_mp_t mp, int initial_value); +BOTAN_DLL int botan_mp_set_from_mp(botan_mp_t dest, botan_mp_t source); +BOTAN_DLL int botan_mp_set_from_str(botan_mp_t dest, const char* str); + +BOTAN_DLL int botan_mp_num_bits(botan_mp_t n, size_t* bits); +BOTAN_DLL int botan_mp_num_bytes(botan_mp_t n, size_t* bytes); + +// Writes botan_mp_num_bytes(mp) to vec +BOTAN_DLL int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[]); +BOTAN_DLL int botan_mp_from_bin(botan_mp_t mp, const uint8_t vec[], size_t vec_len); + +BOTAN_DLL int botan_mp_is_negative(botan_mp_t mp); +BOTAN_DLL int botan_mp_flip_sign(botan_mp_t mp); + +BOTAN_DLL int botan_mp_add(botan_mp_t result, botan_mp_t x, botan_mp_t y); +BOTAN_DLL int botan_mp_sub(botan_mp_t result, botan_mp_t x, botan_mp_t y); +BOTAN_DLL int botan_mp_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y); + +BOTAN_DLL int botan_mp_div(botan_mp_t quotient, + botan_mp_t remainder, + botan_mp_t x, botan_mp_t y); + +BOTAN_DLL int botan_mp_mod_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y, botan_mp_t mod); + +/* +* Returns 0 if x != y +* Returns 1 if x == y +* Returns negative number on error +*/ +BOTAN_DLL int botan_mp_equal(botan_mp_t x, botan_mp_t y); + +/* +* Sets *result to comparison result: +* -1 if x < y, 0 if x == y, 1 if x > y +* Returns negative number on error or zero on success +*/ +BOTAN_DLL int botan_mp_cmp(int* result, botan_mp_t x, botan_mp_t y); + +/* +* Swap two botan_mp_t +*/ +BOTAN_DLL int botan_mp_swap(botan_mp_t x, botan_mp_t y); + +// Return (base^exponent) % modulus +BOTAN_DLL int botan_mp_powmod(botan_mp_t out, botan_mp_t base, botan_mp_t exponent, botan_mp_t modulus); + +BOTAN_DLL int botan_mp_lshift(botan_mp_t out, botan_mp_t in, size_t shift); +BOTAN_DLL int botan_mp_rshift(botan_mp_t out, botan_mp_t in, size_t shift); + +BOTAN_DLL int botan_mp_mod_inverse(botan_mp_t out, botan_mp_t in, botan_mp_t modulus); + +BOTAN_DLL int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits); + +BOTAN_DLL int botan_mp_rand_range(botan_mp_t rand_out, botan_rng_t rng, + botan_mp_t lower_bound, botan_mp_t upper_bound); + +BOTAN_DLL int botan_mp_gcd(botan_mp_t out, botan_mp_t x, botan_mp_t y); + +/** +* Returns 0 if n is not prime +* Returns 1 if n is prime +* Returns negative number on error +*/ +BOTAN_DLL int botan_mp_is_prime(botan_mp_t n, botan_rng_t rng, size_t test_prob); + +/** +* Returns 0 if specified bit of n is not set +* Returns 1 if specified bit of n is set +* Returns negative number on error +*/ +BOTAN_DLL int botan_mp_bit_set(botan_mp_t n, size_t bit); + +/* Bcrypt password hashing */ + /** * Check a previously created password hash * @param pass the password to check against @@ -526,6 +614,9 @@ BOTAN_DLL int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out BOTAN_DLL int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len); +/** +* Returns 0 if key is valid, negative if invalid key or some other error +*/ BOTAN_DLL int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags); BOTAN_DLL int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate); @@ -537,6 +628,27 @@ BOTAN_DLL int botan_pubkey_destroy(botan_pubkey_t key); /* +* Algorithm specific key operations: RSA +*/ +BOTAN_DLL int botan_privkey_load_rsa(botan_privkey_t* key, + botan_mp_t p, + botan_mp_t q, + botan_mp_t d); + +BOTAN_DLL int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key); +BOTAN_DLL int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key); +BOTAN_DLL int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key); +BOTAN_DLL int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key); +BOTAN_DLL int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key); + +BOTAN_DLL int botan_pubkey_load_rsa(botan_pubkey_t* key, + botan_mp_t n, + botan_mp_t e); + +BOTAN_DLL int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key); +BOTAN_DLL int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key); + +/* * Public Key Encryption */ typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t; diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt index 7b3068274..e1d29981f 100644 --- a/src/lib/ffi/info.txt +++ b/src/lib/ffi/info.txt @@ -1,10 +1,11 @@ -define FFI 20151015 +define FFI 20170327 <requires> aead kdf pbkdf pubkey +bigint x509 #tls system_rng diff --git a/src/python/botan2.py b/src/python/botan2.py index 399680b86..9d04b83db 100755 --- a/src/python/botan2.py +++ b/src/python/botan2.py @@ -3,7 +3,7 @@ """Python wrapper of the botan crypto library http://botan.randombit.net -(C) 2015 Jack Lloyd +(C) 2015,2017 Jack Lloyd (C) 2015 Uri Blumenthal (extensions and patches) Botan is released under the Simplified BSD License (see license.txt) @@ -11,10 +11,8 @@ Botan is released under the Simplified BSD License (see license.txt) This module uses the ctypes module and is usable by programs running under at least CPython 2.7, CPython 3.4 and 3.5, or PyPy. -It uses botan's ffi module, which exposes a C API. Right now the C API -is versioned but as it is still in evolution, no provisions are made -for handling more than a single API version in this module. So this -module should be used only with the library version it accompanied. +It uses botan's ffi module, which exposes a C API. It suppports all +versions of Botan >= 2.0 """ import sys @@ -31,11 +29,8 @@ if sys.platform == 'darwin': else: botan = CDLL('libbotan-2.so') -expected_api_rev = 20151015 -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)) +if botan.botan_ffi_supports_api(20151015) == False: + raise Exception("The Botan library does not support the FFI API expected by this version of the Python module") # Internal utilities def _call_fn_returning_vec(guess, fn): diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 9c314c5ff..f6c83c5f9 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -224,7 +224,7 @@ class FFI_Unit_Tests : public Test outstr.resize(out_len); int rc = botan_bcrypt_generate(reinterpret_cast<uint8_t*>(&outstr[0]), - &out_len, passphrase.c_str(), rng, 3, 0); + &out_len, passphrase.c_str(), rng, 4, 0); if(rc == 0) { @@ -386,6 +386,7 @@ class FFI_Unit_Tests : public Test } std::vector<Test::Result> results; + results.push_back(ffi_test_mp(rng)); results.push_back(ffi_test_rsa(rng)); results.push_back(ffi_test_ecdsa(rng)); results.push_back(ffi_test_ecdh(rng)); @@ -398,6 +399,162 @@ class FFI_Unit_Tests : public Test } private: + Test::Result ffi_test_mp(botan_rng_t rng) + { + Test::Result result("FFI MP"); + + botan_mp_t x; + botan_mp_init(&x); + botan_mp_destroy(x); + + botan_mp_init(&x); + size_t bn_bytes = 0; + TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); + result.test_eq("Expected size for MP 0", bn_bytes, 0); + + botan_mp_set_from_int(x, 5); + TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); + result.test_eq("Expected size for MP 5", bn_bytes, 1); + + botan_mp_set_from_int(x, 259); + TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); + result.test_eq("Expected size for MP 259", bn_bytes, 2); + + { + botan_mp_t zero; + botan_mp_init(&zero); + int cmp; + TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero)); + result.confirm("bigint_mp_cmp(+, 0)", cmp == 1); + + TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x)); + result.confirm("bigint_mp_cmp(0, +)", cmp == -1); + + TEST_FFI_OK(botan_mp_flip_sign, (x)); + + TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero)); + result.confirm("bigint_mp_cmp(-, 0)", cmp == -1); + + TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x)); + result.confirm("bigint_mp_cmp(0, -)", cmp == 1); + + TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, zero)); + result.confirm("bigint_mp_cmp(0, 0)", cmp == 0); + + TEST_FFI_OK(botan_mp_cmp, (&cmp, x, x)); + result.confirm("bigint_mp_cmp(x, x)", cmp == 0); + + TEST_FFI_OK(botan_mp_flip_sign, (x)); + + botan_mp_destroy(zero); + } + + size_t x_bits = 0; + TEST_FFI_OK(botan_mp_num_bits, (x, &x_bits)); + result.test_eq("botan_mp_num_bits", x_bits, 9); + + char str_buf[1024] = { 0 }; + size_t str_len = 0; + + TEST_FFI_OK(botan_mp_to_hex, (x, str_buf)); + result.test_eq("botan_mp_to_hex", std::string(str_buf), "0103"); + + botan_mp_t y; + TEST_FFI_OK(botan_mp_init, (&y)); + TEST_FFI_OK(botan_mp_set_from_int, (y, 0x1234567)); + + botan_mp_t r; + botan_mp_init(&r); + + TEST_FFI_OK(botan_mp_add, (r, x, y)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_add", std::string(str_buf), "19089002"); + + TEST_FFI_OK(botan_mp_mul, (r, x, y)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_mul", std::string(str_buf), "4943984437"); + TEST_FFI_RC(0, botan_mp_is_negative, (r)); + + botan_mp_t q; + botan_mp_init(&q); + TEST_FFI_OK(botan_mp_div, (q, r, y, x)); + + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (q, 10, str_buf, &str_len)); + result.test_eq("botan_mp_div_q", std::string(str_buf), "073701"); + + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_div_r", std::string(str_buf), "184"); + + TEST_FFI_OK(botan_mp_set_from_str, (y, "4943984437")); + TEST_FFI_OK(botan_mp_sub, (r, x, y)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_sub", std::string(str_buf), "4943984178"); + TEST_FFI_RC(1, botan_mp_is_negative, (r)); + + TEST_FFI_OK(botan_mp_lshift, (r, x, 39)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_lshift", std::string(str_buf), "142386755796992"); + + TEST_FFI_OK(botan_mp_rshift, (r, r, 3)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_rshift", std::string(str_buf), "17798344474624"); + + TEST_FFI_OK(botan_mp_gcd, (r, x, y)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_gcd", std::string(str_buf), "259"); + + botan_mp_t p; + botan_mp_init(&p); + const uint8_t M127[] = { 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + TEST_FFI_OK(botan_mp_from_bin, (p, M127, sizeof(M127))); + TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64)); + + size_t p_bits = 0; + TEST_FFI_OK(botan_mp_num_bits, (p, &p_bits)); + result.test_eq("botan_mp_num_bits", p_bits, 127); + + TEST_FFI_OK(botan_mp_mod_inverse, (r, x, p)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_mod_inverse", std::string(str_buf), "40728777507911553541948312086427855425"); + + TEST_FFI_OK(botan_mp_powmod, (r, x, r, p)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_powmod", std::string(str_buf), "40550417419160441638948180641668117560"); + + TEST_FFI_OK(botan_mp_num_bytes, (r, &bn_bytes)); + result.test_eq("botan_mp_num_bytes", bn_bytes, 16); + + std::vector<uint8_t> bn_buf; + bn_buf.resize(bn_bytes); + botan_mp_to_bin(r, bn_buf.data()); + result.test_eq("botan_mp_to_bin", bn_buf, "1E81B9EFE0BE1902F6D03F9F5E5FB438"); + + TEST_FFI_OK(botan_mp_set_from_mp, (y, r)); + TEST_FFI_OK(botan_mp_mod_mul, (r, x, y, p)); + str_len = sizeof(str_buf); + TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); + result.test_eq("botan_mp_mod_mul", std::string(str_buf), "123945920473931248854653259523111998693"); + + + botan_mp_destroy(p); + botan_mp_destroy(x); + botan_mp_destroy(y); + botan_mp_destroy(r); + botan_mp_destroy(q); + + return result; + } + void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng) { // export public key @@ -480,7 +637,7 @@ class FFI_Unit_Tests : public Test Test::Result ffi_test_rsa(botan_rng_t rng) { - Test::Result result("FFI"); + Test::Result result("FFI RSA"); botan_privkey_t priv; if(TEST_FFI_OK(botan_privkey_create_rsa, (&priv, rng, 1024))) @@ -493,16 +650,76 @@ class FFI_Unit_Tests : public Test ffi_test_pubkey_export(result, pub, priv, rng); + botan_mp_t p, q, d, n, e; + botan_mp_init(&p); + botan_mp_init(&q); + botan_mp_init(&d); + botan_mp_init(&n); + botan_mp_init(&e); + + TEST_FFI_OK(botan_privkey_rsa_get_p, (p, priv)); + TEST_FFI_OK(botan_privkey_rsa_get_q, (q, priv)); + TEST_FFI_OK(botan_privkey_rsa_get_d, (d, priv)); + TEST_FFI_OK(botan_privkey_rsa_get_e, (e, priv)); + TEST_FFI_OK(botan_privkey_rsa_get_n, (n, priv)); + + // Confirm same (e,n) values in public key + { + botan_mp_t pub_e, pub_n; + botan_mp_init(&pub_e); + botan_mp_init(&pub_n); + TEST_FFI_OK(botan_pubkey_rsa_get_e, (pub_e, pub)); + TEST_FFI_OK(botan_pubkey_rsa_get_n, (pub_n, pub)); + + TEST_FFI_RC(1, botan_mp_equal, (pub_e, e)); + TEST_FFI_RC(1, botan_mp_equal, (pub_n, n)); + botan_mp_destroy(pub_e); + botan_mp_destroy(pub_n); + } + + TEST_FFI_RC(1, botan_mp_is_prime, (p, rng, 64)); + TEST_FFI_RC(1, botan_mp_is_prime, (q, rng, 64)); + + // Test p != q + TEST_FFI_RC(0, botan_mp_equal, (p, q)); + + // Test p * q == n + botan_mp_t x; + botan_mp_init(&x); + TEST_FFI_OK(botan_mp_mul, (x, p, q)); + + TEST_FFI_RC(1, botan_mp_equal, (x, n)); + botan_mp_destroy(x); + + botan_privkey_t loaded_privkey; + // First try loading a bogus key and verify check_key fails + TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, n, d, q)); + TEST_FFI_RC(-1, botan_privkey_check_key, (loaded_privkey, rng, 0)); + botan_privkey_destroy(loaded_privkey); + + TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, p, q, d)); + TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); + + botan_pubkey_t loaded_pubkey; + TEST_FFI_OK(botan_pubkey_load_rsa, (&loaded_pubkey, n, e)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0)); + + botan_mp_destroy(p); + botan_mp_destroy(q); + botan_mp_destroy(d); + botan_mp_destroy(e); + botan_mp_destroy(n); + char namebuf[32] = { 0 }; size_t name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len))) + if(TEST_FFI_OK(botan_pubkey_algo_name, (loaded_pubkey, namebuf, &name_len))) { result.test_eq("algo name", std::string(namebuf), "RSA"); } botan_pk_op_encrypt_t encrypt; - if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&encrypt, pub, "OAEP(SHA-256)", 0))) + if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&encrypt, loaded_pubkey, "OAEP(SHA-256)", 0))) { std::vector<uint8_t> plaintext(32); TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size())); @@ -534,7 +751,9 @@ class FFI_Unit_Tests : public Test TEST_FFI_OK(botan_pk_op_encrypt_destroy, (encrypt)); } + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); TEST_FFI_OK(botan_privkey_destroy, (priv)); } @@ -543,7 +762,7 @@ class FFI_Unit_Tests : public Test Test::Result ffi_test_ecdsa(botan_rng_t rng) { - Test::Result result("FFI"); + Test::Result result("FFI ECDSA"); botan_privkey_t priv; @@ -614,7 +833,7 @@ class FFI_Unit_Tests : public Test Test::Result ffi_test_ecdh(botan_rng_t rng) { - Test::Result result("FFI"); + Test::Result result("FFI ECDH"); botan_privkey_t priv1; REQUIRE_FFI_OK(botan_privkey_create_ecdh, (&priv1, rng, "secp256r1")); @@ -677,7 +896,7 @@ class FFI_Unit_Tests : public Test Test::Result ffi_test_mceliece(botan_rng_t rng) { - Test::Result result("FFI"); + Test::Result result("FFI McEliece"); botan_privkey_t priv; #if defined(BOTAN_HAS_MCELIECE) |