Also add a decoupled OBJ_obj2txt.

We need it in both directions. Also I missed that in OBJ_obj2txt we
allowed uint64_t components, but in my new OBJ_txt2obj we only allowed
uint32_t. For consistency, upgrade that to uint64_t.

Bug: chromium:706445
Change-Id: I38cfeea8ff64b9acf7998e552727c6c3b2cc600f
Reviewed-on: https://boringssl-review.googlesource.com/23544
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
David Benjamin
2017-11-30 18:21:48 +00:00
committed by CQ bot account: commit-bot@chromium.org
parent 1530ef3ec5
commit 095b6c9baa
5 changed files with 195 additions and 142 deletions
+83 -42
View File
@@ -889,54 +889,95 @@ TEST(CBSTest, BitString) {
TEST(CBBTest, AddOIDFromText) {
const struct {
const char *in;
bool ok;
std::vector<uint8_t> out;
} kTests[] = {
const char *text;
std::vector<uint8_t> der;
} kValidOIDs[] = {
// Some valid values.
{"1.2.3.4", true, {0x2a, 0x3, 0x4}},
{"0.0", {0x00}},
{"0.2.3.4", {0x2, 0x3, 0x4}},
{"1.2.3.4", {0x2a, 0x3, 0x4}},
{"2.2.3.4", {0x52, 0x3, 0x4}},
{"1.2.840.113554.4.1.72585",
true,
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09}},
// Test edge cases around the first component.
{"0.39", true, {0x27}},
{"0.40", false, {}},
{"1.0", true, {0x28}},
{"1.39", true, {0x4f}},
{"1.40", false, {}},
{"2.0", true, {0x50}},
{"2.1", true, {0x51}},
{"2.40", true, {0x78}},
// The empty string is not an OID.
{"", false, {}},
// No empty components.
{".1.2.3.4.5", false, {}},
{"1..2.3.4.5", false, {}},
{"1.2.3.4.5.", false, {}},
// There must be at least two components.
{"1", false, {}},
// No extra leading zeros.
{"00.1.2.3.4", false, {}},
{"01.1.2.3.4", false, {}},
// Check for overflow.
{"1.2.4294967295", true, {0x2a, 0x8f, 0xff, 0xff, 0xff, 0x7f}},
{"1.2.4294967296", false, {}},
// 40*A + B overflows.
{"2.4294967215", true, {0x8f, 0xff, 0xff, 0xff, 0x7f}},
{"2.4294967216", false, {}},
{"0.39", {0x27}},
{"1.0", {0x28}},
{"1.39", {0x4f}},
{"2.0", {0x50}},
{"2.1", {0x51}},
{"2.40", {0x78}},
// Edge cases near an overflow.
{"1.2.18446744073709551615",
{0x2a, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
{"2.18446744073709551535",
{0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
};
for (const auto &t : kTests) {
SCOPED_TRACE(t.in);
const char *kInvalidTexts[] = {
// Invalid second component.
"0.40",
"1.40",
// Invalid first component.
"3.1",
// The empty string is not an OID.
"",
// No empty components.
".1.2.3.4.5",
"1..2.3.4.5",
"1.2.3.4.5.",
// There must be at least two components.
"1",
// No extra leading zeros.
"00.1.2.3.4",
"01.1.2.3.4",
// Overflow for both components or 40*A + B.
"1.2.18446744073709551616",
"2.18446744073709551536",
};
const std::vector<uint8_t> kInvalidDER[] = {
// The empty string is not an OID.
{},
// Non-minimal representation.
{0x80, 0x01},
// Overflow. This is the DER representation of
// 1.2.840.113554.4.1.72585.18446744073709551616. (The final value is
// 2^64.)
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09,
0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00},
};
for (const auto &t : kValidOIDs) {
SCOPED_TRACE(t.text);
bssl::ScopedCBB cbb;
ASSERT_TRUE(CBB_init(cbb.get(), 0));
int ok = CBB_add_asn1_oid_from_text(cbb.get(), t.in, strlen(t.in));
EXPECT_EQ(t.ok, static_cast<bool>(ok));
if (ok) {
uint8_t *out;
size_t len;
ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
bssl::UniquePtr<uint8_t> free_out(out);
EXPECT_EQ(Bytes(t.out), Bytes(out, len));
}
ASSERT_TRUE(CBB_add_asn1_oid_from_text(cbb.get(), t.text, strlen(t.text)));
uint8_t *out;
size_t len;
ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
bssl::UniquePtr<uint8_t> free_out(out);
EXPECT_EQ(Bytes(t.der), Bytes(out, len));
CBS cbs;
CBS_init(&cbs, t.der.data(), t.der.size());
bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
ASSERT_TRUE(text.get());
EXPECT_STREQ(t.text, text.get());
}
for (const char *t : kInvalidTexts) {
SCOPED_TRACE(t);
bssl::ScopedCBB cbb;
ASSERT_TRUE(CBB_init(cbb.get(), 0));
EXPECT_FALSE(CBB_add_asn1_oid_from_text(cbb.get(), t, strlen(t)));
}
for (const auto &t : kInvalidDER) {
SCOPED_TRACE(Bytes(t));
CBS cbs;
CBS_init(&cbs, t.data(), t.size());
bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
EXPECT_FALSE(text);
}
}
+8 -8
View File
@@ -332,9 +332,9 @@ int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents) {
// add_base128_integer encodes |v| as a big-endian base-128 integer where the
// high bit of each byte indicates where there is more data. This is the
// encoding used in DER for both high tag number form and OID components.
static int add_base128_integer(CBB *cbb, uint32_t v) {
static int add_base128_integer(CBB *cbb, uint64_t v) {
unsigned len_len = 0;
unsigned copy = v;
uint64_t copy = v;
while (copy > 0) {
len_len++;
copy >>= 7;
@@ -508,7 +508,7 @@ int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
// an OID literal, e.g., "1.2.840.113554.4.1.72585". It consumes both the
// component and the dot, so |cbs| may be passed into the function again for the
// next value.
static int parse_dotted_decimal(CBS *cbs, uint32_t *out) {
static int parse_dotted_decimal(CBS *cbs, uint64_t *out) {
*out = 0;
int seen_digit = 0;
for (;;) {
@@ -524,8 +524,8 @@ static int parse_dotted_decimal(CBS *cbs, uint32_t *out) {
// Forbid stray leading zeros.
(seen_digit && *out == 0) ||
// Check for overflow.
*out > UINT32_MAX / 10 ||
*out * 10 > UINT32_MAX - (u - '0')) {
*out > UINT64_MAX / 10 ||
*out * 10 > UINT64_MAX - (u - '0')) {
return 0;
}
*out = *out * 10 + (u - '0');
@@ -544,7 +544,7 @@ int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text, size_t len) {
CBS_init(&cbs, (const uint8_t *)text, len);
// OIDs must have at least two components.
uint32_t a, b;
uint64_t a, b;
if (!parse_dotted_decimal(&cbs, &a) ||
!parse_dotted_decimal(&cbs, &b)) {
return 0;
@@ -554,8 +554,8 @@ int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text, size_t len) {
// 0, 1, or 2 and that, when it is 0 or 1, |b| is at most 39.
if (a > 2 ||
(a < 2 && b > 39) ||
b > UINT32_MAX - 80 ||
!add_base128_integer(cbb, 40 * a + b)) {
b > UINT64_MAX - 80 ||
!add_base128_integer(cbb, 40u * a + b)) {
return 0;
}
+90 -18
View File
@@ -12,11 +12,16 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <openssl/buf.h>
#include <openssl/mem.h>
#include <openssl/bytestring.h>
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include "internal.h"
@@ -175,6 +180,33 @@ int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) {
return cbs_get_length_prefixed(cbs, out, 3);
}
// 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) {
uint64_t v = 0;
uint8_t b;
do {
if (!CBS_get_u8(cbs, &b)) {
return 0;
}
if ((v >> (64 - 7)) != 0) {
// The value is too large.
return 0;
}
if (v == 0 && b == 0x80) {
// The value must be minimally encoded.
return 0;
}
v = (v << 7) | (b & 0x7f);
// Values end at an octet with the high bit cleared.
} while (b & 0x80);
*out = v;
return 1;
}
static int parse_asn1_tag(CBS *cbs, unsigned *out) {
uint8_t tag_byte;
if (!CBS_get_u8(cbs, &tag_byte)) {
@@ -191,27 +223,15 @@ static int parse_asn1_tag(CBS *cbs, unsigned *out) {
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 ||
uint64_t v;
if (!parse_base128_integer(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.
tag_number < 0x1f) {
v < 0x1f) {
return 0;
}
tag_number = (unsigned)v;
}
tag |= tag_number;
@@ -527,3 +547,55 @@ int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit) {
return byte_num < CBS_len(cbs) &&
(CBS_data(cbs)[byte_num] & (1 << bit_num)) != 0;
}
static int add_decimal(CBB *out, uint64_t v) {
char buf[DECIMAL_SIZE(uint64_t) + 1];
BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
}
char *CBS_asn1_oid_to_text(const CBS *cbs) {
CBB cbb;
if (!CBB_init(&cbb, 32)) {
goto err;
}
CBS copy = *cbs;
// The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
uint64_t v;
if (!parse_base128_integer(&copy, &v)) {
goto err;
}
if (v >= 80) {
if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
!add_decimal(&cbb, v - 80)) {
goto err;
}
} else if (!add_decimal(&cbb, v / 40) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v % 40)) {
goto err;
}
while (CBS_len(&copy) != 0) {
if (!parse_base128_integer(&copy, &v) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v)) {
goto err;
}
}
uint8_t *txt;
size_t txt_len;
if (!CBB_add_u8(&cbb, '\0') ||
!CBB_finish(&cbb, &txt, &txt_len)) {
goto err;
}
return (char *)txt;
err:
CBB_cleanup(&cbb);
return NULL;
}
+6 -73
View File
@@ -434,36 +434,6 @@ static int strlcpy_int(char *dst, const char *src, int dst_size) {
return (int)ret;
}
static int parse_oid_component(CBS *cbs, uint64_t *out) {
uint64_t v = 0;
uint8_t b;
do {
if (!CBS_get_u8(cbs, &b)) {
return 0;
}
if ((v >> (64 - 7)) != 0) {
// The component is too large.
return 0;
}
if (v == 0 && b == 0x80) {
// The component must be minimally encoded.
return 0;
}
v = (v << 7) | (b & 0x7f);
// Components end at an octet with the high bit cleared.
} while (b & 0x80);
*out = v;
return 1;
}
static int add_decimal(CBB *out, uint64_t v) {
char buf[DECIMAL_SIZE(uint64_t) + 1];
BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
}
int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
int always_return_oid) {
// Python depends on the empty OID successfully encoding as the empty
@@ -485,56 +455,19 @@ int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
}
}
CBB cbb;
if (!CBB_init(&cbb, 32)) {
goto err;
}
CBS cbs;
CBS_init(&cbs, obj->data, obj->length);
// The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
uint64_t v;
if (!parse_oid_component(&cbs, &v)) {
goto err;
}
if (v >= 80) {
if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
!add_decimal(&cbb, v - 80)) {
goto err;
char *txt = CBS_asn1_oid_to_text(&cbs);
if (txt == NULL) {
if (out_len > 0) {
out[0] = '\0';
}
} else if (!add_decimal(&cbb, v / 40) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v % 40)) {
goto err;
return -1;
}
while (CBS_len(&cbs) != 0) {
if (!parse_oid_component(&cbs, &v) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v)) {
goto err;
}
}
uint8_t *txt;
size_t txt_len;
if (!CBB_add_u8(&cbb, '\0') ||
!CBB_finish(&cbb, &txt, &txt_len)) {
goto err;
}
int ret = strlcpy_int(out, (const char *)txt, out_len);
int ret = strlcpy_int(out, txt, out_len);
OPENSSL_free(txt);
return ret;
err:
CBB_cleanup(&cbb);
if (out_len > 0) {
out[0] = '\0';
}
return -1;
}
static uint32_t hash_nid(const ASN1_OBJECT *obj) {
+8 -1
View File
@@ -279,6 +279,13 @@ OPENSSL_EXPORT int CBS_is_valid_asn1_bitstring(const CBS *cbs);
// is indexed starting from zero.
OPENSSL_EXPORT int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit);
// CBS_asn1_oid_to_text interprets |cbs| as DER-encoded ASN.1 OBJECT IDENTIFIER
// contents (not including the element framing) and returns the ASCII
// representation (e.g., "1.2.840.113554.4.1.72585") in a newly-allocated
// string, or NULL on failure. The caller must release the result with
// |OPENSSL_free|.
OPENSSL_EXPORT char *CBS_asn1_oid_to_text(const CBS *cbs);
// CRYPTO ByteBuilder.
//
@@ -450,7 +457,7 @@ OPENSSL_EXPORT int CBB_add_asn1_uint64(CBB *cbb, uint64_t value);
// the element's contents.
//
// This function considers OID strings with components which do not fit in a
// |uint32_t| to be invalid.
// |uint64_t| to be invalid.
OPENSSL_EXPORT int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text,
size_t len);