Add helpers to match characters in CBS Needed for URI parsing in crypto/x509. Change-Id: I15c04d535d2453538c38502e127c295a6a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/94307 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc index 95719c0..0e36a2e 100644 --- a/crypto/bytestring/bytestring_test.cc +++ b/crypto/bytestring/bytestring_test.cc
@@ -153,6 +153,54 @@ EXPECT_EQ(CBS_len(&data), sizeof(kData) - 2); } +TEST(CBSTest, GetUntilFirstOf) { + static const uint8_t kData[] = {0, 'a', 'b', 'c', 0, 'a', 'b', 'c'}; + CBS data; + CBS_init(&data, kData, sizeof(kData)); + + CBS prefix; + EXPECT_FALSE(CBS_get_until_first_of(&data, &prefix, "A")); + EXPECT_EQ(CBS_data(&data), kData); + EXPECT_EQ(CBS_len(&data), sizeof(kData)); + + ASSERT_TRUE(CBS_get_until_first_of(&data, &prefix, "Abc")); + EXPECT_EQ(CBS_data(&prefix), kData); + EXPECT_EQ(CBS_len(&prefix), 2u); + EXPECT_EQ(CBS_data(&data), kData + 2); + EXPECT_EQ(CBS_len(&data), sizeof(kData) - 2); +} + +TEST(CBSTest, GetUntilFirstNotOf) { + { + static const uint8_t kData[] = {'a', 'b', 'c', 'd', 'a', 'b', 'c'}; + CBS data; + CBS_init(&data, kData, sizeof(kData)); + + CBS prefix; + EXPECT_FALSE(CBS_get_until_first_not_of(&data, &prefix, "abcd")); + EXPECT_EQ(CBS_data(&data), kData); + EXPECT_EQ(CBS_len(&data), sizeof(kData)); + + ASSERT_TRUE(CBS_get_until_first_not_of(&data, &prefix, "abcD")); + EXPECT_EQ(CBS_data(&prefix), kData); + EXPECT_EQ(CBS_len(&prefix), 3u); + EXPECT_EQ(CBS_data(&data), kData + 3); + EXPECT_EQ(CBS_len(&data), sizeof(kData) - 3); + } + { + static const uint8_t kData[] = {'a', 'b', 'c', 0, 'a', 'b', 'c'}; + CBS data; + CBS_init(&data, kData, sizeof(kData)); + + CBS prefix; + EXPECT_TRUE(CBS_get_until_first_not_of(&data, &prefix, "abcd")); + EXPECT_EQ(CBS_data(&prefix), kData); + EXPECT_EQ(CBS_len(&prefix), 3u); + EXPECT_EQ(CBS_data(&data), kData + 3); + EXPECT_EQ(CBS_len(&data), sizeof(kData) - 3); + } +} + TEST(CBSTest, GetASN1) { static const uint8_t kData1[] = {0x30, 2, 1, 2}; static const uint8_t kData2[] = {0x30, 3, 1, 2};
diff --git a/crypto/bytestring/cbs.cc b/crypto/bytestring/cbs.cc index 167a0cd..dbaa3e8 100644 --- a/crypto/bytestring/cbs.cc +++ b/crypto/bytestring/cbs.cc
@@ -215,6 +215,42 @@ return CBS_get_bytes(cbs, out, split - CBS_data(cbs)); } +int CBS_get_until_first_of(CBS *cbs, CBS *out, const char *chars) { + size_t pos = 0; + while (pos < CBS_len(cbs)) { + uint8_t c = CBS_data(cbs)[pos]; + // Special-case for \0 characters. We don't want to match on a null byte, + // even though strchr will happily return the \0 at the end of `chars`. + if (!c || !strchr(chars, c)) { + pos++; + } else { + break; + } + } + if (pos == CBS_len(cbs)) { + return 0; + } + return CBS_get_bytes(cbs, out, pos); +} + +int CBS_get_until_first_not_of(CBS *cbs, CBS *out, const char *chars) { + size_t pos = 0; + while (pos < CBS_len(cbs)) { + uint8_t c = CBS_data(cbs)[pos]; + // Special-case for \0 characters. We don't want to match on a null byte, + // even though strchr will happily return the \0 at the end of `chars`. + if (c && strchr(chars, c)) { + pos++; + } else { + break; + } + } + if (pos == CBS_len(cbs)) { + return 0; + } + return CBS_get_bytes(cbs, out, pos); +} + int CBS_get_u64_decimal(CBS *cbs, uint64_t *out) { uint64_t v = 0; int seen_digit = 0;
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h index b484710..3e1cfe5 100644 --- a/include/openssl/bytestring.h +++ b/include/openssl/bytestring.h
@@ -161,6 +161,20 @@ // one. Otherwise, it returns zero and leaves |cbs| unmodified. OPENSSL_EXPORT int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c); +// CBS_get_until_first_of finds the first byte in |cbs| matching one of the +// characters in |chars|, which is a NUL-terminated C string. If found, it sets +// |*out| to the text before the match, advances |cbs| over it, and returns one. +// Otherwise, it returns zero and leaves |cbs| unmodified. +OPENSSL_EXPORT int CBS_get_until_first_of(CBS *cbs, CBS *out, + const char *chars); + +// CBS_get_until_first_not_of finds the first byte in |cbs| that does not match +// any of the characters in |chars|, which is a NUL-terminated C string. If +// found, it sets |*out| to the text before the match, advances |cbs| over it, +// and returns one. Otherwise, it returns zero and leaves |cbs| unmodified. +OPENSSL_EXPORT int CBS_get_until_first_not_of(CBS *cbs, CBS *out, + const char *chars); + // CBS_get_u64_decimal reads a decimal integer from |cbs| and writes it to // |*out|. It stops reading at the end of the string, or the first non-digit // character. It returns one on success and zero on error. This function behaves
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h index 9d0b61b..c255e35 100644 --- a/include/openssl/prefix_symbols.h +++ b/include/openssl/prefix_symbols.h
@@ -575,6 +575,8 @@ #pragma redefine_extname CBS_get_u8_length_prefixed BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_u8_length_prefixed) #pragma redefine_extname CBS_get_ucs2_be BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_ucs2_be) #pragma redefine_extname CBS_get_until_first BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_until_first) +#pragma redefine_extname CBS_get_until_first_not_of BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_until_first_not_of) +#pragma redefine_extname CBS_get_until_first_of BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_until_first_of) #pragma redefine_extname CBS_get_utf32_be BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_utf32_be) #pragma redefine_extname CBS_get_utf8 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_utf8) #pragma redefine_extname CBS_is_unsigned_asn1_integer BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_is_unsigned_asn1_integer) @@ -3674,6 +3676,8 @@ #define CBS_get_u8_length_prefixed BORINGSSL_ADD_PREFIX(CBS_get_u8_length_prefixed) #define CBS_get_ucs2_be BORINGSSL_ADD_PREFIX(CBS_get_ucs2_be) #define CBS_get_until_first BORINGSSL_ADD_PREFIX(CBS_get_until_first) +#define CBS_get_until_first_not_of BORINGSSL_ADD_PREFIX(CBS_get_until_first_not_of) +#define CBS_get_until_first_of BORINGSSL_ADD_PREFIX(CBS_get_until_first_of) #define CBS_get_utf32_be BORINGSSL_ADD_PREFIX(CBS_get_utf32_be) #define CBS_get_utf8 BORINGSSL_ADD_PREFIX(CBS_get_utf8) #define CBS_is_unsigned_asn1_integer BORINGSSL_ADD_PREFIX(CBS_is_unsigned_asn1_integer)