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>
This commit is contained in:
David Benjamin
2026-07-07 18:23:05 -04:00
committed by boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com
parent b35f9f1d1a
commit a22861da5e
4 changed files with 88 additions and 12 deletions
+61
View File
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <string.h>
#include <optional>
#include <vector>
#include <gtest/gtest.h>
@@ -2186,5 +2187,65 @@ TEST(CBSTest, GetU64Decimal) {
}
}
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
+10 -12
View File
@@ -277,10 +277,7 @@ int CBS_get_u64_decimal(CBS *cbs, uint64_t *out) {
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 @@ static int parse_asn1_tag(CBS *cbs, CBS_ASN1_TAG *out) {
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 @@ int CBS_is_valid_asn1_oid(const CBS *cbs) {
uint8_t v, prev = 0;
while (CBS_get_u8(&copy, &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 @@ char *CBS_asn1_oid_to_text(const CBS *cbs) {
// The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
uint64_t v;
if (!parse_base128_integer(&copy, &v)) {
if (!CBS_get_asn1_oid_component(&copy, &v)) {
goto err;
}
@@ -785,7 +783,7 @@ char *CBS_asn1_oid_to_text(const CBS *cbs) {
}
while (CBS_len(&copy) != 0) {
if (!parse_base128_integer(&copy, &v) || !CBB_add_u8(&cbb, '.') ||
if (!CBS_get_asn1_oid_component(&copy, &v) || !CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v)) {
goto err;
}
@@ -817,12 +815,12 @@ char *CBS_asn1_relative_oid_to_text(const CBS *cbs) {
// Relative OIDs must have at least one component.
uint64_t v;
if (!parse_base128_integer(&copy, &v) || !add_decimal(cbb.get(), v)) {
if (!CBS_get_asn1_oid_component(&copy, &v) || !add_decimal(cbb.get(), v)) {
return nullptr;
}
while (CBS_len(&copy) != 0) {
if (!parse_base128_integer(&copy, &v) || !CBB_add_u8(cbb.get(), '.') ||
if (!CBS_get_asn1_oid_component(&copy, &v) || !CBB_add_u8(cbb.get(), '.') ||
!add_decimal(cbb.get(), v)) {
return nullptr;
}
+15
View File
@@ -429,6 +429,16 @@ OPENSSL_EXPORT int CBS_is_valid_asn1_relative_oid(const CBS *cbs);
// 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 @@ OPENSSL_EXPORT int CBB_add_asn1_relative_oid_from_text(CBB *cbb,
// 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
+2
View File
@@ -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)