@@ -6547,6 +6547,30 @@ TEST(X509Test, NameAttributeValues) {
6547
6547
// we decide to later.
6548
6548
static const uint8_t kOID [] = {0x2a , 0x86 , 0x48 , 0x86 , 0xf7 , 0x12 ,
6549
6549
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
+ };
6550
6574
6551
6575
const struct {
6552
6576
CBS_ASN1_TAG der_tag;
@@ -6569,6 +6593,11 @@ TEST(X509Test, NameAttributeValues) {
6569
6593
// ENUMERATED is supported but, currently, INTEGER is not.
6570
6594
{CBS_ASN1_ENUMERATED, " \x01 " , V_ASN1_ENUMERATED, " \x01 " },
6571
6595
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
+
6572
6601
// SEQUENCE is supported but, currently, SET is not. Note the
6573
6602
// |ASN1_STRING| representation will include the tag and length.
6574
6603
{CBS_ASN1_SEQUENCE, " " , V_ASN1_SEQUENCE, std::string (" \x30\x00 " , 2 )},
@@ -6596,27 +6625,16 @@ TEST(X509Test, NameAttributeValues) {
6596
6625
6597
6626
// Construct an X.509 name containing a single RDN with a single attribute:
6598
6627
// 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));
6613
6631
6614
6632
// The input should parse.
6615
- const uint8_t *inp = CBB_data (cbb. get () );
6633
+ const uint8_t *inp = encoded. data ( );
6616
6634
bssl::UniquePtr<X509_NAME> name (
6617
- d2i_X509_NAME (nullptr , &inp, CBB_len (cbb. get () )));
6635
+ d2i_X509_NAME (nullptr , &inp, encoded. size ( )));
6618
6636
ASSERT_TRUE (name);
6619
- EXPECT_EQ (inp, CBB_data (cbb. get ()) + CBB_len (cbb. get () ))
6637
+ EXPECT_EQ (inp, encoded. data () + encoded. size ( ))
6620
6638
<< " input was not fully consumed" ;
6621
6639
6622
6640
// Check there is a single attribute with the expected in-memory
@@ -6635,7 +6653,76 @@ TEST(X509Test, NameAttributeValues) {
6635
6653
int der_len = i2d_X509_NAME (name.get (), &der);
6636
6654
ASSERT_GE (der_len, 0 );
6637
6655
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);
6640
6727
}
6641
6728
}
0 commit comments