Skip to content

Commit 369254a

Browse files
Reinstate support for constructed strings in crypto/asn1
1 parent f13b01c commit 369254a

File tree

5 files changed

+213
-57
lines changed

5 files changed

+213
-57
lines changed

crypto/asn1/asn1_test.cc

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,10 +2404,10 @@ TEST(ASN1Test, ZeroTag) {
24042404
ExpectParse(d2i_X509_ALGOR,
24052405
{0x30, 0x10, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04,
24062406
0x01, 0x84, 0xb7, 0x09, 0x01, 0x20, 0x00},
2407-
false);
2407+
true);
24082408

2409-
ExpectParse(d2i_ASN1_TYPE, {0x20, 0x00}, false);
2410-
ExpectParse(d2i_ASN1_TYPE, {0x20, 0x00}, false);
2409+
ExpectParse(d2i_ASN1_TYPE, {0x20, 0x00}, true);
2410+
ExpectParse(d2i_ASN1_TYPE, {0x20, 0x00}, true);
24112411
}
24122412

24132413
TEST(ASN1Test, IndefiniteLength) {
@@ -2418,19 +2418,21 @@ TEST(ASN1Test, IndefiniteLength) {
24182418
{0x31, 0x80, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00},
24192419
true);
24202420

2421-
// The ones below use constructed form and should fail for now. This is
2422-
// indicated with (0x20 | 0x??) in the first byte.
2423-
ExpectParse(d2i_ASN1_INTEGER,
2424-
{0x22, 0x80, 0x02, 0x01, 0x12, 0x02, 0x01, 0x34, 0x00, 0x00},
2425-
false);
2426-
ExpectParse(
2427-
d2i_ASN1_OCTET_STRING,
2428-
{0x24, 0x80, 0x04, 0x02, 0x12, 0x34, 0x04, 0x02, 0x56, 0x78, 0x00, 0x00},
2429-
false);
2421+
// The ones below use constructed form. This is indicated with (0x20 | 0x??)
2422+
// in the first byte.
24302423
ExpectParse(
24312424
d2i_ASN1_BIT_STRING,
24322425
{0x23, 0x80, 0x03, 0x02, 0x00, 0xFF, 0x03, 0x02, 0x00, 0xAA, 0x00, 0x00},
2433-
false);
2426+
true);
2427+
ExpectParse(
2428+
d2i_ASN1_OCTET_STRING,
2429+
{0x24, 0x80, 0x04, 0x02, 0x12, 0x34, 0x04, 0x02, 0x56, 0x78, 0x00, 0x00},
2430+
true);
2431+
// |ASN1_INTEGER|s only have primitive encoding. They do not have constructed
2432+
// forms.
2433+
ExpectParse(d2i_ASN1_INTEGER,
2434+
{0x22, 0x80, 0x02, 0x01, 0x12, 0x02, 0x01, 0x34, 0x00, 0x00},
2435+
false);
24342436
}
24352437

24362438
// Exhaustively test POSIX time conversions for every day across the millenium.

crypto/asn1/tasn_dec.c

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@
7575
static int asn1_check_eoc(const unsigned char **in, long len);
7676
static int asn1_find_end(const unsigned char **in, long len, char inf);
7777

78+
static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
79+
char inf, int tag, int aclass, int depth);
80+
81+
static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen);
82+
7883
static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
7984
char *inf, char *cst, const unsigned char **in,
8085
long len, int exptag, int expclass, char opt);
@@ -668,6 +673,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
668673
long plen;
669674
char cst, inf;
670675
const unsigned char *p;
676+
BUF_MEM buf = {0, NULL, 0 };
671677
const unsigned char *cont = NULL;
672678
long len;
673679
if (!pval) {
@@ -738,11 +744,31 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
738744
p += plen;
739745
}
740746
} else if (cst) {
741-
// This parser historically supported BER constructed strings. We no
742-
// longer do and will gradually tighten this parser into a DER
743-
// parser. BER types should use |CBS_asn1_ber_to_der|.
744-
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
745-
return 0;
747+
if (utype == V_ASN1_NULL || utype == V_ASN1_BOOLEAN
748+
|| utype == V_ASN1_OBJECT || utype == V_ASN1_INTEGER
749+
|| utype == V_ASN1_ENUMERATED) {
750+
// These types only have primitive encodings.
751+
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
752+
return 0;
753+
}
754+
755+
// Free any returned 'buf' content.
756+
// free_cont = 1;
757+
// Should really check the internal tags are correct but some things
758+
// may get this wrong. The relevant specs say that constructed string
759+
// types should be OCTET STRINGs internally irrespective of the type.
760+
// So instead just check for UNIVERSAL class and ignore the tag.
761+
if (!asn1_collect(&buf, &p, plen, inf, -1, V_ASN1_UNIVERSAL, 0)) {
762+
goto err;
763+
}
764+
len = buf.length;
765+
// Append a final null to string.
766+
if (!BUF_MEM_grow_clean(&buf, len + 1)) {
767+
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
768+
goto err;
769+
}
770+
buf.data[len] = 0;
771+
cont = (const unsigned char *)buf.data;
746772
} else {
747773
cont = p;
748774
len = plen;
@@ -757,6 +783,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
757783
*in = p;
758784
ret = 1;
759785
err:
786+
OPENSSL_free(buf.data);
760787
return ret;
761788
}
762789

@@ -960,6 +987,83 @@ static int asn1_find_end(const unsigned char **in, long len, char inf) {
960987
return 1;
961988
}
962989

990+
// This function collects the asn1 data from a constructed string type into
991+
// a buffer. The values of 'in' and 'len' should refer to the contents of the
992+
// constructed type and 'inf' should be set if it is indefinite length.
993+
994+
// This determines how many levels of recursion are permitted in ASN1 string
995+
// types. If it is not limited stack overflows can occur. If set to zero no
996+
// recursion is allowed at all. Although zero should be adequate examples
997+
// exist that require a value of 1. So 5 should be more than enough.
998+
999+
#define ASN1_MAX_STRING_NEST 5
1000+
1001+
static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
1002+
char inf, int tag, int aclass, int depth) {
1003+
const unsigned char *p, *q;
1004+
long plen;
1005+
char cst, ininf;
1006+
p = *in;
1007+
inf &= 1;
1008+
// If no buffer and not indefinite length constructed just pass over the
1009+
// encoded data
1010+
if (!buf && !inf) {
1011+
*in += len;
1012+
return 1;
1013+
}
1014+
while (len > 0) {
1015+
q = p;
1016+
// Check for EOC.
1017+
if (asn1_check_eoc(&p, len)) {
1018+
// EOC is illegal outside indefinite length constructed form
1019+
if (!inf) {
1020+
OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNEXPECTED_EOC);
1021+
return 0;
1022+
}
1023+
inf = 0;
1024+
break;
1025+
}
1026+
1027+
if (!asn1_check_tlen(&plen, NULL, NULL, &ininf, &cst, &p,
1028+
len, tag, aclass, 0)) {
1029+
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
1030+
return 0;
1031+
}
1032+
1033+
// If indefinite length constructed, update max length.
1034+
if (cst) {
1035+
if (depth >= ASN1_MAX_STRING_NEST) {
1036+
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_STRING);
1037+
return 0;
1038+
}
1039+
if (!asn1_collect(buf, &p, plen, ininf, tag, aclass, depth + 1))
1040+
return 0;
1041+
} else if (plen && !collect_data(buf, &p, plen))
1042+
return 0;
1043+
len -= p - q;
1044+
}
1045+
if (inf) {
1046+
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_EOC);
1047+
return 0;
1048+
}
1049+
*in = p;
1050+
return 1;
1051+
}
1052+
1053+
static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen) {
1054+
int len;
1055+
if (buf) {
1056+
len = buf->length;
1057+
if (!BUF_MEM_grow_clean(buf, len + plen)) {
1058+
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
1059+
return 0;
1060+
}
1061+
OPENSSL_memcpy(buf->data + len, *p, plen);
1062+
}
1063+
*p += plen;
1064+
return 1;
1065+
}
1066+
9631067
// Check for ASN1 EOC and swallow it if found.
9641068
static int asn1_check_eoc(const unsigned char **in, long len) {
9651069
const unsigned char *p;

crypto/bytestring/bytestring_test.cc

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,38 @@ static const uint8_t kWrappedIndefBER[] = {0x30, 0x08, 0x30, 0x04, 0x30,
655655
static const uint8_t kWrappedIndefDER[] = {0x30, 0x06, 0x30, 0x02,
656656
0x30, 0x00, 0x30, 0x00};
657657

658+
// kConstructedStringBER contains a deeply-nested constructed OCTET STRING.
659+
// The BER conversion collapses this to one level deep, but not completely.
660+
static const uint8_t kConstructedStringBER[] = {
661+
0xa0, 0x10, 0x24, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01,
662+
0x01, 0x24, 0x06, 0x04, 0x01, 0x02, 0x04, 0x01, 0x03,
663+
};
664+
static const uint8_t kConstructedStringDER[] = {
665+
0xa0, 0x08, 0x04, 0x02, 0x00, 0x01, 0x04, 0x02, 0x02, 0x03,
666+
};
667+
668+
// kWrappedConstructedStringBER contains a constructed OCTET STRING, wrapped
669+
// and followed by valid DER. This tests that we correctly identify BER nested
670+
// inside DER.
671+
//
672+
// SEQUENCE {
673+
// SEQUENCE {
674+
// [OCTET_STRING CONSTRUCTED] {
675+
// OCTET_STRING {}
676+
// }
677+
// }
678+
// SEQUENCE {}
679+
// }
680+
static const uint8_t kWrappedConstructedStringBER[] = {
681+
0x30, 0x08, 0x30, 0x04, 0x24, 0x02, 0x04, 0x00, 0x30, 0x00};
682+
static const uint8_t kWrappedConstructedStringDER[] = {
683+
0x30, 0x06, 0x30, 0x02, 0x04, 0x00, 0x30, 0x00};
684+
685+
// kConstructedBitString contains a BER constructed BIT STRING. These are not
686+
// supported and thus are left unchanged.
687+
static const uint8_t kConstructedBitStringBER[] = {
688+
0x23, 0x0a, 0x03, 0x03, 0x00, 0x12, 0x34, 0x03, 0x03, 0x00, 0x56, 0x78};
689+
658690
TEST(CBSTest, BerConvert) {
659691
static const uint8_t kSimpleBER[] = {0x01, 0x01, 0x00};
660692

@@ -686,38 +718,6 @@ TEST(CBSTest, BerConvert) {
686718
0x6e, 0x10, 0x9b, 0xb8, 0x02, 0x02, 0x07, 0xd0,
687719
};
688720

689-
// kConstructedStringBER contains a deeply-nested constructed OCTET STRING.
690-
// The BER conversion collapses this to one level deep, but not completely.
691-
static const uint8_t kConstructedStringBER[] = {
692-
0xa0, 0x10, 0x24, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01,
693-
0x01, 0x24, 0x06, 0x04, 0x01, 0x02, 0x04, 0x01, 0x03,
694-
};
695-
static const uint8_t kConstructedStringDER[] = {
696-
0xa0, 0x08, 0x04, 0x02, 0x00, 0x01, 0x04, 0x02, 0x02, 0x03,
697-
};
698-
699-
// kWrappedConstructedStringBER contains a constructed OCTET STRING, wrapped
700-
// and followed by valid DER. This tests that we correctly identify BER nested
701-
// inside DER.
702-
//
703-
// SEQUENCE {
704-
// SEQUENCE {
705-
// [OCTET_STRING CONSTRUCTED] {
706-
// OCTET_STRING {}
707-
// }
708-
// }
709-
// SEQUENCE {}
710-
// }
711-
static const uint8_t kWrappedConstructedStringBER[] = {
712-
0x30, 0x08, 0x30, 0x04, 0x24, 0x02, 0x04, 0x00, 0x30, 0x00};
713-
static const uint8_t kWrappedConstructedStringDER[] = {
714-
0x30, 0x06, 0x30, 0x02, 0x04, 0x00, 0x30, 0x00};
715-
716-
// kConstructedBitString contains a BER constructed BIT STRING. These are not
717-
// supported and thus are left unchanged.
718-
static const uint8_t kConstructedBitStringBER[] = {
719-
0x23, 0x0a, 0x03, 0x03, 0x00, 0x12, 0x34, 0x03, 0x03, 0x00, 0x56, 0x78};
720-
721721
ExpectBerConvert("kSimpleBER", kSimpleBER, kSimpleBER);
722722
ExpectBerConvert("kNonMinimalLengthBER", kNonMinimalLengthDER,
723723
kNonMinimalLengthBER);
@@ -762,12 +762,23 @@ TEST(CBSTest, IndefiniteBerASN1Macros) {
762762
kWrappedIndefBER + sizeof(kWrappedIndefBER)),
763763
true);
764764

765-
// TODO: Change expectation of below to true. Below use BER constructed
766-
// strings and will still fail until we revert a70edd4.
765+
// Below use BER constructed strings.
767766
ExpectParse(d2i_ASN1_OCTET_STRING,
768767
std::vector<uint8_t>(kOctetStringBER,
769768
kOctetStringBER + sizeof(kOctetStringBER)),
770-
false);
769+
true);
770+
ExpectParse(d2i_ASN1_TYPE,
771+
std::vector<uint8_t>(kConstructedStringBER,
772+
kConstructedStringBER + sizeof(kConstructedStringBER)),
773+
true);
774+
ExpectParse(d2i_ASN1_SEQUENCE_ANY,
775+
std::vector<uint8_t>(kWrappedConstructedStringBER,
776+
kWrappedConstructedStringBER + sizeof(kWrappedConstructedStringBER)),
777+
true);
778+
ExpectParse(d2i_ASN1_BIT_STRING,
779+
std::vector<uint8_t>(kConstructedBitStringBER,
780+
kConstructedBitStringBER + sizeof(kConstructedBitStringBER)),
781+
true);
771782
}
772783

773784
struct BERTest {

crypto/pkcs7/pkcs7_test.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,32 @@ CSqGSIb3DQEBAQUABIGAWUNdzvU2iiQOtihBwF0h48Nnw/2qX8uRjg6CVTOMcGji
22102210
BxjUMifEbT//KJwljshl4y3yBLqeVYLOd04k6aKSdjgdZnrnUPI6p5tL5PfJkTAE
22112211
L6qflZ9YCU5erE4T5U98hCQBMh4nOYxgaTjnZzhpkKQuEiKq/755cjzTzlI/eok=
22122212
-----END PKCS7-----
2213+
)";
2214+
2215+
// The test has an expected output. Check that we output the same contents
2216+
// as OpenSSL.
2217+
static const char kPKCS7RubyOpenSSLOutput[] =
2218+
R"(-----BEGIN PKCS7-----
2219+
MIIDawYJKoZIhvcNAQcDoIIDXDCCA1gCAQAxggEQMIIBDAIBADB1MHAxEDAOBgNV
2220+
BAoMB2V4YW1wbGUxFzAVBgNVBAMMDlRBUk1BQyBST09UIENBMSIwIAYJKoZIhvcN
2221+
AQkBFhNzb21lb25lQGV4YW1wbGUub3JnMQswCQYDVQQGEwJVUzESMBAGA1UEBwwJ
2222+
VG93biBIYWxsAgFmMA0GCSqGSIb3DQEBAQUABIGAbKV17HvGYRtRRBNz1QLpW763
2223+
UedhVj5KXi70o4BJGM04lItAgt6aFC9SruZjpWr1gCYKCaRSAg273DeGTQwsDoZ8
2224+
6CPXzBpptYLz0MteQXYYWUaPZT+xmvx4NgDyk9P9MoT7JifsPrtXuzqCRFXhGdu8
2225+
d/ru+OWxhHLvKH+bYekwggI9BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECBNs2U5m
2226+
Msd/gIICGFOnLq/EAc9Nv+HjKR3ZVPSJMq0TImjGf5Mvc3nDgI572Hdo2aku0YXM
2227+
6WjSWkpYtxpg7Cqxfl6hPSefLPUnBqlIoM2qbrE7MSKEVD6+2bW9GqYPFVg4qQLL
2228+
sOxnxJIMfOvLFfd7guL+iLH424XfiUUxaf8EdZE4u2IEl4REvkS1FoEGwyA4BEGM
2229+
SeVPedQCbZ0qY7Pc2tmZE3XfEUhIsyStG0Nb6i6AKcAFYGapbgE6kAB0gwsYcHlW
2230+
MOvsvdAfcTq6jwtHlO1s68qtvkWquTQ9lpX+fzddUUNxEHSqv5eU3oo6fT3Vj5ZF
2231+
IVlaA5ThZMrI5PgRPuwJM4GL8/VLwY5mbDLFqn/irGeEvP99J3S87ornLLunjpxS
2232+
y1/AymcVep2H32Tj82WS/IRQXBOzz4EnQRJGszKxAV6tY+Zje3sWyTTgObhlsiTQ
2233+
TDgnvtSW8RvVHqKrwgkxxEsRHg7u8UdzZ0jg+O5+3F8B6/NWMyts0OaFqT9wvI8y
2234+
O7VIy3dUtGdz7Hde6Ggp/iTn1LbgdJ3N8Hzxf1j6NMWUKHVsadvwpRJbUeqq9c3+
2235+
QuxsJi8wWemxxQCE+tPyc1dP+ej5/M7bERbSOHMGgX03758IvP7A/fy2DjGPv2+l
2236+
AwlEke0Uze1367QKgxM0nc3SZDlptY7zPIJC5saWXb8Rt2bw2JxEBOTavrp+ZwJ8
2237+
tcH961onq8Tme2ICaCzk
2238+
-----END PKCS7-----
22132239
)";
22142240

22152241
const bssl::UniquePtr<BIO> bio(
@@ -2225,4 +2251,16 @@ L6qflZ9YCU5erE4T5U98hCQBMh4nOYxgaTjnZzhpkKQuEiKq/755cjzTzlI/eok=
22252251
bssl::UniquePtr<BIO> out(BIO_new(BIO_s_mem()));
22262252
EXPECT_TRUE(PKCS7_verify(pkcs7.get(), nullptr, store.get(), nullptr,
22272253
out.get(), /*flags*/ PKCS7_NOVERIFY));
2254+
2255+
BUF_MEM *buf;
2256+
BIO_get_mem_ptr(out.get(), &buf);
2257+
bssl::UniquePtr<BIO> out2(BIO_new_mem_buf(buf->data, buf->length));
2258+
bssl::UniquePtr<PKCS7> new_pk7(d2i_PKCS7_bio(out2.get(), nullptr));
2259+
ASSERT_TRUE(new_pk7);
2260+
2261+
bssl::UniquePtr<BIO> out3(BIO_new(BIO_s_mem()));
2262+
ASSERT_TRUE(PEM_write_bio_PKCS7(out3.get(), new_pk7.get()));
2263+
2264+
BIO_get_mem_ptr(out3.get(), &buf);
2265+
EXPECT_EQ(Bytes(buf->data, buf->length), Bytes(kPKCS7RubyOpenSSLOutput));
22282266
}

crypto/x509/x509_test.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5224,9 +5224,11 @@ soBsxWI=
52245224
)";
52255225

52265226
TEST(X509Test, BER) {
5227-
// Constructed strings are forbidden in DER.
5228-
EXPECT_FALSE(CertFromPEM(kConstructedBitString));
5229-
EXPECT_FALSE(CertFromPEM(kConstructedOctetString));
5227+
// Constructed strings are forbidden in DER, but allowed in BER. AWS-LC has
5228+
// reinstated support for implicit BER constructed strings in the ASN1 macros
5229+
// to align with OpenSSL behavior.
5230+
EXPECT_TRUE(CertFromPEM(kConstructedBitString));
5231+
EXPECT_TRUE(CertFromPEM(kConstructedOctetString));
52305232
// Indefinite lengths are forbidden in DER, but allowed in BER. AWS-LC has
52315233
// reinstated indefinite BER support in the ASN1 macros to align with OpenSSL
52325234
// behavior.
@@ -7522,7 +7524,6 @@ TEST(X509Test, NameAttributeValues) {
75227524
{CBS_ASN1_UNIVERSALSTRING, "not utf-32"},
75237525
{CBS_ASN1_UTCTIME, "not utctime"},
75247526
{CBS_ASN1_GENERALIZEDTIME, "not generalizedtime"},
7525-
{CBS_ASN1_UTF8STRING | CBS_ASN1_CONSTRUCTED, ""},
75267527
{CBS_ASN1_SEQUENCE & ~CBS_ASN1_CONSTRUCTED, ""},
75277528

75287529
// TODO(crbug.com/boringssl/412): The following inputs should parse, but

0 commit comments

Comments
 (0)