diff --git a/crypto/crypto_test.cc b/crypto/crypto_test.cc index 9ceff77d4e..52a51bc736 100644 --- a/crypto/crypto_test.cc +++ b/crypto/crypto_test.cc @@ -73,6 +73,25 @@ TEST(CryptoTest, Strndup) { EXPECT_STREQ("", str.get()); } +TEST(CryptoTest, OPENSSL_hexstr2buf) { + const char *test_cases[][2] = {{"a2", "\xa2"}, + {"a213", "\xa2\x13"}, + {"ffeedd", "\xff\xee\xdd"}, + {"10aab1c2", "\x10\xaa\xb1\xc2"}}; + + for (auto test_case : test_cases) { + const char *test_value = test_case[0]; + const char *expected_answer = test_case[1]; + size_t actual_answer_len = 0; + // The longest test case we have is currently 4 bytes long + size_t expected_answer_len = OPENSSL_strnlen(test_case[1], 5); + unsigned char *buf = OPENSSL_hexstr2buf(test_value, &actual_answer_len); + EXPECT_EQ(expected_answer_len, actual_answer_len); + EXPECT_EQ(0, OPENSSL_memcmp(buf, expected_answer, expected_answer_len)); + OPENSSL_free(buf); + } +} + #if defined(BORINGSSL_FIPS_COUNTERS) using CounterArray = size_t[fips_counter_max + 1]; diff --git a/crypto/mem.c b/crypto/mem.c index efe42dbee3..6bf94151b3 100644 --- a/crypto/mem.c +++ b/crypto/mem.c @@ -414,6 +414,36 @@ int OPENSSL_fromxdigit(uint8_t *out, int c) { return 0; } +uint8_t *OPENSSL_hexstr2buf(const char *str, size_t *len) { + if (str == NULL || len == NULL) { + return NULL; + } + + const size_t slen = OPENSSL_strnlen(str, INT16_MAX); + if (slen % 2 != 0) { + return NULL; + } + + const size_t buflen = slen / 2; + uint8_t *buf = OPENSSL_zalloc(buflen); + if (buf == NULL) { + return NULL; + } + + for (size_t i = 0; i < buflen; i++) { + uint8_t hi, lo; + if (!OPENSSL_fromxdigit(&hi, str[2 * i]) || + !OPENSSL_fromxdigit(&lo, str[2 * i + 1])) { + OPENSSL_free(buf); + return NULL; + } + buf[i] = (hi << 4) | lo; + } + + *len = buflen; + return buf; +} + int OPENSSL_isalnum(int c) { return OPENSSL_isalpha(c) || OPENSSL_isdigit(c); } int OPENSSL_tolower(int c) { diff --git a/include/openssl/mem.h b/include/openssl/mem.h index 4092066bc6..d306758a99 100644 --- a/include/openssl/mem.h +++ b/include/openssl/mem.h @@ -145,6 +145,13 @@ OPENSSL_EXPORT int OPENSSL_isxdigit(int c); // zero is returned. OPENSSL_EXPORT int OPENSSL_fromxdigit(uint8_t *out, int c); +// OPENSSL_hexstr2buf allocates and returns a buffer containing the bytes +// represented by the hexadecimal string |str|. |str| must be a NULL terminated +// string of hex characters. The length of the buffer is stored in |*len|. +// |len| must not be NULL. The caller must free the returned +// buffer with |OPENSSL_free|. If |str| is malformed, NULL is returned. +OPENSSL_EXPORT uint8_t *OPENSSL_hexstr2buf(const char *str, size_t *len); + // OPENSSL_isalnum is a locale-independent, ASCII-only version of isalnum(3), It // only recognizes what |OPENSSL_isalpha| and |OPENSSL_isdigit| recognize. OPENSSL_EXPORT int OPENSSL_isalnum(int c);