Fix |ASN1_INTEGER_set| when setting zero.
|ASN1_INTEGER_set| and |BN_to_ASN1_INTEGER| disagree about how to encode
zero. OpenSSL master has aligned around the behaviour of the latter
(i.e. a single zero byte) so fix |ASN1_INTEGER_set| to do that. (This is
also the form that DER requires.)
At the same time, fix undefined behaviour when negative a |long| whose
value is |LONG_MIN|.
Change-Id: I1198de35e61a286ac6472e99152f3d22fda59044
Reviewed-on: https://boringssl-review.googlesource.com/24485
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c
index b53c00b..dd74550 100644
--- a/crypto/asn1/a_int.c
+++ b/crypto/asn1/a_int.c
@@ -347,40 +347,16 @@
int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
{
- int j, k;
- unsigned int i;
- unsigned char buf[sizeof(long) + 1];
- long d;
-
- a->type = V_ASN1_INTEGER;
- if (a->length < (int)(sizeof(long) + 1)) {
- if (a->data != NULL)
- OPENSSL_free(a->data);
- if ((a->data =
- (unsigned char *)OPENSSL_malloc(sizeof(long) + 1)) != NULL)
- OPENSSL_memset((char *)a->data, 0, sizeof(long) + 1);
- }
- if (a->data == NULL) {
- OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
- return (0);
- }
- d = v;
- if (d < 0) {
- d = -d;
- a->type = V_ASN1_NEG_INTEGER;
+ if (v >= 0) {
+ return ASN1_INTEGER_set_uint64(a, (uint64_t) v);
}
- for (i = 0; i < sizeof(long); i++) {
- if (d == 0)
- break;
- buf[i] = (int)d & 0xff;
- d >>= 8;
+ if (!ASN1_INTEGER_set_uint64(a, 0 - (uint64_t) v)) {
+ return 0;
}
- j = 0;
- for (k = i - 1; k >= 0; k--)
- a->data[j++] = buf[k];
- a->length = j;
- return (1);
+
+ a->type = V_ASN1_NEG_INTEGER;
+ return 1;
}
int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v)
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 55c0f17..7c114dd 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -15,6 +15,7 @@
#include <stdio.h>
#include <gtest/gtest.h>
+#include <limits.h>
#include <openssl/asn1.h>
#include <openssl/err.h>
@@ -67,23 +68,23 @@
bssl::UniquePtr<ASN1_INTEGER> by_uint64(M_ASN1_INTEGER_new());
bssl::UniquePtr<BIGNUM> bn(BN_new());
- const std::vector<unsigned> kValues = {
- 0, 1, 2, 0xff, 0x100, 0xffff, 0x10000,
+ const std::vector<int64_t> kValues = {
+ LONG_MIN, -2, -1, 0, 1, 2, 0xff, 0x100, 0xffff, 0x10000, LONG_MAX,
};
for (const auto &i : kValues) {
SCOPED_TRACE(i);
ASSERT_EQ(1, ASN1_INTEGER_set(by_long.get(), i));
- ASSERT_EQ(1, ASN1_INTEGER_set_uint64(by_uint64.get(), i));
- ASSERT_TRUE(BN_set_word(bn.get(), i));
+ const uint64_t abs = i < 0 ? (0 - (uint64_t) i) : i;
+ ASSERT_TRUE(BN_set_u64(bn.get(), abs));
+ BN_set_negative(bn.get(), i < 0);
ASSERT_TRUE(BN_to_ASN1_INTEGER(bn.get(), by_bn.get()));
- if (i != 0) {
- // |ASN1_INTEGER_set| and |BN_to_ASN1_INTEGER| disagree about how to
- // encode zero. The former leaves an empty value while the latter encodes
- // as a single zero byte.
- EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_long.get()));
+ EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_long.get()));
+
+ if (i >= 0) {
+ ASSERT_EQ(1, ASN1_INTEGER_set_uint64(by_uint64.get(), i));
+ EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_uint64.get()));
}
- EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_uint64.get()));
}
}