Document ASN1_STRING_to_UTF8.
We already had a test, but move it to asn1_test.cc since it's part of
the ASN.1 library. Also, since it's easy, test it using public APIs
rather than stack-allocating an ASN1_STRING.
Change-Id: Ic77494e6c8f74584d159a600e334416197761475
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48227
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index a14a5cc..725542c 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -398,6 +398,64 @@
TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringEmpty);
}
+TEST(ASN1Test, StringToUTF8) {
+ static const struct {
+ std::vector<uint8_t> in;
+ int type;
+ const char *expected;
+ } kTests[] = {
+ // Non-minimal, two-byte UTF-8.
+ {{0xc0, 0x81}, V_ASN1_UTF8STRING, nullptr},
+ // Non-minimal, three-byte UTF-8.
+ {{0xe0, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
+ // Non-minimal, four-byte UTF-8.
+ {{0xf0, 0x80, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
+ // Truncated, four-byte UTF-8.
+ {{0xf0, 0x80, 0x80}, V_ASN1_UTF8STRING, nullptr},
+ // Low-surrogate value.
+ {{0xed, 0xa0, 0x80}, V_ASN1_UTF8STRING, nullptr},
+ // High-surrogate value.
+ {{0xed, 0xb0, 0x81}, V_ASN1_UTF8STRING, nullptr},
+ // Initial BOMs should be rejected from UCS-2 and UCS-4.
+ {{0xfe, 0xff, 0, 88}, V_ASN1_BMPSTRING, nullptr},
+ {{0, 0, 0xfe, 0xff, 0, 0, 0, 88}, V_ASN1_UNIVERSALSTRING, nullptr},
+ // Otherwise, BOMs should pass through.
+ {{0, 88, 0xfe, 0xff}, V_ASN1_BMPSTRING, "X\xef\xbb\xbf"},
+ {{0, 0, 0, 88, 0, 0, 0xfe, 0xff}, V_ASN1_UNIVERSALSTRING,
+ "X\xef\xbb\xbf"},
+ // The maximum code-point should pass though.
+ {{0, 16, 0xff, 0xfd}, V_ASN1_UNIVERSALSTRING, "\xf4\x8f\xbf\xbd"},
+ // Values outside the Unicode space should not.
+ {{0, 17, 0, 0}, V_ASN1_UNIVERSALSTRING, nullptr},
+ // Non-characters should be rejected.
+ {{0, 1, 0xff, 0xff}, V_ASN1_UNIVERSALSTRING, nullptr},
+ {{0, 1, 0xff, 0xfe}, V_ASN1_UNIVERSALSTRING, nullptr},
+ {{0, 0, 0xfd, 0xd5}, V_ASN1_UNIVERSALSTRING, nullptr},
+ // BMPString is UCS-2, not UTF-16, so surrogate pairs are invalid.
+ {{0xd8, 0, 0xdc, 1}, V_ASN1_BMPSTRING, nullptr},
+ };
+
+ for (const auto &test : kTests) {
+ SCOPED_TRACE(Bytes(test.in));
+ SCOPED_TRACE(test.type);
+ bssl::UniquePtr<ASN1_STRING> s(ASN1_STRING_type_new(test.type));
+ ASSERT_TRUE(s);
+ ASSERT_TRUE(ASN1_STRING_set(s.get(), test.in.data(), test.in.size()));
+
+ uint8_t *utf8;
+ const int utf8_len = ASN1_STRING_to_UTF8(&utf8, s.get());
+ EXPECT_EQ(utf8_len < 0, test.expected == nullptr);
+ if (utf8_len >= 0) {
+ if (test.expected != nullptr) {
+ EXPECT_EQ(Bytes(test.expected), Bytes(utf8, utf8_len));
+ }
+ OPENSSL_free(utf8);
+ } else {
+ ERR_clear_error();
+ }
+ }
+}
+
// 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/x509/a_strex.c b/crypto/x509/a_strex.c
index 2c4824e..752d74e 100644
--- a/crypto/x509/a_strex.c
+++ b/crypto/x509/a_strex.c
@@ -627,7 +627,7 @@
* in output string or a negative error code
*/
-int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
+int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
{
ASN1_STRING stmp, *str = &stmp;
int mbflag, type, ret;
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index a3c7b66..fde8bd5 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -1986,65 +1986,6 @@
EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 2)), 2);
}
-TEST(X509Test, StringDecoding) {
- static const struct {
- std::vector<uint8_t> in;
- int type;
- const char *expected;
- } kTests[] = {
- // Non-minimal, two-byte UTF-8.
- {{0xc0, 0x81}, V_ASN1_UTF8STRING, nullptr},
- // Non-minimal, three-byte UTF-8.
- {{0xe0, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
- // Non-minimal, four-byte UTF-8.
- {{0xf0, 0x80, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
- // Truncated, four-byte UTF-8.
- {{0xf0, 0x80, 0x80}, V_ASN1_UTF8STRING, nullptr},
- // Low-surrogate value.
- {{0xed, 0xa0, 0x80}, V_ASN1_UTF8STRING, nullptr},
- // High-surrogate value.
- {{0xed, 0xb0, 0x81}, V_ASN1_UTF8STRING, nullptr},
- // Initial BOMs should be rejected from UCS-2 and UCS-4.
- {{0xfe, 0xff, 0, 88}, V_ASN1_BMPSTRING, nullptr},
- {{0, 0, 0xfe, 0xff, 0, 0, 0, 88}, V_ASN1_UNIVERSALSTRING, nullptr},
- // Otherwise, BOMs should pass through.
- {{0, 88, 0xfe, 0xff}, V_ASN1_BMPSTRING, "X\xef\xbb\xbf"},
- {{0, 0, 0, 88, 0, 0, 0xfe, 0xff}, V_ASN1_UNIVERSALSTRING,
- "X\xef\xbb\xbf"},
- // The maximum code-point should pass though.
- {{0, 16, 0xff, 0xfd}, V_ASN1_UNIVERSALSTRING, "\xf4\x8f\xbf\xbd"},
- // Values outside the Unicode space should not.
- {{0, 17, 0, 0}, V_ASN1_UNIVERSALSTRING, nullptr},
- // Non-characters should be rejected.
- {{0, 1, 0xff, 0xff}, V_ASN1_UNIVERSALSTRING, nullptr},
- {{0, 1, 0xff, 0xfe}, V_ASN1_UNIVERSALSTRING, nullptr},
- {{0, 0, 0xfd, 0xd5}, V_ASN1_UNIVERSALSTRING, nullptr},
- // BMPString is UCS-2, not UTF-16, so surrogate pairs are invalid.
- {{0xd8, 0, 0xdc, 1}, V_ASN1_BMPSTRING, nullptr},
- };
-
- for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kTests); i++) {
- SCOPED_TRACE(i);
- const auto& test = kTests[i];
- ASN1_STRING s;
- s.type = test.type;
- s.data = const_cast<uint8_t*>(test.in.data());
- s.length = test.in.size();
-
- uint8_t *utf8;
- const int utf8_len = ASN1_STRING_to_UTF8(&utf8, &s);
- EXPECT_EQ(utf8_len < 0, test.expected == nullptr);
- if (utf8_len >= 0) {
- if (test.expected != nullptr) {
- EXPECT_EQ(Bytes(test.expected), Bytes(utf8, utf8_len));
- }
- OPENSSL_free(utf8);
- } else {
- ERR_clear_error();
- }
- }
-}
-
TEST(X509Test, NoBasicConstraintsCertSign) {
bssl::UniquePtr<X509> root(CertFromPEM(kSANTypesRoot));
bssl::UniquePtr<X509> intermediate(
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h
index fe2f29d..4d2368a 100644
--- a/include/openssl/asn1.h
+++ b/include/openssl/asn1.h
@@ -302,6 +302,13 @@
// |OPENSSL_malloc|.
OPENSSL_EXPORT void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len);
+// ASN1_STRING_to_UTF8 converts |in| to UTF-8. On success, sets |*out| to a
+// newly-allocated buffer containing the resulting string and returns the length
+// of the string. The caller must call |OPENSSL_free| to release |*out| when
+// done. On error, it returns a negative number.
+OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out,
+ const ASN1_STRING *in);
+
// TODO(davidben): Expand and document function prototypes generated in macros.
@@ -987,8 +994,6 @@
unsigned long flags);
#endif
-OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in);
-
OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
OPENSSL_EXPORT int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);