Skip to content

Commit a014bda

Browse files
davidbenBoringssl LUCI CQ
authored and
Boringssl LUCI CQ
committed
Improve X509Test.NameAttributeValues coverage
Add a negative ENUMERATED. This is redundant with ASN1Test.NegativeEnumeratedMultistring, but once we fix the X509_NAME value representation, d2i_ASN1_PRINTABLE will be gone. In doing so, I noticed that we weren't really testing re-encoding, so fix that. Also add some negative tests, both capturing actual invalid values, and values which should be valid but aren't due to issue #412. Bug: 412 Change-Id: Iba7f2869607e6361d6cb913416de21a19cdd6275 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/63527 Reviewed-by: Bob Beck <[email protected]> Commit-Queue: David Benjamin <[email protected]>
1 parent 55715e3 commit a014bda

File tree

1 file changed

+106
-19
lines changed

1 file changed

+106
-19
lines changed

crypto/x509/x509_test.cc

+106-19
Original file line numberDiff line numberDiff line change
@@ -6547,6 +6547,30 @@ TEST(X509Test, NameAttributeValues) {
65476547
// we decide to later.
65486548
static const uint8_t kOID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
65496549
0x04, 0x01, 0x84, 0xb7, 0x09, 0x00};
6550+
static const char kOIDText[] = "1.2.840.113554.4.1.72585.0";
6551+
6552+
auto encode_single_attribute_name =
6553+
[](CBS_ASN1_TAG tag,
6554+
const std::string &contents) -> std::vector<uint8_t> {
6555+
bssl::ScopedCBB cbb;
6556+
CBB seq, rdn, attr, attr_type, attr_value;
6557+
if (!CBB_init(cbb.get(), 128) ||
6558+
!CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE) ||
6559+
!CBB_add_asn1(&seq, &rdn, CBS_ASN1_SET) ||
6560+
!CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
6561+
!CBB_add_asn1(&attr, &attr_type, CBS_ASN1_OBJECT) ||
6562+
!CBB_add_bytes(&attr_type, kOID, sizeof(kOID)) ||
6563+
!CBB_add_asn1(&attr, &attr_value, tag) ||
6564+
!CBB_add_bytes(&attr_value,
6565+
reinterpret_cast<const uint8_t *>(contents.data()),
6566+
contents.size()) ||
6567+
!CBB_flush(cbb.get())) {
6568+
ADD_FAILURE() << "Could not encode name";
6569+
return {};
6570+
};
6571+
return std::vector<uint8_t>(CBB_data(cbb.get()),
6572+
CBB_data(cbb.get()) + CBB_len(cbb.get()));
6573+
};
65506574

65516575
const struct {
65526576
CBS_ASN1_TAG der_tag;
@@ -6569,6 +6593,11 @@ TEST(X509Test, NameAttributeValues) {
65696593
// ENUMERATED is supported but, currently, INTEGER is not.
65706594
{CBS_ASN1_ENUMERATED, "\x01", V_ASN1_ENUMERATED, "\x01"},
65716595

6596+
// Test negative values. These are interesting because, when encoding, the
6597+
// ASN.1 type must be determined from the string type, but the string type
6598+
// has an extra |V_ASN1_NEG| bit.
6599+
{CBS_ASN1_ENUMERATED, "\xff", V_ASN1_NEG_ENUMERATED, "\x01"},
6600+
65726601
// SEQUENCE is supported but, currently, SET is not. Note the
65736602
// |ASN1_STRING| representation will include the tag and length.
65746603
{CBS_ASN1_SEQUENCE, "", V_ASN1_SEQUENCE, std::string("\x30\x00", 2)},
@@ -6596,27 +6625,16 @@ TEST(X509Test, NameAttributeValues) {
65966625

65976626
// Construct an X.509 name containing a single RDN with a single attribute:
65986627
// kOID with the specified value.
6599-
bssl::ScopedCBB cbb;
6600-
ASSERT_TRUE(CBB_init(cbb.get(), 128));
6601-
CBB seq, rdn, attr, attr_type, attr_value;
6602-
ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
6603-
ASSERT_TRUE(CBB_add_asn1(&seq, &rdn, CBS_ASN1_SET));
6604-
ASSERT_TRUE(CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE));
6605-
ASSERT_TRUE(CBB_add_asn1(&attr, &attr_type, CBS_ASN1_OBJECT));
6606-
ASSERT_TRUE(CBB_add_bytes(&attr_type, kOID, sizeof(kOID)));
6607-
ASSERT_TRUE(CBB_add_asn1(&attr, &attr_value, t.der_tag));
6608-
ASSERT_TRUE(CBB_add_bytes(
6609-
&attr_value, reinterpret_cast<const uint8_t *>(t.der_contents.data()),
6610-
t.der_contents.size()));
6611-
ASSERT_TRUE(CBB_flush(cbb.get()));
6612-
SCOPED_TRACE(Bytes(CBB_data(cbb.get()), CBB_len(cbb.get())));
6628+
auto encoded = encode_single_attribute_name(t.der_tag, t.der_contents);
6629+
ASSERT_FALSE(encoded.empty());
6630+
SCOPED_TRACE(Bytes(encoded));
66136631

66146632
// The input should parse.
6615-
const uint8_t *inp = CBB_data(cbb.get());
6633+
const uint8_t *inp = encoded.data();
66166634
bssl::UniquePtr<X509_NAME> name(
6617-
d2i_X509_NAME(nullptr, &inp, CBB_len(cbb.get())));
6635+
d2i_X509_NAME(nullptr, &inp, encoded.size()));
66186636
ASSERT_TRUE(name);
6619-
EXPECT_EQ(inp, CBB_data(cbb.get()) + CBB_len(cbb.get()))
6637+
EXPECT_EQ(inp, encoded.data() + encoded.size())
66206638
<< "input was not fully consumed";
66216639

66226640
// Check there is a single attribute with the expected in-memory
@@ -6635,7 +6653,76 @@ TEST(X509Test, NameAttributeValues) {
66356653
int der_len = i2d_X509_NAME(name.get(), &der);
66366654
ASSERT_GE(der_len, 0);
66376655
bssl::UniquePtr<uint8_t> free_der(der);
6638-
EXPECT_EQ(Bytes(der, der_len),
6639-
(Bytes(CBB_data(cbb.get()), CBB_len(cbb.get()))));
6656+
EXPECT_EQ(Bytes(der, der_len), Bytes(encoded));
6657+
6658+
// X509_NAME internally caches its encoding, which means the check above
6659+
// does not fully test re-encoding. Repeat the test by constructing an
6660+
// |X509_NAME| from the string representation.
6661+
name.reset(X509_NAME_new());
6662+
ASSERT_TRUE(name);
6663+
ASSERT_TRUE(X509_NAME_add_entry_by_txt(
6664+
name.get(), kOIDText, t.str_type,
6665+
reinterpret_cast<const uint8_t *>(t.str_contents.data()),
6666+
t.str_contents.size(), /*loc=*/-1, /*set=*/0));
6667+
6668+
// The name should re-encode with the same input.
6669+
der = nullptr;
6670+
der_len = i2d_X509_NAME(name.get(), &der);
6671+
ASSERT_GE(der_len, 0);
6672+
free_der.reset(der);
6673+
EXPECT_EQ(Bytes(der, der_len), Bytes(encoded));
6674+
}
6675+
6676+
const struct {
6677+
CBS_ASN1_TAG der_tag;
6678+
std::string der_contents;
6679+
} kInvalidTests[] = {
6680+
// Errors in supported universal types should be handled.
6681+
{CBS_ASN1_NULL, "not null"},
6682+
{CBS_ASN1_BOOLEAN, "not bool"},
6683+
{CBS_ASN1_OBJECT, ""},
6684+
{CBS_ASN1_INTEGER, std::string("\0\0", 2)},
6685+
{CBS_ASN1_ENUMERATED, std::string("\0\0", 2)},
6686+
{CBS_ASN1_BITSTRING, ""},
6687+
{CBS_ASN1_UTF8STRING, "not utf-8 \xff"},
6688+
{CBS_ASN1_BMPSTRING, "not utf-16 "},
6689+
{CBS_ASN1_UNIVERSALSTRING, "not utf-32"},
6690+
{CBS_ASN1_UTCTIME, "not utctime"},
6691+
{CBS_ASN1_GENERALIZEDTIME, "not generalizedtime"},
6692+
{CBS_ASN1_UTF8STRING | CBS_ASN1_CONSTRUCTED, ""},
6693+
{CBS_ASN1_SEQUENCE & ~CBS_ASN1_CONSTRUCTED, ""},
6694+
6695+
// TODO(crbug.com/boringssl/412): The following inputs should parse, but
6696+
// are currently rejected because they cannot be represented in
6697+
// |ASN1_PRINTABLE|, either because they don't fit in |ASN1_STRING| or
6698+
// simply in the |B_ASN1_PRINTABLE| bitmask.
6699+
{CBS_ASN1_NULL, ""},
6700+
{CBS_ASN1_BOOLEAN, std::string("\x00", 1)},
6701+
{CBS_ASN1_BOOLEAN, "\xff"},
6702+
{CBS_ASN1_OBJECT, "\x01\x02\x03\x04"},
6703+
{CBS_ASN1_INTEGER, "\x01"},
6704+
{CBS_ASN1_INTEGER, "\xff"},
6705+
{CBS_ASN1_OCTETSTRING, ""},
6706+
{CBS_ASN1_UTCTIME, "700101000000Z"},
6707+
{CBS_ASN1_GENERALIZEDTIME, "19700101000000Z"},
6708+
{CBS_ASN1_SET, ""},
6709+
{CBS_ASN1_APPLICATION | CBS_ASN1_CONSTRUCTED | 42, ""},
6710+
{CBS_ASN1_APPLICATION | 42, ""},
6711+
};
6712+
for (const auto &t : kInvalidTests) {
6713+
SCOPED_TRACE(t.der_tag);
6714+
SCOPED_TRACE(Bytes(t.der_contents));
6715+
6716+
// Construct an X.509 name containing a single RDN with a single attribute:
6717+
// kOID with the specified value.
6718+
auto encoded = encode_single_attribute_name(t.der_tag, t.der_contents);
6719+
ASSERT_FALSE(encoded.empty());
6720+
SCOPED_TRACE(Bytes(encoded));
6721+
6722+
// The input should not parse.
6723+
const uint8_t *inp = encoded.data();
6724+
bssl::UniquePtr<X509_NAME> name(
6725+
d2i_X509_NAME(nullptr, &inp, encoded.size()));
6726+
EXPECT_FALSE(name);
66406727
}
66416728
}

0 commit comments

Comments
 (0)