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)