summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crypto/bn/bn_lib.c128
-rw-r--r--doc/man3/BN_bn2bin.pod42
-rw-r--r--include/openssl/bn.h6
-rw-r--r--util/libcrypto.num6
4 files changed, 150 insertions, 32 deletions
diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c
index a08286cab1..9b19a7243c 100644
--- a/crypto/bn/bn_lib.c
+++ b/crypto/bn/bn_lib.c
@@ -431,13 +431,15 @@ int BN_set_word(BIGNUM *a, BN_ULONG w)
}
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)
+ endianess_t endianess, signedness_t signedness)
{
int inc;
const unsigned char *s2;
int inc2;
+ int neg = 0, xor = 0, carry = 0;
unsigned int i;
unsigned int n;
BIGNUM *bn = NULL;
@@ -467,13 +469,30 @@ static BIGNUM *bin2bn(const unsigned char *s, int len, BIGNUM *ret,
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 (zero for unsigned numbers).
+ * 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;
+ /*
+ * 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;
@@ -484,14 +503,16 @@ static BIGNUM *bin2bn(const unsigned char *s, int len, BIGNUM *ret,
return NULL;
}
ret->top = n;
- ret->neg = 0;
+ 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 = *s;
+ 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;
@@ -506,33 +527,56 @@ static BIGNUM *bin2bn(const unsigned char *s, int len, BIGNUM *ret,
BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
{
- return bin2bn(s, len, ret, BIG);
+ 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)
+{
+ 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;
+ 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;
}
@@ -562,9 +606,14 @@ int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen,
lasti = atop - 1;
atop = a->top * BN_BYTES;
for (i = 0, j = 0; j < (size_t)tolen; j++) {
+ unsigned char byte, byte_xored;
+
l = a->d[i / BN_BYTES];
mask = 0 - ((j - atop) >> (8 * sizeof(i) - 1));
- *to = (unsigned char)(l >> (8 * (i % BN_BYTES)) & mask);
+ 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 */
}
@@ -576,24 +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)
{
- return bin2bn(s, len, ret, LITTLE);
+ return bin2bn(s, len, ret, LITTLE, UNSIGNED);
+}
+
+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)
@@ -605,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;
@@ -614,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/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/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: