Expose CBS_get_asn1_oid_component Only needed in libssl actually, but we have CBB_add_asn1_oid_component, so this is a natural counterpart. Bug: 524722668 Change-Id: I14c1a4935919f2ddca971e925dc57c413e0234c4 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98487 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Lily Chen <chlily@google.com>
diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc index 5054052..113637c 100644 --- a/crypto/bytestring/bytestring_test.cc +++ b/crypto/bytestring/bytestring_test.cc
@@ -16,6 +16,7 @@ #include <stdlib.h> #include <string.h> +#include <optional> #include <vector> #include <gtest/gtest.h> @@ -2186,5 +2187,65 @@ } } +TEST(CBSTest, OIDComponent) { + const struct { + // enc is an encoded OID component. + std::vector<uint8_t> enc; + // v is the decoded OID component, or nullopt if invalid. + std::optional<uint64_t> v; + } kTests[] = { + // The empty string is not an OID component. + {{}, std::nullopt}, + // Some valid OID components. + {{0x00}, 0}, + {{0x01}, 1}, + {{0x7f}, 127}, + {{0x81, 0x00}, 128}, + {{0x81, 0x01}, 129}, + {{0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + UINT64_MAX}, + // 2^64 is too large and overflows. + {{0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00}, + std::nullopt}, + // Non-minimal OID components are invalid. + {{0x80, 0x01}, std::nullopt}, + {{0x80, 0x81, 0x00}, std::nullopt}, + {{0x80, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + std::nullopt}, + // Truncated OID component. + {{0xff}, std::nullopt}, + {{0x81}, std::nullopt}, + }; + for (const auto &t : kTests) { + SCOPED_TRACE(Bytes(t.enc)); + CBS cbs; + CBS_init(&cbs, t.enc.data(), t.enc.size()); + uint64_t v; + if (!t.v.has_value()) { + EXPECT_FALSE(CBS_get_asn1_oid_component(&cbs, &v)); + continue; + } + + ASSERT_TRUE(CBS_get_asn1_oid_component(&cbs, &v)); + EXPECT_EQ(v, t.v.value()); + EXPECT_EQ(CBS_len(&cbs), 0u); + + // Trailing data should be left in `cbs`. + std::vector<uint8_t> trailing_data = t.enc; + trailing_data.push_back(42); + CBS_init(&cbs, trailing_data.data(), trailing_data.size()); + ASSERT_TRUE(CBS_get_asn1_oid_component(&cbs, &v)); + EXPECT_EQ(v, t.v.value()); + EXPECT_EQ(CBS_data(&cbs), trailing_data.data() + t.enc.size()); + EXPECT_EQ(CBS_len(&cbs), 1u); + + // Test serialization. + ScopedCBB cbb; + ASSERT_TRUE(CBB_init(cbb.get(), t.enc.size())); + ASSERT_TRUE(CBB_add_asn1_oid_component(cbb.get(), t.v.value())); + EXPECT_EQ(Bytes(CBBAsSpan(cbb.get())), Bytes(t.enc)); + } +} + } // namespace BSSL_NAMESPACE_END
diff --git a/crypto/bytestring/cbs.cc b/crypto/bytestring/cbs.cc index 82f74af..7abf10b 100644 --- a/crypto/bytestring/cbs.cc +++ b/crypto/bytestring/cbs.cc
@@ -277,10 +277,7 @@ return seen_digit; } -// parse_base128_integer reads a big-endian base-128 integer from `cbs` and sets -// `*out` to the result. This is the encoding used in DER for both high tag -// number form and OID components. -static int parse_base128_integer(CBS *cbs, uint64_t *out) { +int CBS_get_asn1_oid_component(CBS *cbs, uint64_t *out) { uint64_t v = 0; uint8_t b; do { @@ -319,8 +316,9 @@ CBS_ASN1_TAG tag = ((CBS_ASN1_TAG)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT; CBS_ASN1_TAG tag_number = tag_byte & 0x1f; if (tag_number == 0x1f) { + // High tag numbers are encoded in the same format as OID components. uint64_t v; - if (!parse_base128_integer(cbs, &v) || + if (!CBS_get_asn1_oid_component(cbs, &v) || // Check the tag number is within our supported bounds. v > CBS_ASN1_TAG_NUMBER_MASK || // Small tag numbers should have used low tag number form, even in BER. @@ -747,9 +745,9 @@ uint8_t v, prev = 0; while (CBS_get_u8(©, &v)) { // OID encodings are a sequence of minimally-encoded base-128 integers (see - // `parse_base128_integer`). If `prev`'s MSB was clear, it was the last byte - // of an integer (or `v` is the first byte). `v` is then the first byte of - // the next integer. If first byte of an integer is 0x80, it is not + // `CBS_get_asn1_oid_component`). If `prev`'s MSB was clear, it was the last + // byte of an integer (or `v` is the first byte). `v` is then the first byte + // of the next integer. If first byte of an integer is 0x80, it is not // minimally-encoded. if ((prev & 0x80) == 0 && v == 0x80) { return 0; @@ -770,7 +768,7 @@ // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2. uint64_t v; - if (!parse_base128_integer(©, &v)) { + if (!CBS_get_asn1_oid_component(©, &v)) { goto err; } @@ -785,7 +783,7 @@ } while (CBS_len(©) != 0) { - if (!parse_base128_integer(©, &v) || !CBB_add_u8(&cbb, '.') || + if (!CBS_get_asn1_oid_component(©, &v) || !CBB_add_u8(&cbb, '.') || !add_decimal(&cbb, v)) { goto err; } @@ -817,12 +815,12 @@ // Relative OIDs must have at least one component. uint64_t v; - if (!parse_base128_integer(©, &v) || !add_decimal(cbb.get(), v)) { + if (!CBS_get_asn1_oid_component(©, &v) || !add_decimal(cbb.get(), v)) { return nullptr; } while (CBS_len(©) != 0) { - if (!parse_base128_integer(©, &v) || !CBB_add_u8(cbb.get(), '.') || + if (!CBS_get_asn1_oid_component(©, &v) || !CBB_add_u8(cbb.get(), '.') || !add_decimal(cbb.get(), v)) { return nullptr; }
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h index 359f764..7005074 100644 --- a/include/openssl/bytestring.h +++ b/include/openssl/bytestring.h
@@ -429,6 +429,16 @@ // OID components are too large. OPENSSL_EXPORT char *CBS_asn1_relative_oid_to_text(const CBS *cbs); +// CBS_get_asn1_oid_component gets a single OID component from `cbs`. On +// success, it sets `*out` to the value, advances `cbs` past the component, and +// returns one. If the input does not contain a valid OID component or if the +// OID component is bigger than `uint64_t`, it returns zero. +// +// Specifically, this decodes a non-empty, minimal-width, base-128, big-endian +// integer. The high bit of each byte is set for continuation bytes and unset +// for the final byte. This encoding is also used for DER tag numbers 31 and up. +OPENSSL_EXPORT int CBS_get_asn1_oid_component(CBS *cbs, uint64_t *out); + // CBS_parse_generalized_time returns one if `cbs` is a valid DER-encoded, ASN.1 // GeneralizedTime body within the limitations imposed by RFC 5280, or zero // otherwise. If `allow_timezone_offset` is non-zero, four-digit timezone @@ -706,6 +716,11 @@ // CBB_add_asn1_oid_component appends a single OID component to `cbb`. // It returns one on success and zero on error. +// +// Specifically, this encodes `value` as a non-empty, minimal-width, base-128, +// big-endian integer. The high bit of each byte is set for continuation bytes +// and unset for the final byte. This encoding is also used for DER tag numbers +// 31 and up. OPENSSL_EXPORT int CBB_add_asn1_oid_component(CBB *cbb, uint64_t value); // CBB_flush_asn1_set_of calls `CBB_flush` on `cbb` and then reorders the
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h index 48d5081..317d396 100644 --- a/include/openssl/prefix_symbols.h +++ b/include/openssl/prefix_symbols.h
@@ -554,6 +554,7 @@ #pragma redefine_extname CBS_get_asn1_element BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_element) #pragma redefine_extname CBS_get_asn1_int64 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_int64) #pragma redefine_extname CBS_get_asn1_int64_with_tag BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_int64_with_tag) +#pragma redefine_extname CBS_get_asn1_oid_component BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_oid_component) #pragma redefine_extname CBS_get_asn1_uint64 BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_uint64) #pragma redefine_extname CBS_get_asn1_uint64_with_tag BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_asn1_uint64_with_tag) #pragma redefine_extname CBS_get_bytes BORINGSSL_ADD_USER_LABEL_AND_PREFIX(CBS_get_bytes) @@ -3673,6 +3674,7 @@ #define CBS_get_asn1_element BORINGSSL_ADD_PREFIX(CBS_get_asn1_element) #define CBS_get_asn1_int64 BORINGSSL_ADD_PREFIX(CBS_get_asn1_int64) #define CBS_get_asn1_int64_with_tag BORINGSSL_ADD_PREFIX(CBS_get_asn1_int64_with_tag) +#define CBS_get_asn1_oid_component BORINGSSL_ADD_PREFIX(CBS_get_asn1_oid_component) #define CBS_get_asn1_uint64 BORINGSSL_ADD_PREFIX(CBS_get_asn1_uint64) #define CBS_get_asn1_uint64_with_tag BORINGSSL_ADD_PREFIX(CBS_get_asn1_uint64_with_tag) #define CBS_get_bytes BORINGSSL_ADD_PREFIX(CBS_get_bytes)