Rewrite ASN1_item_pack and ASN1_item_unpack. ASN1_item_unpack was missing checks for trailing data. ASN1_item_pack's error handling was all wrong. (Leaking the temporary on error, checking the the wrong return value for i2d, would-be redundant check for NULL, were the other check not wrong.) Update-Note: ASN1_item_unpack now checks for trailing data. Change-Id: Ibaa19ba2b264fca36dd21109e66f9558d373c58b Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/49927 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index dee685a..cd47d2b 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc
@@ -1418,6 +1418,66 @@ EXPECT_EQ(nullptr, null_type->value.ptr); } +TEST(ASN1Test, Pack) { + bssl::UniquePtr<BASIC_CONSTRAINTS> val(BASIC_CONSTRAINTS_new()); + ASSERT_TRUE(val); + val->ca = 0; + + // Test all three calling conventions. + static const uint8_t kExpected[] = {0x30, 0x00}; + bssl::UniquePtr<ASN1_STRING> str( + ASN1_item_pack(val.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS), nullptr)); + ASSERT_TRUE(str); + EXPECT_EQ( + Bytes(ASN1_STRING_get0_data(str.get()), ASN1_STRING_length(str.get())), + Bytes(kExpected)); + + ASN1_STRING *raw = nullptr; + str.reset(ASN1_item_pack(val.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS), &raw)); + ASSERT_TRUE(str); + EXPECT_EQ(raw, str.get()); + EXPECT_EQ( + Bytes(ASN1_STRING_get0_data(str.get()), ASN1_STRING_length(str.get())), + Bytes(kExpected)); + + str.reset(ASN1_STRING_new()); + ASSERT_TRUE(str); + raw = str.get(); + EXPECT_TRUE( + ASN1_item_pack(val.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS), &raw)); + EXPECT_EQ(raw, str.get()); + EXPECT_EQ( + Bytes(ASN1_STRING_get0_data(str.get()), ASN1_STRING_length(str.get())), + Bytes(kExpected)); +} + +TEST(ASN1Test, Unpack) { + bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_new()); + ASSERT_TRUE(str); + + static const uint8_t kValid[] = {0x30, 0x00}; + ASSERT_TRUE( + ASN1_STRING_set(str.get(), kValid, sizeof(kValid))); + bssl::UniquePtr<BASIC_CONSTRAINTS> val(static_cast<BASIC_CONSTRAINTS *>( + ASN1_item_unpack(str.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS)))); + ASSERT_TRUE(val); + EXPECT_EQ(val->ca, 0); + EXPECT_EQ(val->pathlen, nullptr); + + static const uint8_t kInvalid[] = {0x31, 0x00}; + ASSERT_TRUE(ASN1_STRING_set(str.get(), kInvalid, sizeof(kInvalid))); + val.reset(static_cast<BASIC_CONSTRAINTS *>( + ASN1_item_unpack(str.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS)))); + EXPECT_FALSE(val); + + static const uint8_t kTraiilingData[] = {0x30, 0x00, 0x00}; + ASSERT_TRUE( + ASN1_STRING_set(str.get(), kTraiilingData, sizeof(kTraiilingData))); + val.reset(static_cast<BASIC_CONSTRAINTS *>( + ASN1_item_unpack(str.get(), ASN1_ITEM_rptr(BASIC_CONSTRAINTS)))); + EXPECT_FALSE(val); +} + // The ASN.1 macros do not work on Windows shared library builds, where usage of // |OPENSSL_EXPORT| is a bit stricter. #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/crypto/asn1/asn_pack.c b/crypto/asn1/asn_pack.c index fd7ab4b..9124f23 100644 --- a/crypto/asn1/asn_pack.c +++ b/crypto/asn1/asn_pack.c
@@ -59,48 +59,43 @@ #include <openssl/err.h> #include <openssl/mem.h> -/* ASN1_ITEM versions of the above */ -ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_STRING **oct) +ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_STRING **out) { - ASN1_STRING *octmp; - - if (!oct || !*oct) { - if (!(octmp = ASN1_STRING_new())) { - OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE); - return NULL; - } - if (oct) - *oct = octmp; - } else - octmp = *oct; - - if (octmp->data) { - OPENSSL_free(octmp->data); - octmp->data = NULL; - } - - if (!(octmp->length = ASN1_item_i2d(obj, &octmp->data, it))) { + uint8_t *new_data = NULL; + int len = ASN1_item_i2d(obj, &new_data, it); + if (len <= 0) { OPENSSL_PUT_ERROR(ASN1, ASN1_R_ENCODE_ERROR); return NULL; } - if (!octmp->data) { - OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE); - return NULL; - } - return octmp; -} -/* Extract an ASN1 object from an ASN1_STRING */ + ASN1_STRING *ret = NULL; + if (out == NULL || *out == NULL) { + ret = ASN1_STRING_new(); + if (ret == NULL) { + OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE); + OPENSSL_free(new_data); + return NULL; + } + } else { + ret = *out; + } + + ASN1_STRING_set0(ret, new_data, len); + if (out != NULL) { + *out = ret; + } + return ret; +} void *ASN1_item_unpack(const ASN1_STRING *oct, const ASN1_ITEM *it) { - const unsigned char *p; - void *ret; - - p = oct->data; - if (!(ret = ASN1_item_d2i(NULL, &p, oct->length, it))) + const unsigned char *p = oct->data; + void *ret = ASN1_item_d2i(NULL, &p, oct->length, it); + if (ret == NULL || p != oct->data + oct->length) { OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR); - /* TODO(davidben): Check for trailing data. */ + ASN1_item_free(ret, it); + return NULL; + } return ret; }