Add ASN1_BIT_STRING_set1 Right now there is no way, without messing with flags, for callers to set a BIT STRING with a non-whole number of bits. This matches the new OpenSSL 4.x API. Bug: 42290311 Change-Id: I43375efb651cf0e8e90d55116e74a733fd6dca3f Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/92570 Reviewed-by: Lily Chen <chlily@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_bitstr.cc b/crypto/asn1/a_bitstr.cc index f455733..b0134e5 100644 --- a/crypto/asn1/a_bitstr.cc +++ b/crypto/asn1/a_bitstr.cc
@@ -37,9 +37,29 @@ str->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; } -int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, const unsigned char *d, +int ASN1_BIT_STRING_set(ASN1_BIT_STRING *str, const uint8_t *data, ossl_ssize_t len) { - return ASN1_STRING_set(x, d, len); + return ASN1_STRING_set(str, data, len); +} + +int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *str, const uint8_t *data, + size_t length, int unused_bits) { + if (unused_bits < 0 || unused_bits > 7) { + OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BIT_STRING_BITS_LEFT); + return 0; + } + const uint8_t unused_bits_mask = (1 << unused_bits) - 1; + if ((length > 0 && (data[length - 1] & unused_bits_mask) != 0) || + (length == 0 && unused_bits != 0)) { + OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BIT_STRING_BITS_LEFT); + return 0; + } + if (!ASN1_STRING_set(str, data, length)) { + return 0; + } + str->type = V_ASN1_BIT_STRING; + set_unused_bits(str, unused_bits); + return 1; } uint8_t ASN1_BIT_STRING_unused_bits(const ASN1_BIT_STRING *str) { @@ -122,13 +142,7 @@ } } - if (!ASN1_STRING_set(out, CBS_data(&cbs), CBS_len(&cbs))) { - return 0; - } - - out->type = V_ASN1_BIT_STRING; - set_unused_bits(out, padding); - return 1; + return ASN1_BIT_STRING_set1(out, CBS_data(&cbs), CBS_len(&cbs), padding); } ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index db9d79f..b106a46 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc
@@ -967,6 +967,35 @@ ASN1_STRING_set(val.get(), kBytesf000, sizeof(kBytesf000))); static const uint8_t kBitStringf000[] = {0x03, 0x03, 0x00, 0xf0, 0x00}; TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringf000); + + // A thin wrapper to simplify getting pointer/length pairs to call the + // function. + auto set1 = [](ASN1_BIT_STRING *s, const std::vector<uint8_t> &data, + int unused_bits) { + return ASN1_BIT_STRING_set1(s, data.data(), data.size(), unused_bits); + }; + + // Setting a count of unused bits works. + ASSERT_TRUE(set1(val.get(), {0xf0}, 4)); + static const uint8_t kBitString1111[] = {0x03, 0x02, 0x04, 0xf0}; + TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1111); + + ASSERT_TRUE(set1(val.get(), {0xf0}, 0)); + static const uint8_t kBitString11110000[] = {0x03, 0x02, 0x00, 0xf0}; + TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString11110000); + + ASSERT_TRUE(set1(val.get(), {}, 0)); + static const uint8_t kBitStringEmpty[] = {0x03, 0x01, 0x00}; + TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringEmpty); + + // All unused bits must be zero. + EXPECT_FALSE(set1(val.get(), {0xff}, 1)); + EXPECT_FALSE(set1(val.get(), {0xf0}, 5)); + + // Invalid unused bit counts. + EXPECT_FALSE(set1(val.get(), {0x00, 0x00}, 8)); + EXPECT_FALSE(set1(val.get(), {0x00, 0x00}, -1)); + EXPECT_FALSE(set1(val.get(), {}, 1)); } TEST(ASN1Test, StringToUTF8) {
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index ac07a5d..b83e3cf 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h
@@ -911,10 +911,18 @@ // // TODO(crbug.com/42290311): This function, and |ASN1_STRING_set|, should reset // the number of unused bits. -OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *str, - const unsigned char *d, +OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *str, const uint8_t *data, ossl_ssize_t length); +// ASN1_BIT_STRING_set1 sets |str| to a BIT STRING containing |length| bytes +// from |data|. It returns one on success and zero on error. The least +// significant |unused_bits| of the last byte of |data| are removed from the bit +// string. The removed bits must all be zero. |unused_bits| must be between 0 +// and 7, and must be 0 if |length| is zero. +OPENSSL_EXPORT int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *str, + const uint8_t *data, size_t length, + int unused_bits); + // ASN1_BIT_STRING_set_bit sets bit |n| of |str| to one if |value| is non-zero // and zero if |value| is zero, resizing |str| as needed. It then truncates // trailing zeros in |str| to align with the DER representation for a bit string
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h index 7c55e99..12588ab 100644 --- a/include/openssl/prefix_symbols.h +++ b/include/openssl/prefix_symbols.h
@@ -61,6 +61,7 @@ #pragma redefine_extname ASN1_BIT_STRING_new BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_new) #pragma redefine_extname ASN1_BIT_STRING_num_bytes BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_num_bytes) #pragma redefine_extname ASN1_BIT_STRING_set BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_set) +#pragma redefine_extname ASN1_BIT_STRING_set1 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_set1) #pragma redefine_extname ASN1_BIT_STRING_set_bit BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_set_bit) #pragma redefine_extname ASN1_BIT_STRING_unused_bits BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BIT_STRING_unused_bits) #pragma redefine_extname ASN1_BMPSTRING_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(ASN1_BMPSTRING_free) @@ -3156,6 +3157,7 @@ #define ASN1_BIT_STRING_new BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_new) #define ASN1_BIT_STRING_num_bytes BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_num_bytes) #define ASN1_BIT_STRING_set BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_set) +#define ASN1_BIT_STRING_set1 BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_set1) #define ASN1_BIT_STRING_set_bit BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_set_bit) #define ASN1_BIT_STRING_unused_bits BORINGSSL_ADD_PREFIX(ASN1_BIT_STRING_unused_bits) #define ASN1_BMPSTRING_free BORINGSSL_ADD_PREFIX(ASN1_BMPSTRING_free)