[openssl] master update

Richard Levitte levitte at openssl.org
Thu Jan 20 17:00:31 UTC 2022


The branch master has been updated
       via  5288303da96084b41b062d99eb37177fb4cf471e (commit)
       via  f5e8050fdcf2083825ef450d51bfacac21d2730e (commit)
       via  c30de601850f367e4c16ad91c0168a2e0dc647c0 (commit)
       via  99d3349d6f4b62c89d0cbcd6200cbc9bda388c52 (commit)
       via  4e26fe508bf18732983212ca4749eabb1f02e142 (commit)
       via  c2cab43574dbb65094d6caf4dc1bf691e826a4fc (commit)
      from  2d280fe016a98b57d488f42fd3941bcd61407c5a (commit)


- Log -----------------------------------------------------------------
commit 5288303da96084b41b062d99eb37177fb4cf471e
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Nov 25 18:00:16 2021 +0100

    TEST: Add a test of the new BN_signed set of functions in test/bntest.c
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

commit f5e8050fdcf2083825ef450d51bfacac21d2730e
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Nov 25 17:55:41 2021 +0100

    Add signed bn2bin and bin2bn functions
    
    This adds the functions BN_signed_bin2bn(), BN_signed_bn2bin(),
    BN_signed_lebin2bn(), BN_signed_bn2lebin(), BN_signed_native2bn(),
    and BN_signed_bn2native(), all essentially doing the same job as
    BN_bin2bn(), BN_bn2binpad(), BN_lebin2bn(), BN_bn2lebinpad(),
    BN_native2bn(), and BN_bn2nativepad(), except that the 'signed'
    ones operate on signed number bins in 2's complement form.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

commit c30de601850f367e4c16ad91c0168a2e0dc647c0
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Nov 24 08:23:02 2021 +0100

    [refactor] BIGNUM: Modify bin2bn() to work from least to most significant chunk
    
    This will make it easier to introduce the possibility for signed input
    numbers.
    
    We also refactor the inner loop to simplify the calculation of each
    bignum chunk.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

commit 99d3349d6f4b62c89d0cbcd6200cbc9bda388c52
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Nov 24 07:19:00 2021 +0100

    BIGNUM: Add a comment on chunk order in struct bignum_st
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

commit 4e26fe508bf18732983212ca4749eabb1f02e142
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Nov 24 07:16:09 2021 +0100

    [refactor] BIGNUM: Modify bn2binpad()'s setup to be more like bin2bn()'s
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

commit c2cab43574dbb65094d6caf4dc1bf691e826a4fc
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Nov 24 07:10:13 2021 +0100

    [refactor] BIGNUM: collapse BN_bin2bn() and BN_lebin2bn() into one
    
    BN_lebin2bn() is a block copy of BN_bin2bn() with just a couple of
    very minute details changed.  For better maintainability, we collapse
    them into the internal function bn2bin(), and change BN_bin2bn() and
    BN_lebin2bn() to become simple wrappers.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/17139)

-----------------------------------------------------------------------

Summary of changes:
 crypto/bn/bn_lib.c     | 253 +++++++++++++++++++++++++++++++++----------------
 crypto/bn/bn_local.h   |   7 +-
 doc/man3/BN_bn2bin.pod |  42 +++++---
 include/openssl/bn.h   |   6 ++
 test/bntest.c          | 132 ++++++++++++++++++++++++++
 util/libcrypto.num     |   6 ++
 6 files changed, 350 insertions(+), 96 deletions(-)

diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c
index d37b89c2a6..9b19a7243c 100644
--- a/crypto/bn/bn_lib.c
+++ b/crypto/bn/bn_lib.c
@@ -430,11 +430,18 @@ int BN_set_word(BIGNUM *a, BN_ULONG w)
     return 1;
 }
 
-BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
+typedef enum {BIG, LITTLE} endianess_t;
+typedef enum {SIGNED, UNSIGNED} signedness_t;
+
+static BIGNUM *bin2bn(const unsigned char *s, int len, BIGNUM *ret,
+                      endianess_t endianess, signedness_t signedness)
 {
-    unsigned int i, m;
+    int inc;
+    const unsigned char *s2;
+    int inc2;
+    int neg = 0, xor = 0, carry = 0;
+    unsigned int i;
     unsigned int n;
-    BN_ULONG l;
     BIGNUM *bn = NULL;
 
     if (ret == NULL)
@@ -442,30 +449,73 @@ BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
     if (ret == NULL)
         return NULL;
     bn_check_top(ret);
-    /* Skip leading zero's. */
-    for ( ; len > 0 && *s == 0; s++, len--)
+
+    /*
+     * The loop that does the work iterates from least to most
+     * significant BIGNUM chunk, so we adapt parameters to tranfer
+     * input bytes accordingly.
+     */
+    switch (endianess) {
+    case LITTLE:
+        s2 = s + len - 1;
+        inc2 = -1;
+        inc = 1;
+        break;
+    case BIG:
+        s2 = s;
+        inc2 = 1;
+        inc = -1;
+        s += len - 1;
+        break;
+    }
+
+    /* Take note of the signedness of the input bytes*/
+    if (signedness == SIGNED) {
+        neg = !!(*s2 & 0x80);
+        xor = neg ? 0xff : 0x00;
+        carry = neg;
+    }
+
+    /*
+     * Skip leading sign extensions (the value of |xor|).
+     * This is the only spot where |s2| and |inc2| are used.
+     */
+    for ( ; len > 0 && *s2 == xor; s2 += inc2, len--)
         continue;
-    n = len;
-    if (n == 0) {
+
+    /*
+     * If there was a set of 0xff, we backtrack one byte unless the next
+     * one has a sign bit, as the last 0xff is then part of the actual
+     * number, rather then a mere sign extension.
+     */
+    if (xor == 0xff) {
+        if (len == 0 || !(*s2 & 0x80))
+            len++;
+    }
+    /* If it was all zeros, we're done */
+    if (len == 0) {
         ret->top = 0;
         return ret;
     }
-    i = ((n - 1) / BN_BYTES) + 1;
-    m = ((n - 1) % (BN_BYTES));
-    if (bn_wexpand(ret, (int)i) == NULL) {
+    n = ((len - 1) / BN_BYTES) + 1; /* Number of resulting bignum chunks */
+    if (!ossl_assert(bn_wexpand(ret, (int)n) != NULL)) {
         BN_free(bn);
         return NULL;
     }
-    ret->top = i;
-    ret->neg = 0;
-    l = 0;
-    while (n--) {
-        l = (l << 8L) | *(s++);
-        if (m-- == 0) {
-            ret->d[--i] = l;
-            l = 0;
-            m = BN_BYTES - 1;
+    ret->top = n;
+    ret->neg = neg;
+    for (i = 0; n-- > 0; i++) {
+        BN_ULONG l = 0;        /* Accumulator */
+        unsigned int m = 0;    /* Offset in a bignum chunk, in bits */
+
+        for (; len > 0 && m < BN_BYTES * 8; len--, s += inc, m += 8) {
+            BN_ULONG byte_xored = *s ^ xor;
+            BN_ULONG byte = (byte_xored + carry) & 0xff;
+
+            carry = byte_xored > byte; /* Implicit 1 or 0 */
+            l |= (byte << m);
         }
+        ret->d[i] = l;
     }
     /*
      * need to call this due to clear byte at top if avoiding having the top
@@ -475,30 +525,58 @@ BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
     return ret;
 }
 
-typedef enum {big, little} endianess_t;
+BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
+{
+    return bin2bn(s, len, ret, BIG, UNSIGNED);
+}
 
-/* ignore negative */
-static
-int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen, endianess_t endianess)
+BIGNUM *BN_signed_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
 {
-    int n;
+    return bin2bn(s, len, ret, BIG, SIGNED);
+}
+
+static int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen,
+                     endianess_t endianess, signedness_t signedness)
+{
+    int inc;
+    int n, n8;
+    int xor = 0, carry = 0, ext = 0;
     size_t i, lasti, j, atop, mask;
     BN_ULONG l;
 
     /*
-     * In case |a| is fixed-top, BN_num_bytes can return bogus length,
+     * In case |a| is fixed-top, BN_num_bits can return bogus length,
      * but it's assumed that fixed-top inputs ought to be "nominated"
      * even for padded output, so it works out...
      */
-    n = BN_num_bytes(a);
+    n8 = BN_num_bits(a);
+    n = (n8 + 7) / 8;           /* This is what BN_num_bytes() does */
+
+    /* Take note of the signedness of the bignum */
+    if (signedness == SIGNED) {
+        xor = a->neg ? 0xff : 0x00;
+        carry = a->neg;
+
+        /*
+         * if |n * 8 == n|, then the MSbit is set, otherwise unset.
+         * We must compensate with one extra byte if that doesn't
+         * correspond to the signedness of the bignum with regards
+         * to 2's complement.
+         */
+        ext = (n * 8 == n8)
+            ? !a->neg            /* MSbit set on nonnegative bignum */
+            : a->neg;            /* MSbit unset on negative bignum */
+    }
+
     if (tolen == -1) {
-        tolen = n;
-    } else if (tolen < n) {     /* uncommon/unlike case */
+        tolen = n + ext;
+    } else if (tolen < n + ext) { /* uncommon/unlike case */
         BIGNUM temp = *a;
 
         bn_correct_top(&temp);
-        n = BN_num_bytes(&temp);
-        if (tolen < n)
+        n8 = BN_num_bits(&temp);
+        n = (n8 + 7) / 8;       /* This is what BN_num_bytes() does */
+        if (tolen < n + ext)
             return -1;
     }
 
@@ -510,19 +588,33 @@ int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen, endianess_t endiane
         return tolen;
     }
 
+    /*
+     * The loop that does the work iterates from least significant
+     * to most significant BIGNUM limb, so we adapt parameters to
+     * tranfer output bytes accordingly.
+     */
+    switch (endianess) {
+    case LITTLE:
+        inc = 1;
+        break;
+    case BIG:
+        inc = -1;
+        to += tolen - 1;         /* Move to the last byte, not beyond */
+        break;
+    }
+
     lasti = atop - 1;
     atop = a->top * BN_BYTES;
-    if (endianess == big)
-        to += tolen; /* start from the end of the buffer */
     for (i = 0, j = 0; j < (size_t)tolen; j++) {
-        unsigned char val;
+        unsigned char byte, byte_xored;
+
         l = a->d[i / BN_BYTES];
         mask = 0 - ((j - atop) >> (8 * sizeof(i) - 1));
-        val = (unsigned char)(l >> (8 * (i % BN_BYTES)) & mask);
-        if (endianess == big)
-            *--to = val;
-        else
-            *to++ = val;
+        byte = (unsigned char)(l >> (8 * (i % BN_BYTES)) & mask);
+        byte_xored = byte ^ xor;
+        *to = (unsigned char)(byte_xored + carry);
+        carry = byte_xored > *to; /* Implicit 1 or 0 */
+        to += inc;
         i += (i - lasti) >> (8 * sizeof(i) - 1); /* stay on last limb */
     }
 
@@ -533,66 +625,43 @@ int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
 {
     if (tolen < 0)
         return -1;
-    return bn2binpad(a, to, tolen, big);
+    return bn2binpad(a, to, tolen, BIG, UNSIGNED);
+}
+
+int BN_signed_bn2bin(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    if (tolen < 0)
+        return -1;
+    return bn2binpad(a, to, tolen, BIG, SIGNED);
 }
 
 int BN_bn2bin(const BIGNUM *a, unsigned char *to)
 {
-    return bn2binpad(a, to, -1, big);
+    return bn2binpad(a, to, -1, BIG, UNSIGNED);
 }
 
 BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret)
 {
-    unsigned int i, m;
-    unsigned int n;
-    BN_ULONG l;
-    BIGNUM *bn = NULL;
+    return bin2bn(s, len, ret, LITTLE, UNSIGNED);
+}
 
-    if (ret == NULL)
-        ret = bn = BN_new();
-    if (ret == NULL)
-        return NULL;
-    bn_check_top(ret);
-    s += len;
-    /* Skip trailing zeroes. */
-    for ( ; len > 0 && s[-1] == 0; s--, len--)
-        continue;
-    n = len;
-    if (n == 0) {
-        ret->top = 0;
-        return ret;
-    }
-    i = ((n - 1) / BN_BYTES) + 1;
-    m = ((n - 1) % (BN_BYTES));
-    if (bn_wexpand(ret, (int)i) == NULL) {
-        BN_free(bn);
-        return NULL;
-    }
-    ret->top = i;
-    ret->neg = 0;
-    l = 0;
-    while (n--) {
-        s--;
-        l = (l << 8L) | *s;
-        if (m-- == 0) {
-            ret->d[--i] = l;
-            l = 0;
-            m = BN_BYTES - 1;
-        }
-    }
-    /*
-     * need to call this due to clear byte at top if avoiding having the top
-     * bit set (-ve number)
-     */
-    bn_correct_top(ret);
-    return ret;
+BIGNUM *BN_signed_lebin2bn(const unsigned char *s, int len, BIGNUM *ret)
+{
+    return bin2bn(s, len, ret, LITTLE, SIGNED);
 }
 
 int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen)
 {
     if (tolen < 0)
         return -1;
-    return bn2binpad(a, to, tolen, little);
+    return bn2binpad(a, to, tolen, LITTLE, UNSIGNED);
+}
+
+int BN_signed_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    if (tolen < 0)
+        return -1;
+    return bn2binpad(a, to, tolen, LITTLE, SIGNED);
 }
 
 BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret)
@@ -604,6 +673,15 @@ BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret)
     return BN_bin2bn(s, len, ret);
 }
 
+BIGNUM *BN_signed_native2bn(const unsigned char *s, int len, BIGNUM *ret)
+{
+    DECLARE_IS_ENDIAN;
+
+    if (IS_LITTLE_ENDIAN)
+        return BN_signed_lebin2bn(s, len, ret);
+    return BN_signed_bin2bn(s, len, ret);
+}
+
 int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen)
 {
     DECLARE_IS_ENDIAN;
@@ -613,6 +691,15 @@ int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen)
     return BN_bn2binpad(a, to, tolen);
 }
 
+int BN_signed_bn2native(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    DECLARE_IS_ENDIAN;
+
+    if (IS_LITTLE_ENDIAN)
+        return BN_signed_bn2lebin(a, to, tolen);
+    return BN_signed_bn2bin(a, to, tolen);
+}
+
 int BN_ucmp(const BIGNUM *a, const BIGNUM *b)
 {
     int i;
diff --git a/crypto/bn/bn_local.h b/crypto/bn/bn_local.h
index 3c8534e1f7..94f647e671 100644
--- a/crypto/bn/bn_local.h
+++ b/crypto/bn/bn_local.h
@@ -223,8 +223,11 @@ BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
                       int num);
 
 struct bignum_st {
-    BN_ULONG *d;                /* Pointer to an array of 'BN_BITS2' bit
-                                 * chunks. */
+    BN_ULONG *d;                /*
+                                 * Pointer to an array of 'BN_BITS2' bit
+                                 * chunks. These chunks are organised in
+                                 * a least significant chunk first order.
+                                 */
     int top;                    /* Index of last used d +1. */
     /* The next are internal book keeping for bn_expand. */
     int dmax;                   /* Size of the d array. */
diff --git a/doc/man3/BN_bn2bin.pod b/doc/man3/BN_bn2bin.pod
index e75b9fffb5..3e5f2deeca 100644
--- a/doc/man3/BN_bn2bin.pod
+++ b/doc/man3/BN_bn2bin.pod
@@ -2,9 +2,10 @@
 
 =head1 NAME
 
-BN_bn2binpad,
-BN_bn2bin, BN_bin2bn, BN_bn2lebinpad, BN_lebin2bn,
-BN_bn2nativepad, BN_native2bn, BN_bn2hex, BN_bn2dec, BN_hex2bn, BN_dec2bn,
+BN_bn2binpad, BN_signed_bn2bin, BN_bn2bin, BN_bin2bn, BN_signed_bin2bn,
+BN_bn2lebinpad, BN_signed_bn2lebin, BN_lebin2bn, BN_signed_lebin2bn,
+BN_bn2nativepad, BN_signed_bn2native, BN_native2bn, BN_signed_native2bn,
+BN_bn2hex, BN_bn2dec, BN_hex2bn, BN_dec2bn,
 BN_print, BN_print_fp, BN_bn2mpi, BN_mpi2bn - format conversions
 
 =head1 SYNOPSIS
@@ -13,13 +14,19 @@ BN_print, BN_print_fp, BN_bn2mpi, BN_mpi2bn - format conversions
 
  int BN_bn2bin(const BIGNUM *a, unsigned char *to);
  int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
+ int BN_signed_bn2bin(const BIGNUM *a, unsigned char *to, int tolen);
  BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
+ BIGNUM *BN_signed_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
 
  int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen);
+ int BN_signed_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
  BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
+ BIGNUM *BN_signed_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
 
  int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen);
+ int BN_signed_bn2native(const BIGNUM *a, unsigned char *to, int tolen);
  BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret);
+ BIGNUM *BN_signed_native2bn(const unsigned char *s, int len, BIGNUM *ret);
 
  char *BN_bn2hex(const BIGNUM *a);
  char *BN_bn2dec(const BIGNUM *a);
@@ -43,17 +50,29 @@ and stores it at B<to>. B<tolen> indicates the length of the output buffer
 B<to>. The result is padded with zeros if necessary. If B<tolen> is less than
 BN_num_bytes(B<a>) an error is returned.
 
+BN_signed_bn2bin() converts the value of B<a> into big-endian signed 2's
+complements form and stores it at B<to>. B<tolen> indicates the length of
+the output buffer B<to>. The result is signed extended (padded with 0x00
+for positive numbers or with 0xff for negative numbers) if necessary.
+If B<tolen> is smaller than the necessary size (which may be
+C<<BN_num_bytes(B<a>) + 1>>), an error is returned.
+
 BN_bin2bn() converts the positive integer in big-endian form of length
 B<len> at B<s> into a B<BIGNUM> and places it in B<ret>. If B<ret> is
 NULL, a new B<BIGNUM> is created.
 
-BN_bn2lebinpad() and BN_lebin2bn() are identical to BN_bn2binpad() and
-BN_bin2bn() except the buffer is in little-endian format.
+BN_signed_bin2bn() converts the integer in big-endian signed 2's complement
+form of length B<len> at B<s> into a B<BIGNUM> and places it in B<ret>. If
+B<ret> is NULL, a new B<BIGNUM> is created.
+
+BN_bn2lebinpad(), BN_signed_bn2lebin() and BN_lebin2bn() are identical to
+BN_bn2binpad(), BN_signed_bn2bin() and BN_bin2bn() except the buffer is in
+little-endian format.
 
-BN_bn2nativepad() and BN_native2bn() are identical to BN_bn2binpad() and
-BN_bin2bn() except the buffer is in native format, i.e. most significant
-byte first on big-endian platforms, and least significant byte first on
-little-endian platforms.
+BN_bn2nativepad(), BN_signed_bn2native() and BN_native2bn() are identical
+to BN_bn2binpad(), BN_signed_bn2bin() and BN_bin2bn() except the buffer is
+in native format, i.e. most significant byte first on big-endian platforms,
+and least significant byte first on little-endian platforms.
 
 BN_bn2hex() and BN_bn2dec() return printable strings containing the
 hexadecimal and decimal encoding of B<a> respectively. For negative
@@ -91,8 +110,9 @@ if B<ret> is NULL.
 BN_bn2bin() returns the length of the big-endian number placed at B<to>.
 BN_bin2bn() returns the B<BIGNUM>, NULL on error.
 
-BN_bn2binpad(), BN_bn2lebinpad(), and BN_bn2nativepad() return the number of bytes written or -1 if the supplied
-buffer is too small.
+BN_bn2binpad(), BN_signed_bn2bin(), BN_bn2lebinpad(), BN_signed_bn2lebin(),
+BN_bn2nativepad(), and_signed BN_bn2native() return the number of bytes
+written or -1 if the supplied buffer is too small.
 
 BN_bn2hex() and BN_bn2dec() return a NUL-terminated string, or NULL
 on error. BN_hex2bn() and BN_dec2bn() return the number of characters
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index ecd7f01b9b..f80aad6cde 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -241,12 +241,18 @@ void BN_clear_free(BIGNUM *a);
 BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b);
 void BN_swap(BIGNUM *a, BIGNUM *b);
 BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
+BIGNUM *BN_signed_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
 int BN_bn2bin(const BIGNUM *a, unsigned char *to);
 int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
+int BN_signed_bn2bin(const BIGNUM *a, unsigned char *to, int tolen);
 BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
+BIGNUM *BN_signed_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
 int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen);
+int BN_signed_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
 BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret);
+BIGNUM *BN_signed_native2bn(const unsigned char *s, int len, BIGNUM *ret);
 int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen);
+int BN_signed_bn2native(const BIGNUM *a, unsigned char *to, int tolen);
 BIGNUM *BN_mpi2bn(const unsigned char *s, int len, BIGNUM *ret);
 int BN_bn2mpi(const BIGNUM *a, unsigned char *to);
 int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
diff --git a/test/bntest.c b/test/bntest.c
index 69506a0e35..efdb3ef963 100644
--- a/test/bntest.c
+++ b/test/bntest.c
@@ -1834,6 +1834,137 @@ static int test_bn2padded(void)
     return st;
 }
 
+static const MPITEST kSignedTests_BE[] = {
+    {"-1", "\xff", 1},
+    {"0", "", 0},
+    {"1", "\x01", 1},
+    /*
+     * The above cover the basics, now let's go for possible bignum
+     * chunk edges and other word edges (for a broad definition of
+     * "word", i.e. 1 byte included).
+     */
+    /* 1 byte edge */
+    {"127", "\x7f", 1},
+    {"-127", "\x81", 1},
+    {"128", "\x00\x80", 2},
+    {"-128", "\x80", 1},
+    {"129", "\x00\x81", 2},
+    {"-129", "\xff\x7f", 2},
+    {"255", "\x00\xff", 2},
+    {"-255", "\xff\x01", 2},
+    {"256", "\x01\x00", 2},
+    {"-256", "\xff\x00", 2},
+    /* 2 byte edge */
+    {"32767", "\x7f\xff", 2},
+    {"-32767", "\x80\x01", 2},
+    {"32768", "\x00\x80\x00", 3},
+    {"-32768", "\x80\x00", 2},
+    {"32769", "\x00\x80\x01", 3},
+    {"-32769", "\xff\x7f\xff", 3},
+    {"65535", "\x00\xff\xff", 3},
+    {"-65535", "\xff\x00\x01", 3},
+    {"65536", "\x01\x00\x00", 3},
+    {"-65536", "\xff\x00\x00", 3},
+    /* 4 byte edge */
+    {"2147483647", "\x7f\xff\xff\xff", 4},
+    {"-2147483647", "\x80\x00\x00\x01", 4},
+    {"2147483648", "\x00\x80\x00\x00\x00", 5},
+    {"-2147483648", "\x80\x00\x00\x00", 4},
+    {"2147483649", "\x00\x80\x00\x00\x01", 5},
+    {"-2147483649", "\xff\x7f\xff\xff\xff", 5},
+    {"4294967295", "\x00\xff\xff\xff\xff", 5},
+    {"-4294967295", "\xff\x00\x00\x00\x01", 5},
+    {"4294967296", "\x01\x00\x00\x00\x00", 5},
+    {"-4294967296", "\xff\x00\x00\x00\x00", 5},
+    /* 8 byte edge */
+    {"9223372036854775807", "\x7f\xff\xff\xff\xff\xff\xff\xff", 8},
+    {"-9223372036854775807", "\x80\x00\x00\x00\x00\x00\x00\x01", 8},
+    {"9223372036854775808", "\x00\x80\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"-9223372036854775808", "\x80\x00\x00\x00\x00\x00\x00\x00", 8},
+    {"9223372036854775809", "\x00\x80\x00\x00\x00\x00\x00\x00\x01", 9},
+    {"-9223372036854775809", "\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
+    {"18446744073709551615", "\x00\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+    {"-18446744073709551615", "\xff\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+    {"18446744073709551616", "\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"-18446744073709551616", "\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+};
+
+static int copy_reversed(uint8_t *dst, uint8_t *src, size_t len)
+{
+    for (dst += len - 1; len > 0; src++, dst--, len--)
+        *dst = *src;
+    return 1;
+}
+
+static int test_bn2signed(int i)
+{
+    uint8_t scratch[10], reversed[10];
+    const MPITEST *test = &kSignedTests_BE[i];
+    BIGNUM *bn = NULL, *bn2 = NULL;
+    int st = 0;
+
+    if (!TEST_ptr(bn = BN_new())
+        || !TEST_true(BN_asc2bn(&bn, test->base10)))
+        goto err;
+
+    /*
+     * Check BN_signed_bn2bin() / BN_signed_bin2bn()
+     * The interesting stuff happens in the last bytes of the buffers,
+     * the beginning is just padding (i.e. sign extension).
+     */
+    i = sizeof(scratch) - test->mpi_len;
+    if (!TEST_int_eq(BN_signed_bn2bin(bn, scratch, sizeof(scratch)),
+                     sizeof(scratch))
+        || !TEST_true(copy_reversed(reversed, scratch, sizeof(scratch)))
+        || !TEST_mem_eq(test->mpi, test->mpi_len, scratch + i, test->mpi_len))
+        goto err;
+
+    if (!TEST_ptr(bn2 = BN_signed_bin2bn(scratch, sizeof(scratch), NULL))
+        || !TEST_BN_eq(bn, bn2))
+        goto err;
+
+    BN_free(bn2);
+    bn2 = NULL;
+
+    /* Check that a parse of the reversed buffer works too */
+    if (!TEST_ptr(bn2 = BN_signed_lebin2bn(reversed, sizeof(reversed), NULL))
+        || !TEST_BN_eq(bn, bn2))
+        goto err;
+
+    BN_free(bn2);
+    bn2 = NULL;
+
+    /*
+     * Check BN_signed_bn2lebin() / BN_signed_lebin2bn()
+     * The interesting stuff happens in the first bytes of the buffers,
+     * the end is just padding (i.e. sign extension).
+     */
+    i = sizeof(reversed) - test->mpi_len;
+    if (!TEST_int_eq(BN_signed_bn2lebin(bn, scratch, sizeof(scratch)),
+                     sizeof(scratch))
+        || !TEST_true(copy_reversed(reversed, scratch, sizeof(scratch)))
+        || !TEST_mem_eq(test->mpi, test->mpi_len, reversed + i, test->mpi_len))
+        goto err;
+
+    if (!TEST_ptr(bn2 = BN_signed_lebin2bn(scratch, sizeof(scratch), NULL))
+        || !TEST_BN_eq(bn, bn2))
+        goto err;
+
+    BN_free(bn2);
+    bn2 = NULL;
+
+    /* Check that a parse of the reversed buffer works too */
+    if (!TEST_ptr(bn2 = BN_signed_bin2bn(reversed, sizeof(reversed), NULL))
+        || !TEST_BN_eq(bn, bn2))
+        goto err;
+
+    st = 1;
+ err:
+    BN_free(bn2);
+    BN_free(bn);
+    return st;
+}
+
 static int test_dec2bn(void)
 {
     BIGNUM *bn = NULL;
@@ -2998,6 +3129,7 @@ int setup_tests(void)
         ADD_TEST(test_hex2bn);
         ADD_TEST(test_asc2bn);
         ADD_ALL_TESTS(test_mpi, (int)OSSL_NELEM(kMPITests));
+        ADD_ALL_TESTS(test_bn2signed, (int)OSSL_NELEM(kSignedTests_BE));
         ADD_TEST(test_negzero);
         ADD_TEST(test_badmod);
         ADD_TEST(test_expmodzero);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 762e23a858..7b63154b55 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5428,3 +5428,9 @@ EVP_PKEY_CTX_get0_provider              5555	3_0_0	EXIST::FUNCTION:
 OSSL_STACK_OF_X509_free                 ?	3_1_0	EXIST::FUNCTION:
 EVP_MD_CTX_dup                          ?	3_1_0	EXIST::FUNCTION:
 EVP_CIPHER_CTX_dup                      ?	3_1_0	EXIST::FUNCTION:
+BN_signed_bin2bn                        ?	3_1_0	EXIST::FUNCTION:
+BN_signed_bn2bin                        ?	3_1_0	EXIST::FUNCTION:
+BN_signed_lebin2bn                      ?	3_1_0	EXIST::FUNCTION:
+BN_signed_bn2lebin                      ?	3_1_0	EXIST::FUNCTION:
+BN_signed_native2bn                     ?	3_1_0	EXIST::FUNCTION:
+BN_signed_bn2native                     ?	3_1_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list