summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreroman <eroman@chromium.org>2015-11-10 20:24:29 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-11 04:25:20 +0000
commit7b97d82c73a604053f0ac6b5539984e95bb14561 (patch)
treef80f4b1f284bf29c0c4338174e12e81c2e45248c
parenta91f4345158a5cd33931325312703a4e294512d0 (diff)
downloadchromium_src-7b97d82c73a604053f0ac6b5539984e95bb14561.zip
chromium_src-7b97d82c73a604053f0ac6b5539984e95bb14561.tar.gz
chromium_src-7b97d82c73a604053f0ac6b5539984e95bb14561.tar.bz2
Add a function to parse RFC 5280 "Extensions".
BUG=410574 Review URL: https://codereview.chromium.org/1415293004 Cr-Commit-Position: refs/heads/master@{#359050}
-rw-r--r--net/cert/internal/parse_certificate.cc115
-rw-r--r--net/cert/internal/parse_certificate.h63
-rw-r--r--net/cert/internal/parse_certificate_unittest.cc190
-rw-r--r--net/data/parse_certificate_unittest/extensions_basic_constraints.pem16
-rw-r--r--net/data/parse_certificate_unittest/extensions_data_after_sequence.pem11
-rw-r--r--net/data/parse_certificate_unittest/extensions_duplicate_key_usage.pem14
-rw-r--r--net/data/parse_certificate_unittest/extensions_empty_sequence.pem6
-rw-r--r--net/data/parse_certificate_unittest/extensions_extended_key_usage.pem20
-rw-r--r--net/data/parse_certificate_unittest/extensions_key_usage.pem16
-rw-r--r--net/data/parse_certificate_unittest/extensions_not_sequence.pem6
-rw-r--r--net/data/parse_certificate_unittest/extensions_policies.pem25
-rw-r--r--net/data/parse_certificate_unittest/extensions_real.pem35
-rw-r--r--net/data/parse_certificate_unittest/extensions_subject_alt_name.pem16
-rw-r--r--net/data/parse_certificate_unittest/extensions_unknown_critical.pem10
-rw-r--r--net/data/parse_certificate_unittest/extensions_unknown_non_critical.pem9
15 files changed, 552 insertions, 0 deletions
diff --git a/net/cert/internal/parse_certificate.cc b/net/cert/internal/parse_certificate.cc
index d2bc980..3247381 100644
--- a/net/cert/internal/parse_certificate.cc
+++ b/net/cert/internal/parse_certificate.cc
@@ -4,6 +4,8 @@
#include "net/cert/internal/parse_certificate.h"
+#include <utility>
+
#include "net/der/input.h"
#include "net/der/parse_values.h"
#include "net/der/parser.h"
@@ -405,4 +407,117 @@ bool ParseExtension(const der::Input& extension_tlv, ParsedExtension* out) {
return true;
}
+der::Input KeyUsageOid() {
+ // From RFC 5280:
+ //
+ // id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+ //
+ // In dotted notation: 2.5.29.15
+ static const uint8_t oid[] = {0x55, 0x1d, 0x0f};
+ return der::Input(oid);
+}
+
+der::Input SubjectAltNameOid() {
+ // From RFC 5280:
+ //
+ // id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
+ //
+ // In dotted notation: 2.5.29.17
+ static const uint8_t oid[] = {0x55, 0x1d, 0x11};
+ return der::Input(oid);
+}
+
+der::Input BasicConstraintsOid() {
+ // From RFC 5280:
+ //
+ // id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
+ //
+ // In dotted notation: 2.5.29.19
+ static const uint8_t oid[] = {0x55, 0x1d, 0x13};
+ return der::Input(oid);
+}
+
+der::Input NameConstraintsOid() {
+ // From RFC 5280:
+ //
+ // id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
+ //
+ // In dotted notation: 2.5.29.30
+ static const uint8_t oid[] = {0x55, 0x1d, 0x1e};
+ return der::Input(oid);
+}
+
+der::Input CertificatePoliciesOid() {
+ // From RFC 5280:
+ //
+ // id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
+ //
+ // In dotted notation: 2.5.29.32
+ static const uint8_t oid[] = {0x55, 0x1d, 0x20};
+ return der::Input(oid);
+}
+
+der::Input PolicyConstraintsOid() {
+ // From RFC 5280:
+ //
+ // id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
+ //
+ // In dotted notation: 2.5.29.36
+ static const uint8_t oid[] = {0x55, 0x1d, 0x24};
+ return der::Input(oid);
+}
+
+der::Input ExtKeyUsageOid() {
+ // From RFC 5280:
+ //
+ // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+ //
+ // In dotted notation: 2.5.29.37
+ static const uint8_t oid[] = {0x55, 0x1d, 0x25};
+ return der::Input(oid);
+}
+
+NET_EXPORT bool ParseExtensions(
+ const der::Input& extensions_tlv,
+ std::map<der::Input, ParsedExtension>* extensions) {
+ der::Parser parser(extensions_tlv);
+
+ // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ der::Parser extensions_parser;
+ if (!parser.ReadSequence(&extensions_parser))
+ return false;
+
+ // The Extensions SEQUENCE must contains at least 1 element (otherwise it
+ // should have been omitted).
+ if (!extensions_parser.HasMore())
+ return false;
+
+ extensions->clear();
+
+ while (extensions_parser.HasMore()) {
+ ParsedExtension extension;
+
+ der::Input extension_tlv;
+ if (!extensions_parser.ReadRawTLV(&extension_tlv))
+ return false;
+
+ if (!ParseExtension(extension_tlv, &extension))
+ return false;
+
+ bool is_duplicate =
+ !extensions->insert(std::make_pair(extension.oid, extension)).second;
+
+ // RFC 5280 says that an extension should not appear more than once.
+ if (is_duplicate)
+ return false;
+ }
+
+ // By definition the input was a single Extensions sequence, so there
+ // shouldn't be unconsumed data.
+ if (parser.HasMore())
+ return false;
+
+ return true;
+}
+
} // namespace net
diff --git a/net/cert/internal/parse_certificate.h b/net/cert/internal/parse_certificate.h
index 7d2d826..3980695 100644
--- a/net/cert/internal/parse_certificate.h
+++ b/net/cert/internal/parse_certificate.h
@@ -5,6 +5,8 @@
#ifndef NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_
#define NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_
+#include <map>
+
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "net/base/net_export.h"
@@ -245,6 +247,67 @@ struct NET_EXPORT ParsedExtension {
NET_EXPORT bool ParseExtension(const der::Input& extension_tlv,
ParsedExtension* out) WARN_UNUSED_RESULT;
+// From RFC 5280:
+//
+// id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+//
+// In dotted notation: 2.5.29.15
+NET_EXPORT der::Input KeyUsageOid();
+
+// From RFC 5280:
+//
+// id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
+//
+// In dotted notation: 2.5.29.17
+NET_EXPORT der::Input SubjectAltNameOid();
+
+// From RFC 5280:
+//
+// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
+//
+// In dotted notation: 2.5.29.19
+NET_EXPORT der::Input BasicConstraintsOid();
+
+// From RFC 5280:
+//
+// id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
+//
+// In dotted notation: 2.5.29.30
+NET_EXPORT der::Input NameConstraintsOid();
+
+// From RFC 5280:
+//
+// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
+//
+// In dotted notation: 2.5.29.32
+NET_EXPORT der::Input CertificatePoliciesOid();
+
+// From RFC 5280:
+//
+// id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
+//
+// In dotted notation: 2.5.29.36
+NET_EXPORT der::Input PolicyConstraintsOid();
+
+// From RFC 5280:
+//
+// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+//
+// In dotted notation: 2.5.29.37
+NET_EXPORT der::Input ExtKeyUsageOid();
+
+// Parses the Extensions sequence as defined by RFC 5280. Extensions are added
+// to the map |extensions| keyed by the OID. Parsing guarantees that each OID
+// is unique. Note that certificate verification must consume each extension
+// marked as critical.
+//
+// Returns true on success and fills |extensions|. The output will reference
+// bytes in |extensions_tlv|, so that data must be kept alive.
+// On failure |extensions| may be partially written to and should not be used.
+NET_EXPORT bool ParseExtensions(
+ const der::Input& extensions_tlv,
+ std::map<der::Input, ParsedExtension>* extensions) WARN_UNUSED_RESULT;
+
} // namespace net
#endif // NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_
diff --git a/net/cert/internal/parse_certificate_unittest.cc b/net/cert/internal/parse_certificate_unittest.cc
index 99312cd..5292e5b 100644
--- a/net/cert/internal/parse_certificate_unittest.cc
+++ b/net/cert/internal/parse_certificate_unittest.cc
@@ -391,6 +391,196 @@ TEST(ParseExtensionTest, Critical3) {
ParseExtensionFromFile("extension_critical_3.pem", &extension, &data));
}
+// Runs a test for extensions parsing. The input file is a PEM file which
+// contains a DER-encoded Extensions sequence, as well as the expected value
+// for each contained extension.
+void EnsureParsingExtensionsSucceeds(
+ const std::string& file_name,
+ std::map<der::Input, ParsedExtension>* extensions,
+ std::string* data) {
+ const PemBlockMapping mappings[] = {
+ // Test Input.
+ {"EXTENSIONS", data},
+ };
+
+ ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(file_name), mappings));
+ ASSERT_TRUE(ParseExtensions(InputFromString(data), extensions));
+}
+
+// Runs a test that verifies extensions parsing fails. The input file is a PEM
+// file which contains a DER-encoded Extensions sequence.
+void EnsureParsingExtensionsFails(const std::string& file_name) {
+ std::string data;
+
+ const PemBlockMapping mappings[] = {
+ {"EXTENSIONS", &data},
+ };
+
+ std::map<der::Input, ParsedExtension> extensions;
+ ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(file_name), mappings));
+ ASSERT_FALSE(ParseExtensions(InputFromString(&data), &extensions));
+}
+
+// Parses an Extensions that is an empty sequence.
+TEST(ParseExtensionsTest, EmptySequence) {
+ EnsureParsingExtensionsFails("extensions_empty_sequence.pem");
+}
+
+// Parses an Extensions that is not a sequence.
+TEST(ParseExtensionsTest, NotSequence) {
+ EnsureParsingExtensionsFails("extensions_not_sequence.pem");
+}
+
+// Parses an Extensions that has data after the sequence.
+TEST(ParseExtensionsTest, DataAfterSequence) {
+ EnsureParsingExtensionsFails("extensions_data_after_sequence.pem");
+}
+
+// Parses an Extensions that contains duplicated key usages.
+TEST(ParseExtensionsTest, DuplicateKeyUsage) {
+ EnsureParsingExtensionsFails("extensions_duplicate_key_usage.pem");
+}
+
+// Parses an Extensions that contains an unknown critical extension.
+TEST(ParseExtensionsTest, UnknownCritical) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_unknown_critical.pem",
+ &extensions, &data);
+
+ ASSERT_EQ(1u, extensions.size());
+ // This OID corresponds with
+ // 1.2.840.113554.4.1.72585.0 (https://davidben.net/oid)
+ const uint8_t oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
+ 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00};
+
+ auto iter = extensions.find(der::Input(oid));
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_TRUE(iter->second.critical);
+ EXPECT_EQ(4u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains an unknown non-critical extension.
+TEST(ParseExtensionsTest, UnknownNonCritical) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_unknown_non_critical.pem",
+ &extensions, &data);
+
+ ASSERT_EQ(1u, extensions.size());
+ // This OID corresponds with
+ // 1.2.840.113554.4.1.72585.0 (https://davidben.net/oid)
+ const uint8_t oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
+ 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00};
+
+ auto iter = extensions.find(der::Input(oid));
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_FALSE(iter->second.critical);
+ EXPECT_EQ(4u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains a basic constraints.
+TEST(ParseExtensionsTest, BasicConstraints) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_basic_constraints.pem",
+ &extensions, &data);
+
+ ASSERT_EQ(1u, extensions.size());
+
+ auto iter = extensions.find(BasicConstraintsOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_TRUE(iter->second.critical);
+ EXPECT_EQ(2u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains an extended key usages.
+TEST(ParseExtensionsTest, ExtendedKeyUsage) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_extended_key_usage.pem",
+ &extensions, &data);
+
+ ASSERT_EQ(1u, extensions.size());
+
+ auto iter = extensions.find(ExtKeyUsageOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_FALSE(iter->second.critical);
+ EXPECT_EQ(45u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains a key usage.
+TEST(ParseExtensionsTest, KeyUsage) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_key_usage.pem", &extensions,
+ &data);
+
+ ASSERT_EQ(1u, extensions.size());
+
+ auto iter = extensions.find(KeyUsageOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_TRUE(iter->second.critical);
+ EXPECT_EQ(4u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains a policies extension.
+TEST(ParseExtensionsTest, Policies) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_policies.pem", &extensions,
+ &data);
+
+ ASSERT_EQ(1u, extensions.size());
+
+ auto iter = extensions.find(CertificatePoliciesOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_FALSE(iter->second.critical);
+ EXPECT_EQ(95u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains a subjectaltname extension.
+TEST(ParseExtensionsTest, SubjectAltName) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_subject_alt_name.pem",
+ &extensions, &data);
+
+ ASSERT_EQ(1u, extensions.size());
+
+ auto iter = extensions.find(SubjectAltNameOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_FALSE(iter->second.critical);
+ EXPECT_EQ(23u, iter->second.value.Length());
+}
+
+// Parses an Extensions that contains multiple extensions, sourced from a
+// real-world certificate.
+TEST(ParseExtensionsTest, Real) {
+ std::string data;
+ std::map<der::Input, ParsedExtension> extensions;
+ EnsureParsingExtensionsSucceeds("extensions_real.pem", &extensions, &data);
+
+ ASSERT_EQ(7u, extensions.size());
+
+ auto iter = extensions.find(KeyUsageOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_TRUE(iter->second.critical);
+ EXPECT_EQ(4u, iter->second.value.Length());
+
+ iter = extensions.find(BasicConstraintsOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_TRUE(iter->second.critical);
+ EXPECT_EQ(8u, iter->second.value.Length());
+
+ iter = extensions.find(CertificatePoliciesOid());
+ ASSERT_TRUE(iter != extensions.end());
+ EXPECT_FALSE(iter->second.critical);
+ EXPECT_EQ(16u, iter->second.value.Length());
+
+ // TODO(eroman): Verify the other 4 extensions' values.
+}
+
} // namespace
} // namespace net
diff --git a/net/data/parse_certificate_unittest/extensions_basic_constraints.pem b/net/data/parse_certificate_unittest/extensions_basic_constraints.pem
new file mode 100644
index 0000000..211cef0
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_basic_constraints.pem
@@ -0,0 +1,16 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 14 cons: SEQUENCE
+ 2:d=1 hl=2 l= 12 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
+ 9:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 12:d=2 hl=2 l= 2 prim: OCTET STRING [HEX DUMP]:3000
+-----BEGIN EXTENSIONS-----
+MA4wDAYDVR0TAQH/BAIwAA==
+-----END EXTENSIONS-----
+
+$ openssl asn1parse -i < [BASIC CONSTRAINTS]
+ 0:d=0 hl=2 l= 0 cons: SEQUENCE
+-----BEGIN BASIC CONSTRAINTS-----
+MAA=
+-----END BASIC CONSTRAINTS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_data_after_sequence.pem b/net/data/parse_certificate_unittest/extensions_data_after_sequence.pem
new file mode 100644
index 0000000..5bf26ac
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_data_after_sequence.pem
@@ -0,0 +1,11 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 18 cons: SEQUENCE
+ 2:d=1 hl=2 l= 14 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
+ 9:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 12:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030203B8
+ 18:d=1 hl=2 l= 0 prim: NULL
+-----BEGIN EXTENSIONS-----
+MBIwDgYDVR0PAQH/BAQDAgO4BQA=
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_duplicate_key_usage.pem b/net/data/parse_certificate_unittest/extensions_duplicate_key_usage.pem
new file mode 100644
index 0000000..c263384
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_duplicate_key_usage.pem
@@ -0,0 +1,14 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 32 cons: SEQUENCE
+ 2:d=1 hl=2 l= 14 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
+ 9:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 12:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030203B8
+ 18:d=1 hl=2 l= 14 cons: SEQUENCE
+ 20:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
+ 25:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 28:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030203B8
+-----BEGIN EXTENSIONS-----
+MCAwDgYDVR0PAQH/BAQDAgO4MA4GA1UdDwEB/wQEAwIDuA==
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_empty_sequence.pem b/net/data/parse_certificate_unittest/extensions_empty_sequence.pem
new file mode 100644
index 0000000..d3f1a73
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_empty_sequence.pem
@@ -0,0 +1,6 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 0 cons: SEQUENCE
+-----BEGIN EXTENSIONS-----
+MAA=
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_extended_key_usage.pem b/net/data/parse_certificate_unittest/extensions_extended_key_usage.pem
new file mode 100644
index 0000000..9cda6e2
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_extended_key_usage.pem
@@ -0,0 +1,20 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 54 cons: SEQUENCE
+ 2:d=1 hl=2 l= 52 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Extended Key Usage
+ 9:d=2 hl=2 l= 45 prim: OCTET STRING [HEX DUMP]:302B06082B0601050507030106082B06010505070302060A2B0601040182370A030306096086480186F8420401
+-----BEGIN EXTENSIONS-----
+MDYwNAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglghkgBhvhCBAE
+=
+-----END EXTENSIONS-----
+
+$ openssl asn1parse -i < [EXTENDED KEY USAGE]
+ 0:d=0 hl=2 l= 43 cons: SEQUENCE
+ 2:d=1 hl=2 l= 8 prim: OBJECT :TLS Web Server Authentication
+ 12:d=1 hl=2 l= 8 prim: OBJECT :TLS Web Client Authentication
+ 22:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Server Gated Crypto
+ 34:d=1 hl=2 l= 9 prim: OBJECT :Netscape Server Gated Crypto
+-----BEGIN EXTENDED KEY USAGE-----
+MCsGCCsGAQUFBwMBBggrBgEFBQcDAgYKKwYBBAGCNwoDAwYJYIZIAYb4QgQB
+-----END EXTENDED KEY USAGE-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_key_usage.pem b/net/data/parse_certificate_unittest/extensions_key_usage.pem
new file mode 100644
index 0000000..1b56edd
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_key_usage.pem
@@ -0,0 +1,16 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 16 cons: SEQUENCE
+ 2:d=1 hl=2 l= 14 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
+ 9:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 12:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030205A0
+-----BEGIN EXTENSIONS-----
+MBAwDgYDVR0PAQH/BAQDAgWg
+-----END EXTENSIONS-----
+
+$ openssl asn1parse -i < [KEY USAGE]
+ 0:d=0 hl=2 l= 2 prim: BIT STRING
+-----BEGIN KEY USAGE-----
+AwIFoA==
+-----END KEY USAGE-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_not_sequence.pem b/net/data/parse_certificate_unittest/extensions_not_sequence.pem
new file mode 100644
index 0000000..f8cb11b
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_not_sequence.pem
@@ -0,0 +1,6 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 0 prim: NULL
+-----BEGIN EXTENSIONS-----
+BQA=
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_policies.pem b/net/data/parse_certificate_unittest/extensions_policies.pem
new file mode 100644
index 0000000..79a1c90
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_policies.pem
@@ -0,0 +1,25 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 104 cons: SEQUENCE
+ 2:d=1 hl=2 l= 102 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Certificate Policies
+ 9:d=2 hl=2 l= 95 prim: OCTET STRING [HEX DUMP]:305D304D060A2A83088C9C1E01020201303F303D06082B06010505070201163168747470733A2F2F7265706F312E7365636F6D74727573742E6E65742F73706370702F6370732F696E6465782E68746D6C300C060A2A83088C9B1B64870501
+-----BEGIN EXTENSIONS-----
+MGgwZgYDVR0gBF8wXTBNBgoqgwiMnB4BAgIBMD8wPQYIKwYBBQUHAgEWMWh0dHBzOi8vcmVwbzE
+uc2Vjb210cnVzdC5uZXQvc3BjcHAvY3BzL2luZGV4Lmh0bWwwDAYKKoMIjJsbZIcFAQ==
+-----END EXTENSIONS-----
+
+$ openssl asn1parse -i < [POLICIES]
+ 0:d=0 hl=2 l= 93 cons: SEQUENCE
+ 2:d=1 hl=2 l= 77 cons: SEQUENCE
+ 4:d=2 hl=2 l= 10 prim: OBJECT :1.2.392.200222.1.2.2.1
+ 16:d=2 hl=2 l= 63 cons: SEQUENCE
+ 18:d=3 hl=2 l= 61 cons: SEQUENCE
+ 20:d=4 hl=2 l= 8 prim: OBJECT :Policy Qualifier CPS
+ 30:d=4 hl=2 l= 49 prim: IA5STRING :https://repo1.secomtrust.net/spcpp/cps/index.html
+ 81:d=1 hl=2 l= 12 cons: SEQUENCE
+ 83:d=2 hl=2 l= 10 prim: OBJECT :1.2.392.200091.100.901.1
+-----BEGIN POLICIES-----
+MF0wTQYKKoMIjJweAQICATA/MD0GCCsGAQUFBwIBFjFodHRwczovL3JlcG8xLnNlY29tdHJ1c3Q
+ubmV0L3NwY3BwL2Nwcy9pbmRleC5odG1sMAwGCiqDCIybG2SHBQE=
+-----END POLICIES-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_real.pem b/net/data/parse_certificate_unittest/extensions_real.pem
new file mode 100644
index 0000000..5ece71c
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_real.pem
@@ -0,0 +1,35 @@
+A real world extensions sequence (taken from Google's GAI2).
+
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=3 l= 228 cons: SEQUENCE
+ 3:d=1 hl=2 l= 31 cons: SEQUENCE
+ 5:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier
+ 10:d=2 hl=2 l= 24 prim: OCTET STRING [HEX DUMP]:30168014C07A98688D89FBAB05640C117DAA7D65B8CACC4E
+ 36:d=1 hl=2 l= 29 cons: SEQUENCE
+ 38:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier
+ 43:d=2 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:04144ADD06161BBCF668B576F581B6BB621ABA5A812F
+ 67:d=1 hl=2 l= 14 cons: SEQUENCE
+ 69:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
+ 74:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 77:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:03020106
+ 83:d=1 hl=2 l= 46 cons: SEQUENCE
+ 85:d=2 hl=2 l= 8 prim: OBJECT :Authority Information Access
+ 95:d=2 hl=2 l= 34 prim: OCTET STRING [HEX DUMP]:3020301E06082B060105050730018612687474703A2F2F672E73796D63642E636F6D
+ 131:d=1 hl=2 l= 18 cons: SEQUENCE
+ 133:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
+ 138:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 141:d=2 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:30060101FF020100
+ 151:d=1 hl=2 l= 53 cons: SEQUENCE
+ 153:d=2 hl=2 l= 3 prim: OBJECT :X509v3 CRL Distribution Points
+ 158:d=2 hl=2 l= 46 prim: OCTET STRING [HEX DUMP]:302C302AA028A0268624687474703A2F2F672E73796D63622E636F6D2F63726C732F6774676C6F62616C2E63726C
+ 206:d=1 hl=2 l= 23 cons: SEQUENCE
+ 208:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Certificate Policies
+ 213:d=2 hl=2 l= 16 prim: OCTET STRING [HEX DUMP]:300E300C060A2B06010401D679020501
+-----BEGIN EXTENSIONS-----
+MIHkMB8GA1UdIwQYMBaAFMB6mGiNifurBWQMEX2qfWW4ysxOMB0GA1UdDgQWBBRK3QYWG7z2aLV
+29YG2u2IaulqBLzAOBgNVHQ8BAf8EBAMCAQYwLgYIKwYBBQUHAQEEIjAgMB4GCCsGAQUFBzABhh
+JodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADA1BgNVHR8ELjAsMCqgKKAmh
+iRodHRwOi8vZy5zeW1jYi5jb20vY3Jscy9ndGdsb2JhbC5jcmwwFwYDVR0gBBAwDjAMBgorBgEE
+AdZ5AgUB
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_subject_alt_name.pem b/net/data/parse_certificate_unittest/extensions_subject_alt_name.pem
new file mode 100644
index 0000000..3ce5fda
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_subject_alt_name.pem
@@ -0,0 +1,16 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 32 cons: SEQUENCE
+ 2:d=1 hl=2 l= 30 cons: SEQUENCE
+ 4:d=2 hl=2 l= 3 prim: OBJECT :X509v3 Subject Alternative Name
+ 9:d=2 hl=2 l= 23 prim: OCTET STRING [HEX DUMP]:30158213656D657267656E6379737570706F72742E7573
+-----BEGIN EXTENSIONS-----
+MCAwHgYDVR0RBBcwFYITZW1lcmdlbmN5c3VwcG9ydC51cw==
+-----END EXTENSIONS-----
+
+$ openssl asn1parse -i < [SUBJECT ALT NAME]
+ 0:d=0 hl=2 l= 21 cons: SEQUENCE
+ 2:d=1 hl=2 l= 19 prim: cont [ 2 ]
+-----BEGIN SUBJECT ALT NAME-----
+MBWCE2VtZXJnZW5jeXN1cHBvcnQudXM=
+-----END SUBJECT ALT NAME-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_unknown_critical.pem b/net/data/parse_certificate_unittest/extensions_unknown_critical.pem
new file mode 100644
index 0000000..3913615
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_unknown_critical.pem
@@ -0,0 +1,10 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 25 cons: SEQUENCE
+ 2:d=1 hl=2 l= 23 cons: SEQUENCE
+ 4:d=2 hl=2 l= 12 prim: OBJECT :1.2.840.113554.4.1.72585.0
+ 18:d=2 hl=2 l= 1 prim: BOOLEAN :255
+ 21:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030203B8
+-----BEGIN EXTENSIONS-----
+MBkwFwYMKoZIhvcSBAGEtwkAAQH/BAQDAgO4
+-----END EXTENSIONS-----
+
diff --git a/net/data/parse_certificate_unittest/extensions_unknown_non_critical.pem b/net/data/parse_certificate_unittest/extensions_unknown_non_critical.pem
new file mode 100644
index 0000000..532cb2ab
--- /dev/null
+++ b/net/data/parse_certificate_unittest/extensions_unknown_non_critical.pem
@@ -0,0 +1,9 @@
+$ openssl asn1parse -i < [EXTENSIONS]
+ 0:d=0 hl=2 l= 22 cons: SEQUENCE
+ 2:d=1 hl=2 l= 20 cons: SEQUENCE
+ 4:d=2 hl=2 l= 12 prim: OBJECT :1.2.840.113554.4.1.72585.0
+ 18:d=2 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030203B8
+-----BEGIN EXTENSIONS-----
+MBYwFAYMKoZIhvcSBAGEtwkABAQDAgO4
+-----END EXTENSIONS-----
+