Support high tag numbers in CBS/CBB.
Android's attestion format uses some ludicrously large tag numbers:
https://developer.android.com/training/articles/security-key-attestation.html#certificate_schema
Add support for these in CBS/CBB. The public API does not change for
callers who were using the CBS_ASN1_* constants, but it is no longer the
case that tag representations match their DER encodings for small tag
numbers.
Chromium needs https://chromium-review.googlesource.com/#/c/chromium/src/+/783254,
but otherwise I don't expect this to break things.
Bug: 214
Change-Id: I9b5dc27ae3ea020e9edaabec4d665fd73da7d31e
Reviewed-on: https://boringssl-review.googlesource.com/23304
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/bytestring/ber.c b/crypto/bytestring/ber.c
index 4dc94f6..bb5e17c 100644
--- a/crypto/bytestring/ber.c
+++ b/crypto/bytestring/ber.c
@@ -29,10 +29,7 @@
// is_string_type returns one if |tag| is a string type and zero otherwise. It
// ignores the constructed bit.
static int is_string_type(unsigned tag) {
- if ((tag & 0xc0) != 0) {
- return 0;
- }
- switch (tag & 0x1f) {
+ switch (tag & ~CBS_ASN1_CONSTRUCTED) {
case CBS_ASN1_BITSTRING:
case CBS_ASN1_OCTETSTRING:
case CBS_ASN1_UTF8STRING:
diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc
index e8e14d9..7e3d453 100644
--- a/crypto/bytestring/bytestring_test.cc
+++ b/crypto/bytestring/bytestring_test.cc
@@ -123,27 +123,27 @@
uint64_t value;
CBS_init(&data, kData1, sizeof(kData1));
- EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x1));
- EXPECT_TRUE(CBS_peek_asn1_tag(&data, 0x30));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_BOOLEAN));
+ EXPECT_TRUE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
- ASSERT_TRUE(CBS_get_asn1(&data, &contents, 0x30));
+ ASSERT_TRUE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
EXPECT_EQ(Bytes("\x01\x02"), Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData2, sizeof(kData2));
// data is truncated
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData3, sizeof(kData3));
// zero byte length of length
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData4, sizeof(kData4));
// long form mistakenly used.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData5, sizeof(kData5));
// length takes too many bytes.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData1, sizeof(kData1));
// wrong tag.
@@ -151,56 +151,72 @@
CBS_init(&data, NULL, 0);
// peek at empty data.
- EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x30));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
CBS_init(&data, NULL, 0);
// optional elements at empty data.
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
- ASSERT_TRUE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, NULL,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
EXPECT_EQ(42u, value);
CBS_init(&data, kData6, sizeof(kData6));
// optional element.
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa1));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
EXPECT_TRUE(present);
EXPECT_EQ(Bytes("\x04\x01\x01"),
Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData6, sizeof(kData6));
// optional octet string.
- ASSERT_TRUE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
EXPECT_TRUE(present);
EXPECT_EQ(Bytes("\x01"), Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData7, sizeof(kData7));
// invalid optional octet string.
- EXPECT_FALSE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
+ EXPECT_FALSE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
CBS_init(&data, kData8, sizeof(kData8));
// optional integer.
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
EXPECT_EQ(42u, value);
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
EXPECT_EQ(1u, value);
CBS_init(&data, kData9, sizeof(kData9));
// invalid optional integer.
- EXPECT_FALSE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+ EXPECT_FALSE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
unsigned tag;
CBS_init(&data, kData1, sizeof(kData1));
@@ -217,6 +233,54 @@
Bytes(CBS_data(&contents), CBS_len(&contents)));
}
+TEST(CBSTest, ParseASN1Tag) {
+ const struct {
+ bool ok;
+ unsigned tag;
+ std::vector<uint8_t> in;
+ } kTests[] = {
+ {true, CBS_ASN1_SEQUENCE, {0x30, 0}},
+ {true, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4, {0xa4, 0}},
+ {true, CBS_ASN1_APPLICATION | 30, {0x5e, 0}},
+ {true, CBS_ASN1_APPLICATION | 31, {0x5f, 0x1f, 0}},
+ {true, CBS_ASN1_APPLICATION | 32, {0x5f, 0x20, 0}},
+ {true,
+ CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff,
+ {0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 0}},
+ // Tag number fits in unsigned but not |CBS_ASN1_TAG_NUMBER_MASK|.
+ {false, 0, {0xff, 0x82, 0xff, 0xff, 0xff, 0x7f, 0}},
+ // Tag number does not fit in unsigned.
+ {false, 0, {0xff, 0x90, 0x80, 0x80, 0x80, 0, 0}},
+ // Tag number is not minimally-encoded
+ {false, 0, {0x5f, 0x80, 0x1f, 0}},
+ // Tag number should have used short form.
+ {false, 0, {0x5f, 0x80, 0x1e, 0}},
+ };
+ for (const auto &t : kTests) {
+ SCOPED_TRACE(Bytes(t.in));
+ unsigned tag;
+ CBS cbs, child;
+ CBS_init(&cbs, t.in.data(), t.in.size());
+ ASSERT_EQ(t.ok, !!CBS_get_any_asn1(&cbs, &child, &tag));
+ if (t.ok) {
+ EXPECT_EQ(t.tag, tag);
+ EXPECT_EQ(0u, CBS_len(&child));
+ EXPECT_EQ(0u, CBS_len(&cbs));
+
+ CBS_init(&cbs, t.in.data(), t.in.size());
+ EXPECT_TRUE(CBS_peek_asn1_tag(&cbs, t.tag));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&cbs, t.tag + 1));
+
+ EXPECT_TRUE(CBS_get_asn1(&cbs, &child, t.tag));
+ EXPECT_EQ(0u, CBS_len(&child));
+ EXPECT_EQ(0u, CBS_len(&cbs));
+
+ CBS_init(&cbs, t.in.data(), t.in.size());
+ EXPECT_FALSE(CBS_get_asn1(&cbs, &child, t.tag + 1));
+ }
+ }
+}
+
TEST(CBSTest, GetOptionalASN1Bool) {
static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff};
static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00};
@@ -416,15 +480,42 @@
}
TEST(CBBTest, ASN1) {
- static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
+ static const uint8_t kExpected[] = {
+ // SEQUENCE { 1 2 3 }
+ 0x30, 3, 1, 2, 3,
+ // [4 CONSTRUCTED] { 4 5 6 }
+ 0xa4, 3, 4, 5, 6,
+ // [APPLICATION 30 PRIMITIVE] { 7 8 9 }
+ 0x5e, 3, 7, 8, 9,
+ // [APPLICATION 31 PRIMITIVE] { 10 11 12 }
+ 0x5f, 0x1f, 3, 10, 11, 12,
+ // [PRIVATE 2^29-1 CONSTRUCTED] { 13 14 15 }
+ 0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 3, 13, 14, 15,
+ };
uint8_t *buf;
size_t buf_len;
bssl::ScopedCBB cbb;
CBB contents, inner_contents;
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x01\x02\x03", 3));
+ ASSERT_TRUE(
+ CBB_add_asn1(cbb.get(), &contents,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4));
+ ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x04\x05\x06", 3));
+ ASSERT_TRUE(
+ CBB_add_asn1(cbb.get(), &contents,
+ CBS_ASN1_APPLICATION | 30));
+ ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x07\x08\x09", 3));
+ ASSERT_TRUE(
+ CBB_add_asn1(cbb.get(), &contents,
+ CBS_ASN1_APPLICATION | 31));
+ ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0a\x0b\x0c", 3));
+ ASSERT_TRUE(
+ CBB_add_asn1(cbb.get(), &contents,
+ CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff));
+ ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0d\x0e\x0f", 3));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
bssl::UniquePtr<uint8_t> scoper(buf);
@@ -432,7 +523,7 @@
std::vector<uint8_t> test_data(100000, 0x42);
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 130));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -442,7 +533,7 @@
EXPECT_EQ(Bytes(test_data.data(), 130), Bytes(buf + 3, 130));
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 1000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -452,8 +543,8 @@
EXPECT_EQ(Bytes(test_data.data(), 1000), Bytes(buf + 4, 1000));
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
- ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&inner_contents, test_data.data(), 100000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -490,6 +581,12 @@
static const uint8_t kIndefBER[] = {0x30, 0x80, 0x01, 0x01, 0x02, 0x00, 0x00};
static const uint8_t kIndefDER[] = {0x30, 0x03, 0x01, 0x01, 0x02};
+ // kIndefBER2 contains a constructed [APPLICATION 31] with an indefinite
+ // length.
+ static const uint8_t kIndefBER2[] = {0x7f, 0x1f, 0x80, 0x01,
+ 0x01, 0x02, 0x00, 0x00};
+ static const uint8_t kIndefDER2[] = {0x7f, 0x1f, 0x03, 0x01, 0x01, 0x02};
+
// kOctetStringBER contains an indefinite length OCTET STRING with two parts.
// These parts need to be concatenated in DER form.
static const uint8_t kOctetStringBER[] = {0x24, 0x80, 0x04, 0x02, 0, 1,
@@ -534,6 +631,8 @@
sizeof(kSimpleBER));
ExpectBerConvert("kIndefBER", kIndefDER, sizeof(kIndefDER), kIndefBER,
sizeof(kIndefBER));
+ ExpectBerConvert("kIndefBER2", kIndefDER2, sizeof(kIndefDER2), kIndefBER2,
+ sizeof(kIndefBER2));
ExpectBerConvert("kOctetStringBER", kOctetStringDER, sizeof(kOctetStringDER),
kOctetStringBER, sizeof(kOctetStringBER));
ExpectBerConvert("kNSSBER", kNSSDER, sizeof(kNSSDER), kNSSBER,
diff --git a/crypto/bytestring/cbb.c b/crypto/bytestring/cbb.c
index 5576fa9..b1afe7d 100644
--- a/crypto/bytestring/cbb.c
+++ b/crypto/bytestring/cbb.c
@@ -329,17 +329,36 @@
}
int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
- if (tag > 0xff ||
- (tag & 0x1f) == 0x1f) {
- // Long form identifier octets are not supported. Further, all current valid
- // tag serializations are 8 bits.
- cbb->base->error = 1;
+ if (!CBB_flush(cbb)) {
return 0;
}
- if (!CBB_flush(cbb) ||
- // |tag|'s representation matches the DER encoding.
- !CBB_add_u8(cbb, (uint8_t)tag)) {
+ // Split the tag into leading bits and tag number.
+ uint8_t tag_bits = (tag >> CBS_ASN1_TAG_SHIFT) & 0xe0;
+ unsigned tag_number = tag & CBS_ASN1_TAG_NUMBER_MASK;
+ if (tag_number >= 0x1f) {
+ // Set all the bits in the tag number to signal high tag number form.
+ if (!CBB_add_u8(cbb, tag_bits | 0x1f)) {
+ return 0;
+ }
+
+ unsigned len_len = 0;
+ unsigned copy = tag_number;
+ while (copy > 0) {
+ len_len++;
+ copy >>= 7;
+ }
+ for (unsigned i = len_len - 1; i < len_len; i--) {
+ uint8_t byte = (tag_number >> (7 * i)) & 0x7f;
+ if (i != 0) {
+ // The high bit denotes whether there is more data.
+ byte |= 0x80;
+ }
+ if (!CBB_add_u8(cbb, byte)) {
+ return 0;
+ }
+ }
+ } else if (!CBB_add_u8(cbb, tag_bits | tag_number)) {
return 0;
}
diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c
index ec495d2..d96371c 100644
--- a/crypto/bytestring/cbs.c
+++ b/crypto/bytestring/cbs.c
@@ -175,18 +175,9 @@
return cbs_get_length_prefixed(cbs, out, 3);
}
-static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
- size_t *out_header_len, int ber_ok) {
- uint8_t tag, length_byte;
- CBS header = *cbs;
- CBS throwaway;
-
- if (out == NULL) {
- out = &throwaway;
- }
-
- if (!CBS_get_u8(&header, &tag) ||
- !CBS_get_u8(&header, &length_byte)) {
+static int parse_asn1_tag(CBS *cbs, unsigned *out) {
+ uint8_t tag_byte;
+ if (!CBS_get_u8(cbs, &tag_byte)) {
return 0;
}
@@ -197,22 +188,70 @@
// allotted bits), then the tag is more than one byte long and the
// continuation bytes contain the tag number. This parser only supports tag
// numbers less than 31 (and thus single-byte tags).
- if ((tag & 0x1f) == 0x1f) {
- return 0;
+ unsigned tag = ((unsigned)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT;
+ unsigned tag_number = tag_byte & 0x1f;
+ if (tag_number == 0x1f) {
+ tag_number = 0;
+ for (;;) {
+ if (!CBS_get_u8(cbs, &tag_byte) ||
+ ((tag_number << 7) >> 7) != tag_number) {
+ return 0;
+ }
+ tag_number = (tag_number << 7) | (tag_byte & 0x7f);
+ // The tag must be represented in the minimal number of bytes.
+ if (tag_number == 0) {
+ return 0;
+ }
+ if ((tag_byte & 0x80) == 0) {
+ break;
+ }
+ }
+ if (// Check the tag number is within our supported bounds.
+ tag_number > CBS_ASN1_TAG_NUMBER_MASK ||
+ // Small tag numbers should have used low tag number form.
+ tag_number < 0x1f) {
+ return 0;
+ }
}
+ tag |= tag_number;
+
+ *out = tag;
+ return 1;
+}
+
+static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+ size_t *out_header_len, int ber_ok) {
+ CBS header = *cbs;
+ CBS throwaway;
+
+ if (out == NULL) {
+ out = &throwaway;
+ }
+
+ unsigned tag;
+ if (!parse_asn1_tag(&header, &tag)) {
+ return 0;
+ }
if (out_tag != NULL) {
*out_tag = tag;
}
+ uint8_t length_byte;
+ if (!CBS_get_u8(&header, &length_byte)) {
+ return 0;
+ }
+
+ size_t header_len = CBS_len(cbs) - CBS_len(&header);
+
size_t len;
// The format for the length encoding is specified in ITU-T X.690 section
// 8.1.3.
if ((length_byte & 0x80) == 0) {
// Short form length.
- len = ((size_t) length_byte) + 2;
+ len = ((size_t) length_byte) + header_len;
if (out_header_len != NULL) {
- *out_header_len = 2;
+ *out_header_len = header_len;
}
} else {
// The high bit indicate that this is the long form, while the next 7 bits
@@ -224,9 +263,9 @@
if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
// indefinite length
if (out_header_len != NULL) {
- *out_header_len = 2;
+ *out_header_len = header_len;
}
- return CBS_get_bytes(cbs, out, 2);
+ return CBS_get_bytes(cbs, out, header_len);
}
// ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be
@@ -249,13 +288,13 @@
return 0;
}
len = len32;
- if (len + 2 + num_bytes < len) {
+ if (len + header_len + num_bytes < len) {
// Overflow.
return 0;
}
- len += 2 + num_bytes;
+ len += header_len + num_bytes;
if (out_header_len != NULL) {
- *out_header_len = 2 + num_bytes;
+ *out_header_len = header_len + num_bytes;
}
}
@@ -323,7 +362,10 @@
if (CBS_len(cbs) < 1) {
return 0;
}
- return CBS_data(cbs)[0] == tag_value;
+
+ CBS copy = *cbs;
+ unsigned actual_tag;
+ return parse_asn1_tag(©, &actual_tag) && tag_value == actual_tag;
}
int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
diff --git a/crypto/ec_extra/ec_asn1.c b/crypto/ec_extra/ec_asn1.c
index dc710a8..c125af2 100644
--- a/crypto/ec_extra/ec_asn1.c
+++ b/crypto/ec_extra/ec_asn1.c
@@ -67,9 +67,9 @@
#include "../internal.h"
-static const uint8_t kParametersTag =
+static const unsigned kParametersTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
-static const uint8_t kPublicKeyTag =
+static const unsigned kPublicKeyTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
EC_KEY *EC_KEY_parse_private_key(CBS *cbs, const EC_GROUP *group) {
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 2fb1b1b..cd4e61d 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -834,7 +834,7 @@
/* This ensures the X509 took a reference to |buf|, otherwise this will be a
* reference to free memory and ASAN should notice. */
- ASSERT_EQ(CBS_ASN1_SEQUENCE, enc_pointer[0]);
+ ASSERT_EQ(0x30, enc_pointer[0]);
}
TEST(X509Test, TestFromBufferWithTrailingData) {
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index 6d355b5..ad0c928 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -164,34 +164,37 @@
#define CBS_ASN1_UNIVERSALSTRING 0x1cu
#define CBS_ASN1_BMPSTRING 0x1eu
+// CBS_ASN1_TAG_SHIFT is how much the in-memory representation shifts the class
+// and constructed bits from the DER serialization. This allows representing tag
+// numbers beyond 31.
+//
+// Consumers must use the following constants to decompose or assemble tags.
+#define CBS_ASN1_TAG_SHIFT 24
+
// CBS_ASN1_CONSTRUCTED may be ORed into a tag to toggle the constructed
// bit. |CBS| and |CBB| APIs consider the constructed bit to be part of the
// tag.
-#define CBS_ASN1_CONSTRUCTED 0x20u
+#define CBS_ASN1_CONSTRUCTED (0x20u << CBS_ASN1_TAG_SHIFT)
// The following values specify the constructed bit or tag class and may be ORed
// into a tag number to produce the final tag. If none is used, the tag will be
// UNIVERSAL.
-//
-// Note that although they currently match the DER serialization, consumers must
-// use these bits rather than make assumptions about the representation. This is
-// to allow for tag numbers beyond 31 in the future.
-#define CBS_ASN1_APPLICATION 0x40u
-#define CBS_ASN1_CONTEXT_SPECIFIC 0x80u
-#define CBS_ASN1_PRIVATE 0xc0u
+#define CBS_ASN1_UNIVERSAL (0u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_APPLICATION (0x40u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_CONTEXT_SPECIFIC (0x80u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_PRIVATE (0xc0u << CBS_ASN1_TAG_SHIFT)
-// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class.
-#define CBS_ASN1_CLASS_MASK 0xc0u
+// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class. This will
+// give one of the four values above.
+#define CBS_ASN1_CLASS_MASK (0xc0u << CBS_ASN1_TAG_SHIFT)
// CBS_ASN1_TAG_NUMBER_MASK may be ANDed with a tag to query its number.
-#define CBS_ASN1_TAG_NUMBER_MASK 0x1fu
+#define CBS_ASN1_TAG_NUMBER_MASK ((1u << (5 + CBS_ASN1_TAG_SHIFT)) - 1)
// CBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not
// including tag and length bytes) and advances |cbs| over it. The ASN.1
// element must match |tag_value|. It returns one on success and zero
// on error.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value);
// CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the
@@ -209,16 +212,12 @@
// (not including tag and length bytes), sets |*out_tag| to the tag number, and
// advances |*cbs|. It returns one on success and zero on error. Either of |out|
// and |out_tag| may be NULL to ignore the value.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_any_asn1(CBS *cbs, CBS *out, unsigned *out_tag);
// CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from
// |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to
// the tag number and |*out_header_len| to the length of the ASN.1 header. Each
// of |out|, |out_tag|, and |out_header_len| may be NULL to ignore the value.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_any_asn1_element(CBS *cbs, CBS *out,
unsigned *out_tag,
size_t *out_header_len);
@@ -396,9 +395,7 @@
// CBB_add_asn1 sets |*out_contents| to a |CBB| into which the contents of an
// ASN.1 object can be written. The |tag| argument will be used as the tag for
-// the object. Passing in |tag| number 31 will return in an error since only
-// single octet identifiers are supported. It returns one on success or zero
-// on error.
+// the object. It returns one on success or zero on error.
OPENSSL_EXPORT int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag);
// CBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on
diff --git a/ssl/ssl_asn1.cc b/ssl/ssl_asn1.cc
index 7bcfdd7..eb7df5b 100644
--- a/ssl/ssl_asn1.cc
+++ b/ssl/ssl_asn1.cc
@@ -152,49 +152,49 @@
static const unsigned kVersion = 1;
-static const int kTimeTag =
+static const unsigned kTimeTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
-static const int kTimeoutTag =
+static const unsigned kTimeoutTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2;
-static const int kPeerTag =
+static const unsigned kPeerTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3;
-static const int kSessionIDContextTag =
+static const unsigned kSessionIDContextTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 4;
-static const int kVerifyResultTag =
+static const unsigned kVerifyResultTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 5;
-static const int kHostNameTag =
+static const unsigned kHostNameTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 6;
-static const int kPSKIdentityTag =
+static const unsigned kPSKIdentityTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 8;
-static const int kTicketLifetimeHintTag =
+static const unsigned kTicketLifetimeHintTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 9;
-static const int kTicketTag =
+static const unsigned kTicketTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 10;
-static const int kPeerSHA256Tag =
+static const unsigned kPeerSHA256Tag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 13;
-static const int kOriginalHandshakeHashTag =
+static const unsigned kOriginalHandshakeHashTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 14;
-static const int kSignedCertTimestampListTag =
+static const unsigned kSignedCertTimestampListTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 15;
-static const int kOCSPResponseTag =
+static const unsigned kOCSPResponseTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16;
-static const int kExtendedMasterSecretTag =
+static const unsigned kExtendedMasterSecretTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17;
-static const int kGroupIDTag =
+static const unsigned kGroupIDTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
-static const int kCertChainTag =
+static const unsigned kCertChainTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
-static const int kTicketAgeAddTag =
+static const unsigned kTicketAgeAddTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 21;
-static const int kIsServerTag =
+static const unsigned kIsServerTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 22;
-static const int kPeerSignatureAlgorithmTag =
+static const unsigned kPeerSignatureAlgorithmTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23;
-static const int kTicketMaxEarlyDataTag =
+static const unsigned kTicketMaxEarlyDataTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24;
-static const int kAuthTimeoutTag =
+static const unsigned kAuthTimeoutTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25;
-static const int kEarlyALPNTag =
+static const unsigned kEarlyALPNTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 26;
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,