Repository: randombit/botan
Pull request #1915 (2019-04-27 10:40:32)

More Python API enhancements.
GH #1906 


Patch:
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..b0b2856ec 100755
--- a/src/python/botan2.py
+++ b/src/python/botan2.py
@@ -71,18 +71,19 @@ 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=None):
+        if allowed_errors is None:
+            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 +100,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 +241,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 +264,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 +436,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
@@ -448,6 +454,8 @@ def _ctype_bits(s):
     if version_info[0] < 3:
         if isinstance(s, str):
             return s
+        elif isinstance(s, unicode): # pylint: disable=undefined-variable
+            return s.decode('utf-8')
         else:
             raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
     else:
@@ -468,7 +476,7 @@ def _hex_encode(buf):
     return hexlify(buf).decode('ascii')
 
 #
-# Versions
+# Versioning
 #
 def version_major():
     return int(_DLL.botan_version_major())
@@ -479,9 +487,23 @@ 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
 #
@@ -836,12 +858,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 +954,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 +961,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 +988,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 +1002,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 +1092,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): # pylint: disable=redefined-outer-name
+        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)
@@ -1012,7 +1185,7 @@ class PKKeyAgreement(object):
     def __init__(self, key, kdf_name):
         self.__obj = c_void_p(0)
         flags = c_uint32(0) # always zero in this ABI
-        _DLL.botan_pk_op_key_agreement_create(byref(self.__obj), key.handle_(), kdf_name, flags)
+        _DLL.botan_pk_op_key_agreement_create(byref(self.__obj), key.handle_(), _ctype_str(kdf_name), flags)
 
         self.m_public_value = _call_fn_returning_vec(
             0, lambda b, bl: _DLL.botan_pk_op_key_agreement_export_public(key.handle_(), b, bl))
@@ -1023,7 +1196,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 +1381,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


Test patch:
diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py
index 1ba21f570..34629398f 100644
--- a/src/scripts/test_python.py
+++ b/src/scripts/test_python.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 
 """
-(C) 2015,2017,2018 Jack Lloyd
+(C) 2015,2017,2018,2019 Jack Lloyd
 
 Botan is released under the Simplified BSD License (see license.txt)
 """
@@ -17,6 +17,7 @@ def hex_decode(buf):
     return binascii.unhexlify(buf.encode('ascii'))
 
 class BotanPythonTests(unittest.TestCase):
+    # pylint: disable=too-many-public-methods
 
     def test_version(self):
         version_str = botan2.version_string()
@@ -25,6 +26,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 +193,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 +243,49 @@ ofvkP1EDmpx50fHLawIDAQAB
         rsapub = botan2.PublicKey.load(rsa_pub_pem)
         self.assertEqual(rsapub.to_pem(), rsa_pub_pem)
 
+        n = 0xB5AD8818DCA1F256FF8FAB0888D0667D95DF2098B0D201A4C75590D3EBDFA159DD91C64AFDA082609EF885B2D1F4DC055C8FF9FA371C2F3398E0B612C603151131C81DB322C8D15E53EB56B4DF7325F05046889CB25021DE4282E16B9B28F5CBB2B8DDECE0F8E4E8A77F674F26AE92B7220920A1FBE43F51039A9C79D1F1CB6B # pylint: disable=line-too-long
+        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()
+        priv = botan2.PrivateKey.create('RSA', '1024', rng)
+        passphrase = "super secret tell noone"
+
+        for is_pem in [True, False]:
+            ref_val = priv.export(is_pem)
+
+            enc1 = priv.export_encrypted(passphrase, rng, True, msec=10)
+            dec1 = botan2.PrivateKey.load(enc1, passphrase)
+            self.assertEqual(dec1.export(is_pem), ref_val)
+
+            pem2 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/SIV")
+            dec2 = botan2.PrivateKey.load(pem2, passphrase)
+            self.assertEqual(dec2.export(is_pem), ref_val)
+
+            pem3 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/GCM", pbkdf="Scrypt")
+            dec3 = botan2.PrivateKey.load(pem3, passphrase)
+            self.assertEqual(dec3.export(is_pem), ref_val)
+
+    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 +337,108 @@ ofvkP1EDmpx50fHLawIDAQAB
         verify.update('message')
         self.assertTrue(verify.check_signature(sig))
 
-    def test_dh(self):
+    def test_ecdsa(self):
+        rng = botan2.RandomNumberGenerator()
+
+        hash_fn = '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_fn)
+        signer.update(msg)
+        signature = signer.finish(rng)
+
+        verifier = botan2.PKVerify(pub, hash_fn)
+        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_fn)
+        verifier.update(msg)
+        self.assertTrue(verifier.check_signature(signature))
+
+        priv2 = botan2.PrivateKey.load_ecdsa(group, priv.get_field('x'))
+        signer = botan2.PKSign(priv2, hash_fn)
+        # sign empty message
+        signature = signer.finish(rng)
+
+        # verify empty message
+        self.assertTrue(verifier.check_signature(signature))
+
+    def test_sm2(self):
+        rng = botan2.RandomNumberGenerator()
+
+        hash_fn = 'EMSA1(SM3)'
+        group = 'sm2p256v1'
+        msg = 'test message'
+
+        priv = botan2.PrivateKey.create('SM2', 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_fn)
+        signer.update(msg)
+        signature = signer.finish(rng)
+
+        verifier = botan2.PKVerify(pub, hash_fn)
+        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_sm2(group, pub_x, pub_y)
+        verifier = botan2.PKVerify(pub2, hash_fn)
+        verifier.update(msg)
+        self.assertTrue(verifier.check_signature(signature))
+
+        priv2 = botan2.PrivateKey.load_sm2(group, priv.get_field('x'))
+        signer = botan2.PKSign(priv2, hash_fn)
+        # sign empty message
+        signature = signer.finish(rng)
+
+        # verify empty message
+        self.assertTrue(verifier.check_signature(signature))
+
+    def test_ecdh(self):
+        # pylint: disable=too-many-locals
         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)
+        kdf = 'KDF2(SHA-384)'
+
+        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 +471,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>