Repository: randombit/botan
Pull request #2208 (2019-12-02 16:09:54)

Implementing a few functions missing in the Python binding
I tried to add another bit of functionality mentioned in #1906.

randombit: Thanks change looks good. There are two pylint warnings that need to be addressed https://travis-ci.org/randombit/botan/jobs/619691102

The trailing whitespace should be removed. The too-many-public-methods warnings should just be disabled within the test file

Patch:
diff --git a/doc/api_ref/python.rst b/doc/api_ref/python.rst
index 80c1ca89d..1fda54193 100644
--- a/doc/api_ref/python.rst
+++ b/doc/api_ref/python.rst
@@ -474,17 +474,19 @@ Multiple Precision Integers (MPI)
 -------------------------------------
 .. versionadded:: 2.8.0
 
-.. py:class:: MPI(initial_value=None)
+.. py:class:: MPI(initial_value=None, radix=None)
 
    Initialize an MPI object with specified value, left as zero otherwise.  The
    ``initial_value`` should be an ``int``, ``str``, or ``MPI``.
+   The ``radix`` value should be set to 16 when initializing from a base 16 `str` value.
+
 
    Most of the usual arithmetic operators (``__add__``, ``__mul__``, etc) are
    defined.
 
    .. py:method:: inverse_mod(modulus)
 
-      Return the inverse of ``self`` modulo modulus, or zero if no inverse exists
+      Return the inverse of ``self`` modulo ``modulus``, or zero if no inverse exists
 
    .. py:method:: is_prime(rng, prob=128)
 
@@ -494,6 +496,15 @@ Multiple Precision Integers (MPI)
 
       Return ``self`` to the ``exponent`` power modulo ``modulus``
 
+   .. py:method:: mod_mul(other, modulus):
+
+      Return the multiplication product of ``self`` and ``other`` modulo ``modulus``
+
+   .. py:method:: gcd(other):
+
+      Return the greatest common divisor of ``self`` and ``other``
+
+
 Format Preserving Encryption (FE1 scheme)
 -----------------------------------------
 .. versionadded:: 2.8.0
diff --git a/src/python/botan2.py b/src/python/botan2.py
index 2018cae20..dcff583a9 100755
--- a/src/python/botan2.py
+++ b/src/python/botan2.py
@@ -1415,9 +1415,9 @@ class X509Cert(object): # pylint: disable=invalid-name
     def validation_status(cls, error_code):
         return _ctype_to_str(_DLL.botan_x509_cert_validation_status(c_int(error_code)))
 
-class MPI(object):
+class MPI(object): # pylint: disable=too-many-public-methods
 
-    def __init__(self, initial_value=None):
+    def __init__(self, initial_value=None, radix=None):
 
         self.__obj = c_void_p(0)
         _DLL.botan_mp_init(byref(self.__obj))
@@ -1426,6 +1426,8 @@ class MPI(object):
             pass # left as zero
         elif isinstance(initial_value, MPI):
             _DLL.botan_mp_set_from_mp(self.__obj, initial_value.handle_())
+        elif radix is not None:
+            _DLL.botan_mp_set_from_radix_str(self.__obj, _ctype_str(initial_value), c_size_t(radix))
         elif isinstance(initial_value, str):
             _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(initial_value))
         else:
@@ -1591,6 +1593,16 @@ class MPI(object):
         _DLL.botan_mp_rshift(self.__obj, self.__obj, shift)
         return self
 
+    def mod_mul(self, other, modulus):
+        r = MPI()
+        _DLL.botan_mp_mod_mul(r.handle_(), self.__obj, other.handle_(), modulus.handle_())
+        return r
+
+    def gcd(self, other):
+        r = MPI()
+        _DLL.botan_mp_gcd(r.handle_(), self.__obj, other.handle_())
+        return r
+
     def pow_mod(self, exponent, modulus):
         r = MPI()
         _DLL.botan_mp_powmod(r.handle_(), self.__obj, exponent.handle_(), modulus.handle_())


Test patch:
diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py
index 60cb80ec5..ad1be89ce 100644
--- a/src/scripts/test_python.py
+++ b/src/scripts/test_python.py
@@ -507,11 +507,13 @@ ofvkP1EDmpx50fHLawIDAQAB
         big = botan2.MPI('0x85839682368923476892367235')
         self.assertEqual(big.bit_count(), 104)
         small = botan2.MPI(0xDEADBEEF)
+        radix = botan2.MPI("DEADBEEF", 16)
 
         self.assertEqual(hex_encode(small.to_bytes()), "deadbeef")
         self.assertEqual(hex_encode(big.to_bytes()), "85839682368923476892367235")
 
         self.assertEqual(int(small), 0xDEADBEEF)
+        self.assertEqual(int(radix), int(small))
 
         self.assertEqual(int(small >> 16), 0xDEAD)
 
@@ -577,6 +579,20 @@ ofvkP1EDmpx50fHLawIDAQAB
         p = inv.pow_mod(botan2.MPI(46), mod)
         self.assertEqual(int(p), 42)
 
+        one = botan2.MPI(1)
+        twelve = botan2.MPI("C", 16)
+        eight = botan2.MPI(8)
+
+        mul = twelve.mod_mul(eight, inv)
+        self.assertEqual(int(mul), 27)
+
+        gcd = one.gcd(one)
+        self.assertEqual(one, gcd)
+        gcd = one.gcd(twelve)
+        self.assertEqual(one, gcd)
+        gcd = twelve.gcd(eight)
+        self.assertEqual(4, int(gcd))
+
     def test_mpi_random(self):
         rng = botan2.RandomNumberGenerator()