Merge pull request #7489 from minosgalanakis/ecp/7246_xtrack_core_shift_l

[Bignum]: Introduce left shift from prototype
diff --git a/library/bignum_core.c b/library/bignum_core.c
index b0ffa37..de57cfc 100644
--- a/library/bignum_core.c
+++ b/library/bignum_core.c
@@ -366,6 +366,41 @@
     }
 }
 
+void mbedtls_mpi_core_shift_l(mbedtls_mpi_uint *X, size_t limbs,
+                              size_t count)
+{
+    size_t i, v0, v1;
+    mbedtls_mpi_uint r0 = 0, r1;
+
+    v0 = count / (biL);
+    v1 = count & (biL - 1);
+
+    /*
+     * shift by count / limb_size
+     */
+    if (v0 > 0) {
+        for (i = limbs; i > v0; i--) {
+            X[i - 1] = X[i - v0 - 1];
+        }
+
+        for (; i > 0; i--) {
+            X[i - 1] = 0;
+        }
+    }
+
+    /*
+     * shift by count % limb_size
+     */
+    if (v1 > 0) {
+        for (i = v0; i < limbs; i++) {
+            r1 = X[i] >> (biL - v1);
+            X[i] <<= v1;
+            X[i] |= r0;
+            r0 = r1;
+        }
+    }
+}
+
 mbedtls_mpi_uint mbedtls_mpi_core_add(mbedtls_mpi_uint *X,
                                       const mbedtls_mpi_uint *A,
                                       const mbedtls_mpi_uint *B,
diff --git a/library/bignum_core.h b/library/bignum_core.h
index 158d2b3..21a5a11 100644
--- a/library/bignum_core.h
+++ b/library/bignum_core.h
@@ -281,7 +281,7 @@
                               unsigned char *output,
                               size_t output_length);
 
-/** \brief              Shift an MPI right in place by a number of bits.
+/** \brief              Shift an MPI in-place right by a number of bits.
  *
  *                      Shifting by more bits than there are bit positions
  *                      in \p X is valid and results in setting \p X to 0.
@@ -297,6 +297,21 @@
                               size_t count);
 
 /**
+ * \brief               Shift an MPI in-place left by a number of bits.
+ *
+ *                      Shifting by more bits than there are bit positions
+ *                      in \p X will produce an unspecified result.
+ *
+ *                      This function's execution time depends on the value
+ *                      of \p count (and of course \p limbs).
+ * \param[in,out] X     The number to shift.
+ * \param limbs         The number of limbs of \p X. This must be at least 1.
+ * \param count         The number of bits to shift by.
+ */
+void mbedtls_mpi_core_shift_l(mbedtls_mpi_uint *X, size_t limbs,
+                              size_t count);
+
+/**
  * \brief Add two fixed-size large unsigned integers, returning the carry.
  *
  * Calculates `A + B` where `A` and `B` have the same size.
diff --git a/scripts/mbedtls_dev/bignum_common.py b/scripts/mbedtls_dev/bignum_common.py
index d8ef4a8..51b25a3 100644
--- a/scripts/mbedtls_dev/bignum_common.py
+++ b/scripts/mbedtls_dev/bignum_common.py
@@ -80,6 +80,23 @@
     """ Retrun the hex digits need for a number of limbs. """
     return 2 * (limbs * bits_in_limb // 8)
 
+def hex_digits_max_int(val: str, bits_in_limb: int) -> int:
+    """ Return the first number exceeding maximum  the limb space
+    required to store the input hex-string value. This method
+    weights on the input str_len rather than numerical value
+    and works with zero-padded inputs"""
+    n = ((1 << (len(val) * 4)) - 1)
+    l = limbs_mpi(n, bits_in_limb)
+    return bound_mpi_limbs(l, bits_in_limb)
+
+def zfill_match(reference: str, target: str) -> str:
+    """ Zero pad target hex-string to match the limb size of
+    the reference input """
+    lt = len(target)
+    lr = len(reference)
+    target_len = lr if lt < lr else lt
+    return "{:x}".format(int(target, 16)).zfill(target_len)
+
 class OperationCommon(test_data_generation.BaseTest):
     """Common features for bignum binary operations.
 
diff --git a/scripts/mbedtls_dev/bignum_core.py b/scripts/mbedtls_dev/bignum_core.py
index 5801cae..ff3fd23 100644
--- a/scripts/mbedtls_dev/bignum_core.py
+++ b/scripts/mbedtls_dev/bignum_core.py
@@ -68,6 +68,68 @@
             for count in counts:
                 yield cls(input_hex, descr, count).create_test_case()
 
+
+class BignumCoreShiftL(BignumCoreTarget, bignum_common.ModOperationCommon):
+    """Test cases for mbedtls_bignum_core_shift_l()."""
+
+    BIT_SHIFT_VALUES = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
+                        '1f', '20', '21', '3f', '40', '41', '47', '48', '4f',
+                        '50', '51', '58', '80', '81', '88']
+    DATA = ["0", "1", "40", "dee5ca1a7ef10a75", "a1055eb0bb1efa1150ff",
+            "002e7ab0070ad57001", "020100000000000000001011121314151617",
+            "1946e2958a85d8863ae21f4904fcc49478412534ed53eaf321f63f2a222"
+            "7a3c63acbf50b6305595f90cfa8327f6db80d986fe96080bcbb5df1bdbe"
+            "9b74fb8dedf2bddb3f8215b54dffd66409323bcc473e45a8fe9d08e77a51"
+            "1698b5dad0416305db7fcf"]
+    arity = 1
+    test_function = "mpi_core_shift_l"
+    test_name = "Core shift(L)"
+    input_style = "arch_split"
+    symbol = "<<"
+    input_values = BIT_SHIFT_VALUES
+    moduli = DATA
+
+    @property
+    def val_n_max_limbs(self) -> int:
+        """ Return the limb count required to store the maximum number that can
+        fit in a the number of digits used by val_n """
+        m = bignum_common.hex_digits_max_int(self.val_n, self.bits_in_limb) - 1
+        return bignum_common.limbs_mpi(m, self.bits_in_limb)
+
+    def arguments(self) -> List[str]:
+        return [bignum_common.quote_str(self.val_n),
+                str(self.int_a)
+                ] + self.result()
+
+    def description(self) -> str:
+        """ Format the output as:
+        #{count} {hex input} ({input bits} {limbs capacity}) << {bit shift} """
+        bits = "({} bits in {} limbs)".format(self.int_n.bit_length(), self.val_n_max_limbs)
+        return "{} #{} {} {} {} {}".format(self.test_name,
+                                           self.count,
+                                           self.val_n,
+                                           bits,
+                                           self.symbol,
+                                           self.int_a)
+
+    def format_result(self, res: int) -> str:
+        # Override to match zero-pading for leading digits between the output and input.
+        res_str = bignum_common.zfill_match(self.val_n, "{:x}".format(res))
+        return bignum_common.quote_str(res_str)
+
+    def result(self) -> List[str]:
+        result = (self.int_n << self.int_a)
+        # Calculate if there is space for shifting to the left(leading zero limbs)
+        mx = bignum_common.hex_digits_max_int(self.val_n, self.bits_in_limb)
+        # If there are empty limbs ahead, adjust the bitmask accordingly
+        result = result & (mx - 1)
+        return [self.format_result(result)]
+
+    @property
+    def is_valid(self) -> bool:
+        return True
+
+
 class BignumCoreCTLookup(BignumCoreTarget, test_data_generation.BaseTest):
     """Test cases for mbedtls_mpi_core_ct_uint_table_lookup()."""
     test_function = "mpi_core_ct_uint_table_lookup"
diff --git a/tests/suites/test_suite_bignum_core.function b/tests/suites/test_suite_bignum_core.function
index 53aa002..81a3a45 100644
--- a/tests/suites/test_suite_bignum_core.function
+++ b/tests/suites/test_suite_bignum_core.function
@@ -563,6 +563,26 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void mpi_core_shift_l(char *input, int count, char *result)
+{
+    mbedtls_mpi_uint *X = NULL;
+    mbedtls_mpi_uint *Y = NULL;
+    size_t limbs, n;
+
+    TEST_EQUAL(0, mbedtls_test_read_mpi_core(&X, &limbs, input));
+    TEST_EQUAL(0, mbedtls_test_read_mpi_core(&Y, &n, result));
+    TEST_EQUAL(limbs, n);
+
+    mbedtls_mpi_core_shift_l(X, limbs, count);
+    ASSERT_COMPARE(X, limbs * ciL, Y, limbs * ciL);
+
+exit:
+    mbedtls_free(X);
+    mbedtls_free(Y);
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void mpi_core_add_and_add_if(char *input_A, char *input_B,
                              char *input_S, int carry)
 {