Add CBS APIs for fetching an implicitly tagged int64/uint64 fields.
Change-Id: Iafff08857bf2af8038e2c8bf4e6a57c38cf0b7ec
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/75887
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc
index b0a254b..23133e0 100644
--- a/crypto/bytestring/bytestring_test.cc
+++ b/crypto/bytestring/bytestring_test.cc
@@ -901,6 +901,24 @@
{"\x02\x02\x00\x01", 4, false},
};
+struct ASN1Uint64WithTagTest {
+ CBS_ASN1_TAG tag;
+ uint64_t value;
+ const char *encoding;
+ size_t encoding_len;
+};
+
+static const ASN1Uint64WithTagTest kASN1Uint64WithTagTests[]{
+ {CBS_ASN1_CONTEXT_SPECIFIC, 0, "\x80\x01\x00", 3},
+ {CBS_ASN1_CONTEXT_SPECIFIC | 1, 1, "\x81\x01\x01", 3},
+ {CBS_ASN1_INTEGER, 127, "\x02\x01\x7f", 3},
+ {CBS_ASN1_CONTEXT_SPECIFIC, 128, "\x80\x02\x00\x80", 4},
+ {CBS_ASN1_CONTEXT_SPECIFIC, UINT64_C(0x0102030405060708),
+ "\x80\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10},
+ {CBS_ASN1_CONTEXT_SPECIFIC, (0xffffffffffffffff),
+ "\x80\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11},
+};
+
TEST(CBSTest, ASN1Uint64) {
for (const ASN1Uint64Test &test : kASN1Uint64Tests) {
SCOPED_TRACE(Bytes(test.encoding, test.encoding_len));
@@ -961,6 +979,37 @@
EXPECT_EQ(test.overflow, !!CBS_is_unsigned_asn1_integer(&child));
}
}
+
+ for (const ASN1Uint64WithTagTest &test : kASN1Uint64WithTagTests) {
+ SCOPED_TRACE(Bytes(test.encoding, test.encoding_len));
+ SCOPED_TRACE(test.value);
+ CBS cbs;
+ uint64_t value;
+ uint8_t *out;
+ size_t len;
+
+ CBS_init(&cbs, (const uint8_t *)test.encoding, test.encoding_len);
+ ASSERT_TRUE(CBS_get_asn1_uint64_with_tag(&cbs, &value, test.tag));
+ EXPECT_EQ(0u, CBS_len(&cbs));
+ EXPECT_EQ(test.value, value);
+
+ CBS child;
+ int is_negative;
+ CBS_init(&cbs, (const uint8_t *)test.encoding, test.encoding_len);
+ ASSERT_TRUE(CBS_get_asn1(&cbs, &child, test.tag));
+ EXPECT_TRUE(CBS_is_valid_asn1_integer(&child, &is_negative));
+ EXPECT_EQ(0, is_negative);
+ EXPECT_TRUE(CBS_is_unsigned_asn1_integer(&child));
+
+ {
+ bssl::ScopedCBB cbb;
+ ASSERT_TRUE(CBB_init(cbb.get(), 0));
+ ASSERT_TRUE(CBB_add_asn1_uint64_with_tag(cbb.get(), test.value, test.tag));
+ ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
+ bssl::UniquePtr<uint8_t> scoper(out);
+ EXPECT_EQ(Bytes(test.encoding, test.encoding_len), Bytes(out, len));
+ }
+ }
}
struct ASN1Int64Test {
@@ -1007,6 +1056,24 @@
{"\x02\x02\xff\xff", 4, false},
};
+struct ASN1Int64WithTagTest {
+ CBS_ASN1_TAG tag;
+ int64_t value;
+ const char *encoding;
+ size_t encoding_len;
+};
+
+static const ASN1Int64WithTagTest kASN1Int64WithTagTests[] = {
+ {CBS_ASN1_CONTEXT_SPECIFIC, 0, "\x80\x01\x00", 3},
+ {CBS_ASN1_CONTEXT_SPECIFIC | 1, 1, "\x81\x01\x01", 3},
+ {CBS_ASN1_INTEGER, 1, "\x02\x01\x01", 3},
+ {CBS_ASN1_CONTEXT_SPECIFIC, INT64_MIN,
+ "\x80\x08\x80\x00\x00\x00\x00\x00\x00\x00", 10},
+ {CBS_ASN1_CONTEXT_SPECIFIC, INT64_MAX,
+ "\x80\x08\x7f\xff\xff\xff\xff\xff\xff\xff", 10},
+};
+
+
TEST(CBSTest, ASN1Int64) {
for (const ASN1Int64Test &test : kASN1Int64Tests) {
SCOPED_TRACE(Bytes(test.encoding, test.encoding_len));
@@ -1067,6 +1134,37 @@
EXPECT_EQ(test.overflow, !!CBS_is_valid_asn1_integer(&child, NULL));
}
}
+
+ for (const ASN1Int64WithTagTest &test : kASN1Int64WithTagTests) {
+ SCOPED_TRACE(Bytes(test.encoding, test.encoding_len));
+ SCOPED_TRACE(test.value);
+ CBS cbs;
+ int64_t value;
+ uint8_t *out;
+ size_t len;
+
+ CBS_init(&cbs, (const uint8_t *)test.encoding, test.encoding_len);
+ ASSERT_TRUE(CBS_get_asn1_int64_with_tag(&cbs, &value, test.tag));
+ EXPECT_EQ(0u, CBS_len(&cbs));
+ EXPECT_EQ(test.value, value);
+
+ CBS child;
+ int is_negative;
+ CBS_init(&cbs, (const uint8_t *)test.encoding, test.encoding_len);
+ ASSERT_TRUE(CBS_get_asn1(&cbs, &child, test.tag));
+ EXPECT_TRUE(CBS_is_valid_asn1_integer(&child, &is_negative));
+ EXPECT_EQ(test.value < 0, !!is_negative);
+ EXPECT_EQ(test.value >= 0, !!CBS_is_unsigned_asn1_integer(&child));
+
+ {
+ bssl::ScopedCBB cbb;
+ ASSERT_TRUE(CBB_init(cbb.get(), 0));
+ ASSERT_TRUE(CBB_add_asn1_int64_with_tag(cbb.get(), test.value, test.tag));
+ ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
+ bssl::UniquePtr<uint8_t> scoper(out);
+ EXPECT_EQ(Bytes(test.encoding, test.encoding_len), Bytes(out, len));
+ }
+ }
}
TEST(CBBTest, Zero) {
diff --git a/crypto/bytestring/cbs.cc b/crypto/bytestring/cbs.cc
index efdf3f0..73a9d63 100644
--- a/crypto/bytestring/cbs.cc
+++ b/crypto/bytestring/cbs.cc
@@ -472,8 +472,12 @@
}
int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
+ return CBS_get_asn1_uint64_with_tag(cbs, out, CBS_ASN1_INTEGER);
+}
+
+int CBS_get_asn1_uint64_with_tag(CBS *cbs, uint64_t *out, CBS_ASN1_TAG tag) {
CBS bytes;
- if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER) ||
+ if (!CBS_get_asn1(cbs, &bytes, tag) ||
!CBS_is_unsigned_asn1_integer(&bytes)) {
return 0;
}
@@ -494,9 +498,13 @@
}
int CBS_get_asn1_int64(CBS *cbs, int64_t *out) {
+ return CBS_get_asn1_int64_with_tag(cbs, out, CBS_ASN1_INTEGER);
+}
+
+int CBS_get_asn1_int64_with_tag(CBS *cbs, int64_t *out, CBS_ASN1_TAG tag) {
int is_negative;
CBS bytes;
- if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER) ||
+ if (!CBS_get_asn1(cbs, &bytes, tag) ||
!CBS_is_valid_asn1_integer(&bytes, &is_negative)) {
return 0;
}
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index ffab33b..813decb 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -292,11 +292,28 @@
// in 64 bits.
OPENSSL_EXPORT int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out);
+// CBS_get_asn1_uint64_with_tag gets an ASN.1 INTEGER from |cbs| using
+// |CBS_get_asn1| and sets |*out| to its value. |tag| is used to handle to
+// handle implicitly tagged INTEGER fields. It returns one on success and zero
+// on error, where error includes the integer being negative, or too large to
+// represent in 64 bits.
+OPENSSL_EXPORT int CBS_get_asn1_uint64_with_tag(CBS *cbs, uint64_t *out,
+ CBS_ASN1_TAG tag);
+
+
// CBS_get_asn1_int64 gets an ASN.1 INTEGER from |cbs| using |CBS_get_asn1|
// and sets |*out| to its value. It returns one on success and zero on error,
// where error includes the integer being too large to represent in 64 bits.
OPENSSL_EXPORT int CBS_get_asn1_int64(CBS *cbs, int64_t *out);
+// CBS_get_asn1_int64_with_tag gets an ASN.1 INTEGER from |cbs| using
+// |CBS_get_asn1| and sets |*out| to its value. |tag| is used to handle to
+// handle implicitly tagged INTEGER fields. It returns one on success and zero
+// on error, where error includes the integer being too large to represent in 64
+// bits.
+OPENSSL_EXPORT int CBS_get_asn1_int64_with_tag(CBS *cbs, int64_t *out,
+ CBS_ASN1_TAG tag);
+
// CBS_get_asn1_bool gets an ASN.1 BOOLEAN from |cbs| and sets |*out| to zero
// or one based on its value. It returns one on success or zero on error.
OPENSSL_EXPORT int CBS_get_asn1_bool(CBS *cbs, int *out);