diff options
-rw-r--r-- | src/lib/ffi/ffi.h | 73 | ||||
-rw-r--r-- | src/lib/ffi/ffi_pkey.cpp | 4 | ||||
-rwxr-xr-x | src/python/botan2.py | 208 | ||||
-rw-r--r-- | src/scripts/test_python.py | 116 | ||||
-rw-r--r-- | src/tests/test_ffi.cpp | 2 |
5 files changed, 350 insertions, 53 deletions
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 6345acf2c..323f47557 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -794,9 +794,9 @@ BOTAN_PUBLIC_API(2,1) int botan_mp_from_bin(const botan_mp_t mp, const uint8_t v BOTAN_PUBLIC_API(2,1) int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val); /** -* This function is not well named. Returns 1 iff mp is greater than -* *or equal to* zero. Use botan_mp_is_negative to detect negative -* numbers, botan_mp_is_zero to check for zero. +* This function should have been named mp_is_non_negative. Returns 1 +* iff mp is greater than *or equal to* zero. Use botan_mp_is_negative +* to detect negative numbers, botan_mp_is_zero to check for zero. */ BOTAN_PUBLIC_API(2,1) int botan_mp_is_positive(const botan_mp_t mp); @@ -808,8 +808,11 @@ BOTAN_PUBLIC_API(2,1) int botan_mp_is_negative(const botan_mp_t mp); BOTAN_PUBLIC_API(2,1) int botan_mp_flip_sign(botan_mp_t mp); BOTAN_PUBLIC_API(2,1) int botan_mp_is_zero(const botan_mp_t mp); -BOTAN_PUBLIC_API(2,1) int botan_mp_is_odd(const botan_mp_t mp); -BOTAN_PUBLIC_API(2,1) int botan_mp_is_even(const botan_mp_t mp); + +BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)") +int botan_mp_is_odd(const botan_mp_t mp); +BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)") +int botan_mp_is_even(const botan_mp_t mp); BOTAN_PUBLIC_API(2,8) int botan_mp_add_u32(botan_mp_t result, const botan_mp_t x, uint32_t y); BOTAN_PUBLIC_API(2,8) int botan_mp_sub_u32(botan_mp_t result, const botan_mp_t x, uint32_t y); @@ -936,12 +939,16 @@ BOTAN_PUBLIC_API(2,0) int botan_privkey_create(botan_privkey_t* key, BOTAN_PUBLIC_API(2,0) int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags); -BOTAN_PUBLIC_API(2,0) int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits); -BOTAN_PUBLIC_API(2,0) int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params); -BOTAN_PUBLIC_API(2,0) int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params); -BOTAN_PUBLIC_API(2,0) int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t); -BOTAN_PUBLIC_API(2,0) int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, const char* param); - +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create") +int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create") +int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create") +int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create") +int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create") +int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, const char* param); /** * Generates DSA key pair. Gives to a caller control over key length @@ -1102,11 +1109,16 @@ BOTAN_PUBLIC_API(2,8) int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key, const uint8_t bits[], size_t len); -BOTAN_PUBLIC_API(2,0) int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key); -BOTAN_PUBLIC_API(2,0) int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key); -BOTAN_PUBLIC_API(2,0) int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key); -BOTAN_PUBLIC_API(2,0) int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key); -BOTAN_PUBLIC_API(2,0) int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key); BOTAN_PUBLIC_API(2,8) int botan_privkey_rsa_get_privkey(botan_privkey_t rsa_key, uint8_t out[], size_t* out_len, @@ -1116,8 +1128,10 @@ BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_rsa(botan_pubkey_t* key, botan_mp_t n, botan_mp_t e); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key); /* * Algorithm specific key operations: DSA @@ -1134,12 +1148,17 @@ BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_dsa(botan_pubkey_t* key, botan_mp_t g, botan_mp_t y); -BOTAN_PUBLIC_API(2,0) int botan_privkey_dsa_get_x(botan_mp_t n, botan_privkey_t key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field") +int botan_privkey_dsa_get_x(botan_mp_t n, botan_privkey_t key); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_dsa_get_g(botan_mp_t d, botan_pubkey_t key); -BOTAN_PUBLIC_API(2,0) int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_dsa_get_g(botan_mp_t d, botan_pubkey_t key); +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field") +int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key); /* * Loads Diffie Hellman private key @@ -1283,13 +1302,13 @@ int botan_privkey_load_sm2(botan_privkey_t* key, const botan_mp_t scalar, const char* curve_name); -BOTAN_PUBLIC_API(2,2) +BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_pubkey_load_sm2") int botan_pubkey_load_sm2_enc(botan_pubkey_t* key, const botan_mp_t public_x, const botan_mp_t public_y, const char* curve_name); -BOTAN_PUBLIC_API(2,2) +BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_privkey_load_sm2") int botan_privkey_load_sm2_enc(botan_privkey_t* key, const botan_mp_t scalar, const char* curve_name); @@ -1412,7 +1431,7 @@ BOTAN_PUBLIC_API(2,0) int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs * @param mce_key must be a McEliece key * ct_len should be pt_len + n/8 + a few? */ -BOTAN_PUBLIC_API(2,0) +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code") int botan_mceies_encrypt(botan_pubkey_t mce_key, botan_rng_t rng, const char* aead, @@ -1420,7 +1439,7 @@ int botan_mceies_encrypt(botan_pubkey_t mce_key, const uint8_t ad[], size_t ad_len, uint8_t ct[], size_t* ct_len); -BOTAN_PUBLIC_API(2,0) +BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code") int botan_mceies_decrypt(botan_privkey_t mce_key, const char* aead, const uint8_t ct[], size_t ct_len, diff --git a/src/lib/ffi/ffi_pkey.cpp b/src/lib/ffi/ffi_pkey.cpp index 24bc96758..e0ba2380a 100644 --- a/src/lib/ffi/ffi_pkey.cpp +++ b/src/lib/ffi/ffi_pkey.cpp @@ -138,14 +138,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 : BOTAN_FFI_ERROR_INVALID_INPUT; }); } 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 : BOTAN_FFI_ERROR_INVALID_INPUT; }); } int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags) diff --git a/src/python/botan2.py b/src/python/botan2.py index 73f4e73d7..d92fd029b 100755 --- a/src/python/botan2.py +++ b/src/python/botan2.py @@ -71,18 +71,17 @@ def _errcheck(rc, fn, _args): # This errcheck should only be used for int-returning functions assert isinstance(rc, int) - if rc >= 0: - return rc - if rc == -10: # insufficient buffer space, pass up to caller + if rc >= 0 or rc in fn.allowed_errors: return rc raise BotanException('%s failed' % (fn.__name__), rc) def _set_prototypes(dll): # pylint: disable=too-many-statements,line-too-long - def ffi_api(fn, args): + def ffi_api(fn, args, allowed_errors = [-10]): fn.argtypes = args fn.restype = c_int fn.errcheck = _errcheck + fn.allowed_errors = allowed_errors dll.botan_version_string.argtypes = [] dll.botan_version_string.restype = c_char_p @@ -99,11 +98,14 @@ def _set_prototypes(dll): dll.botan_version_patch.argtypes = [] dll.botan_version_patch.restype = c_uint32 + dll.botan_ffi_api_version.argtypes = [] + dll.botan_ffi_api_version.restype = c_uint32 + dll.botan_error_description.argtypes = [c_int] dll.botan_error_description.restype = c_char_p # These are generated using src/scripts/ffi_decls.py: - ffi_api(dll.botan_constant_time_compare, [c_char_p, c_char_p, c_size_t]) + ffi_api(dll.botan_constant_time_compare, [c_void_p, c_void_p, c_size_t], [-1]) ffi_api(dll.botan_scrub_mem, [c_void_p, c_size_t]) ffi_api(dll.botan_hex_encode, [c_char_p, c_size_t, c_char_p, c_uint32]) @@ -237,7 +239,7 @@ def _set_prototypes(dll): # PUBKEY ffi_api(dll.botan_privkey_create, [c_void_p, c_char_p, c_char_p, c_void_p]) - ffi_api(dll.botan_privkey_check_key, [c_void_p, c_void_p, c_uint32]) + ffi_api(dll.botan_privkey_check_key, [c_void_p, c_void_p, c_uint32], [-1]) ffi_api(dll.botan_privkey_create_rsa, [c_void_p, c_void_p, c_size_t]) ffi_api(dll.botan_privkey_create_ecdsa, [c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_privkey_create_ecdh, [c_void_p, c_void_p, c_char_p]) @@ -260,7 +262,7 @@ def _set_prototypes(dll): ffi_api(dll.botan_pubkey_load, [c_void_p, c_char_p, c_size_t]) ffi_api(dll.botan_pubkey_export, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32]) ffi_api(dll.botan_pubkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)]) - ffi_api(dll.botan_pubkey_check_key, [c_void_p, c_void_p, c_uint32]) + ffi_api(dll.botan_pubkey_check_key, [c_void_p, c_void_p, c_uint32], [-1]) ffi_api(dll.botan_pubkey_estimated_strength, [c_void_p, POINTER(c_size_t)]) ffi_api(dll.botan_pubkey_fingerprint, [c_void_p, c_char_p, c_char_p, POINTER(c_size_t)]) ffi_api(dll.botan_pubkey_destroy, [c_void_p]) @@ -432,6 +434,8 @@ def _call_fn_returning_str(guess, fn): return v.decode('ascii')[:-1] def _ctype_str(s): + if s is None: + return None assert isinstance(s, str) if version_info[0] < 3: return s @@ -468,7 +472,7 @@ def _hex_encode(buf): return hexlify(buf).decode('ascii') # -# Versions +# Versioning # def version_major(): return int(_DLL.botan_version_major()) @@ -479,10 +483,24 @@ def version_minor(): def version_patch(): return int(_DLL.botan_version_patch()) +def ffi_api_version(): + return int(_DLL.botan_ffi_api_version()) + def version_string(): return _DLL.botan_version_string().decode('ascii') # +# Utilities +# +def const_time_compare(x, y): + len_x = len(x) + len_y = len(y) + if len_x != len_y: + return False + rc = _DLL.botan_constant_time_compare(_ctype_bits(x), _ctype_bits(y), c_size_t(len_x)) + return (rc == 0) + +# # RNG # class RandomNumberGenerator(object): @@ -836,12 +854,78 @@ class PublicKey(object): # pylint: disable=invalid-name _DLL.botan_pubkey_load(byref(obj), _ctype_bits(val), len(val)) return PublicKey(obj) + @classmethod + def load_rsa(cls, n, e): + obj = c_void_p(0) + n = MPI(n) + e = MPI(e) + _DLL.botan_pubkey_load_rsa(byref(obj), n.handle_(), e.handle_()) + return PublicKey(obj) + + @classmethod + def load_dsa(cls, p, q, g, y): + obj = c_void_p(0) + p = MPI(p) + q = MPI(q) + g = MPI(g) + y = MPI(y) + _DLL.botan_pubkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_()) + return PublicKey(obj) + + @classmethod + def load_dh(cls, p, g, y): + obj = c_void_p(0) + p = MPI(p) + g = MPI(g) + y = MPI(y) + _DLL.botan_pubkey_load_dh(byref(obj), p.handle_(), g.handle_(), y.handle_()) + return PublicKey(obj) + + @classmethod + def load_elgamal(cls, p, q, g, y): + obj = c_void_p(0) + p = MPI(p) + q = MPI(q) + g = MPI(g) + y = MPI(y) + _DLL.botan_pubkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_()) + return PublicKey(obj) + + @classmethod + def load_ecdsa(cls, curve, pub_x, pub_y): + obj = c_void_p(0) + pub_x = MPI(pub_x) + pub_y = MPI(pub_y) + _DLL.botan_pubkey_load_ecdsa(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return PublicKey(obj) + + @classmethod + def load_ecdh(cls, curve, pub_x, pub_y): + obj = c_void_p(0) + pub_x = MPI(pub_x) + pub_y = MPI(pub_y) + _DLL.botan_pubkey_load_ecdh(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return PublicKey(obj) + + @classmethod + def load_sm2(cls, curve, pub_x, pub_y): + obj = c_void_p(0) + pub_x = MPI(pub_x) + pub_y = MPI(pub_y) + _DLL.botan_pubkey_load_sm2(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return PublicKey(obj) + def __del__(self): _DLL.botan_pubkey_destroy(self.__obj) def handle_(self): return self.__obj + def check_key(self, rng_obj, strong=True): + flags = 1 if strong else 0 + rc = _DLL.botan_pubkey_check_key(self.__obj, rng_obj.handle_(), flags) + return rc == 0 + def estimated_strength(self): r = c_size_t(0) _DLL.botan_pubkey_estimated_strength(self.__obj, byref(r)) @@ -866,7 +950,6 @@ class PublicKey(object): # pylint: disable=invalid-name return self.export(True) def fingerprint(self, hash_algorithm='SHA-256'): - n = HashFunction(hash_algorithm).output_length() buf = create_string_buffer(n) buf_len = c_size_t(n) @@ -874,6 +957,11 @@ class PublicKey(object): # pylint: disable=invalid-name _DLL.botan_pubkey_fingerprint(self.__obj, _ctype_str(hash_algorithm), buf, byref(buf_len)) return _hex_encode(buf[0:int(buf_len.value)]) + def get_field(self, field_name): + v = MPI() + _DLL.botan_pubkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name)) + return int(v) + # # Private Key # @@ -896,14 +984,12 @@ class PrivateKey(object): params = "%d" % (params) elif algo == 'ecdsa': algo = 'ECDSA' - elif algo == 'ecdh': - + elif algo in ['ecdh', 'ECDH']: if params == 'curve25519': algo = 'Curve25519' params = '' else: algo = 'ECDH' - elif algo in ['mce', 'mceliece']: algo = 'McEliece' params = "%d,%d" % (params[0], params[1]) @@ -912,12 +998,76 @@ class PrivateKey(object): _DLL.botan_privkey_create(byref(obj), _ctype_str(algo), _ctype_str(params), rng_obj.handle_()) return PrivateKey(obj) + @classmethod + def load_rsa(cls, p, q, e): + obj = c_void_p(0) + p = MPI(p) + q = MPI(q) + e = MPI(e) + _DLL.botan_privkey_load_rsa(byref(obj), p.handle_(), q.handle_(), e.handle_()) + return PrivateKey(obj) + + @classmethod + def load_dsa(cls, p, q, g, x): + obj = c_void_p(0) + p = MPI(p) + q = MPI(q) + g = MPI(g) + x = MPI(x) + _DLL.botan_privkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_()) + return PrivateKey(obj) + + @classmethod + def load_dh(cls, p, g, x): + obj = c_void_p(0) + p = MPI(p) + g = MPI(g) + x = MPI(x) + _DLL.botan_privkey_load_dh(byref(obj), p.handle_(), g.handle_(), x.handle_()) + return PrivateKey(obj) + + @classmethod + def load_elgamal(cls, p, q, g, x): + obj = c_void_p(0) + p = MPI(p) + q = MPI(q) + g = MPI(g) + x = MPI(x) + _DLL.botan_privkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_()) + return PrivateKey(obj) + + @classmethod + def load_ecdsa(cls, curve, x): + obj = c_void_p(0) + x = MPI(x) + _DLL.botan_privkey_load_ecdsa(byref(obj), x.handle_(), _ctype_str(curve)) + return PrivateKey(obj) + + @classmethod + def load_ecdh(cls, curve, x): + obj = c_void_p(0) + x = MPI(x) + _DLL.botan_privkey_load_ecdh(byref(obj), x.handle_(), _ctype_str(curve)) + return PrivateKey(obj) + + @classmethod + def load_sm2(cls, curve, x): + obj = c_void_p(0) + x = MPI(x) + _DLL.botan_privkey_load_sm2(byref(obj), x.handle_(), _ctype_str(curve)) + return PrivateKey(obj) + def __del__(self): _DLL.botan_privkey_destroy(self.__obj) def handle_(self): return self.__obj + def check_key(self, rng_obj, strong=True): + flags = 1 if strong else 0 + rc = _DLL.botan_privkey_check_key(self.__obj, rng_obj.handle_(), flags) + return rc == 0 + def algo_name(self): return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_privkey_algo_name(self.__obj, b, bl)) @@ -938,6 +1088,25 @@ class PrivateKey(object): else: return _call_fn_returning_vec(4096, lambda b, bl: _DLL.botan_privkey_export(self.__obj, b, bl, 0)) + def export_encrypted(self, passphrase, rng_obj, pem=False, msec=300, cipher=None, pbkdf=None): + flags = 1 if pem else 0 + msec = c_uint32(msec) + _iters = c_size_t(0) + + cb = lambda b, bl: _DLL.botan_privkey_export_encrypted_pbkdf_msec( + self.__obj, b, bl, rng_obj.handle_(), _ctype_str(passphrase), + msec, byref(_iters), _ctype_str(cipher), _ctype_str(pbkdf), flags) + + if pem: + return _call_fn_returning_str(8192, cb) + else: + return _call_fn_returning_vec(4096, cb) + + def get_field(self, field_name): + v = MPI() + _DLL.botan_privkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name)) + return int(v) + class PKEncrypt(object): def __init__(self, key, padding): self.__obj = c_void_p(0) @@ -1023,7 +1192,14 @@ class PKKeyAgreement(object): def public_value(self): return self.m_public_value + def underlying_output_length(self): + out_len = c_size_t(0) + _DLL.botan_pk_op_key_agreement_size(self.__obj, byref(out_len)) + return out_len.value + def agree(self, other, key_len, salt): + if key_len == 0: + key_len = self.underlying_output_length() return _call_fn_returning_vec(key_len, lambda b, bl: _DLL.botan_pk_op_key_agreement(self.__obj, b, bl, other, len(other), @@ -1201,6 +1377,14 @@ class MPI(object): else: return s + def to_bytes(self): + byte_count = self.byte_count() + out_len = c_size_t(byte_count) + out = create_string_buffer(out_len.value) + _DLL.botan_mp_to_bin(self.__obj, out, byref(out_len)) + assert out_len.value == byte_count + return out + def is_negative(self): rc = _DLL.botan_mp_is_negative(self.__obj) return rc == 1 diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py index 1ba21f570..9cba9a4ea 100644 --- a/src/scripts/test_python.py +++ b/src/scripts/test_python.py @@ -25,6 +25,17 @@ class BotanPythonTests(unittest.TestCase): self.assertEqual(botan2.version_major(), 2) self.assertTrue(botan2.version_minor() >= 8) + self.assertTrue(botan2.ffi_api_version() >= 20180713) + + def test_compare(self): + + x = "1234" + y = "1234" + z = "1233" + self.assertTrue(botan2.const_time_compare(x, y)) + self.assertFalse(botan2.const_time_compare(x, z)) + self.assertFalse(botan2.const_time_compare(x, x + z)) + def test_block_cipher(self): aes = botan2.BlockCipher("AES-128") self.assertEqual(aes.algo_name(), "AES-128") @@ -181,7 +192,7 @@ class BotanPythonTests(unittest.TestCase): def test_mceliece(self): rng = botan2.RandomNumberGenerator() - mce_priv = botan2.PrivateKey.create('mce', [2960, 57], rng) + mce_priv = botan2.PrivateKey.create('McEliece', '2960,57', rng) mce_pub = mce_priv.get_public_key() self.assertEqual(mce_pub.estimated_strength(), 128) @@ -231,6 +242,38 @@ ofvkP1EDmpx50fHLawIDAQAB rsapub = botan2.PublicKey.load(rsa_pub_pem) self.assertEqual(rsapub.to_pem(), rsa_pub_pem) + n = 0xB5AD8818DCA1F256FF8FAB0888D0667D95DF2098B0D201A4C75590D3EBDFA159DD91C64AFDA082609EF885B2D1F4DC055C8FF9FA371C2F3398E0B612C603151131C81DB322C8D15E53EB56B4DF7325F05046889CB25021DE4282E16B9B28F5CBB2B8DDECE0F8E4E8A77F674F26AE92B7220920A1FBE43F51039A9C79D1F1CB6B + e = 0x10001 + + rsapub2 = botan2.PublicKey.load_rsa(n, e) + self.assertEqual(rsapub2.to_pem(), rsa_pub_pem) + + self.assertEqual(rsapub2.get_field("n"), n) + self.assertEqual(rsapub2.get_field("e"), e) + + def test_key_crypto(self): + rng = botan2.RandomNumberGenerator() + rsapriv = botan2.PrivateKey.create('RSA', '1024', rng) + passphrase = "super secret tell noone" + + pem = rsapriv.export_encrypted(passphrase, rng, True, msec=10) + pem2 = rsapriv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/SIV") + pem3 = rsapriv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/SIV", pbkdf="Scrypt") + + def test_check_key(self): + # valid (if rather small) RSA key + n = 273279220906618527352827457840955116141 + e = 0x10001 + + rng = botan2.RandomNumberGenerator() + + rsapub = botan2.PublicKey.load_rsa(n, e) + self.assertTrue(rsapub.check_key(rng)) + + # invalid + rsapub = botan2.PublicKey.load_rsa(n - 1, e) + self.assertFalse(rsapub.check_key(rng)) + def test_rsa(self): # pylint: disable=too-many-locals rng = botan2.RandomNumberGenerator() @@ -282,28 +325,74 @@ ofvkP1EDmpx50fHLawIDAQAB verify.update('message') self.assertTrue(verify.check_signature(sig)) - def test_dh(self): + def test_ecdsa(self): + rng = botan2.RandomNumberGenerator() + + hash = 'EMSA1(SHA-256)' + group = 'secp256r1' + msg = 'test message' + + priv = botan2.PrivateKey.create('ECDSA', group, rng) + pub = priv.get_public_key() + self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x')) + self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y')) + + signer = botan2.PKSign(priv, hash) + signer.update(msg) + signature = signer.finish(rng) + + verifier = botan2.PKVerify(pub, hash) + verifier.update(msg) + self.assertTrue(verifier.check_signature(signature)) + + pub_x = pub.get_field('public_x') + pub_y = priv.get_field('public_y') + pub2 = botan2.PublicKey.load_ecdsa(group, pub_x, pub_y) + verifier = botan2.PKVerify(pub2, hash) + verifier.update(msg) + self.assertTrue(verifier.check_signature(signature)) + + priv2 = botan2.PrivateKey.load_ecdsa(group, priv.get_field('x')) + signer = botan2.PKSign(priv2, hash) + # sign empty message + signature = signer.finish(rng) + + # verify empty message + self.assertTrue(verifier.check_signature(signature)) + + + def test_ecdh(self): a_rng = botan2.RandomNumberGenerator('user') b_rng = botan2.RandomNumberGenerator('user') - for dh_grp in ['secp256r1', 'curve25519']: - dh_kdf = 'KDF2(SHA-384)'.encode('utf-8') - a_dh_priv = botan2.PrivateKey.create('ecdh', dh_grp, a_rng) - b_dh_priv = botan2.PrivateKey.create('ecdh', dh_grp, b_rng) + # XXX why need the encode here?? should be handled in wrapper + kdf = 'KDF2(SHA-384)'.encode('utf-8') + + for grp in ['secp256r1', 'secp384r1', 'brainpool256r1']: + a_priv = botan2.PrivateKey.create('ECDH', grp, a_rng) + b_priv = botan2.PrivateKey.create('ECDH', grp, b_rng) - a_dh = botan2.PKKeyAgreement(a_dh_priv, dh_kdf) - b_dh = botan2.PKKeyAgreement(b_dh_priv, dh_kdf) + a_op = botan2.PKKeyAgreement(a_priv, kdf) + b_op = botan2.PKKeyAgreement(b_priv, kdf) - a_dh_pub = a_dh.public_value() - b_dh_pub = b_dh.public_value() + a_pub = a_op.public_value() + b_pub = b_op.public_value() salt = a_rng.get(8) + b_rng.get(8) - a_key = a_dh.agree(b_dh_pub, 32, salt) - b_key = b_dh.agree(a_dh_pub, 32, salt) + a_key = a_op.agree(b_pub, 32, salt) + b_key = b_op.agree(a_pub, 32, salt) self.assertEqual(a_key, b_key) + a_pem = a_priv.to_pem() + + a_priv_x = a_priv.get_field('x') + + new_a = botan2.PrivateKey.load_ecdh(grp, a_priv_x) + + self.assertEqual(a_pem, new_a.to_pem()) + def test_certs(self): cert = botan2.X509Cert(filename="src/tests/data/x509/ecc/CSCA.CSCA.csca-germany.1.crt") pubkey = cert.subject_public_key() @@ -336,6 +425,9 @@ ofvkP1EDmpx50fHLawIDAQAB self.assertEqual(big.bit_count(), 104) small = botan2.MPI(0xDEADBEEF) + self.assertEqual(hex_encode(small.to_bytes()), "deadbeef") + self.assertEqual(hex_encode(big.to_bytes()), "85839682368923476892367235") + self.assertEqual(int(small), 0xDEADBEEF) self.assertEqual(int(small >> 16), 0xDEAD) diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index f2926d777..6263999d9 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -7,6 +7,8 @@ * Botan is released under the Simplified BSD License (see license.txt) */ +#define BOTAN_NO_DEPRECATED_WARNINGS + #include "tests.h" #include <botan/version.h> |