Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/cert/internal/parse_certificate.h" | |
| 6 | |
| 7 #include "net/der/input.h" | |
| 8 #include "net/der/parse_values.h" | |
| 9 #include "net/der/parser.h" | |
| 10 | |
| 11 namespace net { | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 // Parses a Version according to RFC 5280: | |
| 16 // | |
| 17 // Version ::= INTEGER { v1(0), v2(1), v3(2) } | |
| 18 // | |
| 19 // No value other that v1, v2, or v3 is allowed (and if given will fail). RFC | |
| 20 // 5280 minimally requires the handling of v3 (and overwhelmingly these are the | |
| 21 // certificate versions in use today): | |
|
davidben
2015/08/11 20:31:56
Do we need to handle this unspecified v4 thing?
eroman
2015/08/11 21:13:33
From what I can tell v4 is not really a thing yet
davidben
2015/08/11 22:39:20
I know nothing about what v4 is. I think Ryan said
eroman
2015/08/12 00:37:10
Acknowledged. I will try and follow-up with v4 req
| |
| 22 // | |
| 23 // Implementations SHOULD be prepared to accept any version certificate. | |
| 24 // At a minimum, conforming implementations MUST recognize version 3 | |
| 25 // certificates. | |
| 26 // | |
| 27 WARN_UNUSED_RESULT bool ParseVersion(const der::Input& in, | |
| 28 CertificateVersion* version) { | |
| 29 der::Parser parser(in); | |
| 30 uint64_t version64; | |
| 31 if (!parser.ReadUint64(&version64)) | |
| 32 return false; | |
| 33 | |
| 34 switch (version64) { | |
| 35 case 0: | |
| 36 *version = CertificateVersion::V1; | |
| 37 break; | |
| 38 case 1: | |
| 39 *version = CertificateVersion::V2; | |
| 40 break; | |
| 41 case 2: | |
| 42 *version = CertificateVersion::V3; | |
| 43 break; | |
| 44 default: | |
| 45 // Don't allow any other version identifier. | |
| 46 return false; | |
| 47 } | |
| 48 | |
| 49 // By definition the input to this function was a single INTEGER, so there | |
| 50 // shouldn't be anything else after it. | |
| 51 return !parser.HasMore(); | |
| 52 } | |
| 53 | |
| 54 // Returns true if the given serial number (CertificateSerialNumber in RFC 5280) | |
| 55 // is valid: | |
| 56 // | |
| 57 // CertificateSerialNumber ::= INTEGER | |
| 58 // | |
| 59 // The input to this function is the (unverified) value octets of the INTEGER. | |
| 60 // This function will verify that: | |
| 61 // | |
| 62 // * The octets are a valid DER-encoding of an INTEGER (for instance, minimal | |
| 63 // encoding length). | |
| 64 // | |
| 65 // * No more than 20 octets are used. | |
| 66 // | |
| 67 // Note that it DOES NOT reject non-positive values (zero or negative). | |
| 68 // | |
| 69 // For reference, here is what RFC 5280 section 4.1.2.2 says: | |
| 70 // | |
| 71 // Given the uniqueness requirements above, serial numbers can be | |
| 72 // expected to contain long integers. Certificate users MUST be able to | |
| 73 // handle serialNumber values up to 20 octets. Conforming CAs MUST NOT | |
| 74 // use serialNumber values longer than 20 octets. | |
| 75 // | |
| 76 // Note: Non-conforming CAs may issue certificates with serial numbers | |
| 77 // that are negative or zero. Certificate users SHOULD be prepared to | |
| 78 // gracefully handle such certificates. | |
| 79 WARN_UNUSED_RESULT bool VerifySerialNumber(const der::Input& value) { | |
| 80 der::ByteReader reader(value); | |
| 81 | |
| 82 if (value.Length() == 0) | |
| 83 return false; // Not a valid DER-encoded INTEGER. | |
| 84 | |
| 85 // Check if the serial number is too long per RFC 5280. | |
| 86 if (value.Length() > 20) | |
| 87 return false; | |
| 88 | |
| 89 // Accept any single-byte serial number (including zero and negatives). | |
| 90 if (value.Length() == 1) | |
| 91 return true; | |
| 92 | |
| 93 // INTEGER values in DER should be minimal. They should only contain a leading | |
| 94 // zero if the second octet has its most significant bit set to 1 (since | |
| 95 // without the leading zero the described number would be negative). | |
| 96 uint8_t first_byte; | |
| 97 if (!reader.ReadByte(&first_byte)) | |
| 98 return false; // Unexpected | |
| 99 | |
| 100 if (first_byte == 0) { | |
| 101 uint8_t second_byte; | |
| 102 if (!reader.ReadByte(&second_byte)) | |
| 103 return false; // Unexpected | |
| 104 | |
| 105 if ((second_byte & 0x80) == 0) | |
| 106 return false; // MSB must be 1. | |
| 107 } | |
| 108 | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 } // namespace | |
| 113 | |
| 114 ParsedTbsCertificate::ParsedTbsCertificate() | |
| 115 : version(CertificateVersion::V1), | |
| 116 has_issuer_unique_id(false), | |
| 117 has_subject_unique_id(false), | |
| 118 has_extensions(false) {} | |
| 119 | |
| 120 ParsedTbsCertificate::~ParsedTbsCertificate() {} | |
| 121 | |
| 122 bool ParseCertificate(const der::Input& certificate_tlv, | |
| 123 ParsedCertificate* out) { | |
|
davidben
2015/08/11 20:31:56
[I probably would have written this function in Bo
eroman
2015/08/11 21:13:33
This is a good point. I found it convenient in the
davidben
2015/08/11 22:39:20
Either's fine by me. I have noticed that Chromium
| |
| 124 der::Parser parser(certificate_tlv); | |
| 125 | |
| 126 // Certificate ::= SEQUENCE { | |
| 127 der::Parser certificate_parser; | |
| 128 if (!parser.ReadSequence(&certificate_parser)) | |
| 129 return false; | |
| 130 | |
| 131 // tbsCertificate TBSCertificate, | |
| 132 if (!certificate_parser.ReadRawTLV(&out->tbs_certificate_tlv)) | |
| 133 return false; | |
| 134 | |
| 135 // signatureAlgorithm AlgorithmIdentifier, | |
| 136 if (!certificate_parser.ReadRawTLV(&out->signature_algorithm_tlv)) | |
| 137 return false; | |
| 138 | |
| 139 // signatureValue BIT STRING } | |
| 140 if (!certificate_parser.ReadBitString(&out->signature_value)) | |
| 141 return false; | |
| 142 | |
| 143 // By definition the input was a single Certificate, so there shouldn't be | |
| 144 // unconsumed data. | |
| 145 if (parser.HasMore()) | |
| 146 return false; | |
| 147 | |
| 148 // There isn't an extension point at the end of Certificate. | |
| 149 if (certificate_parser.HasMore()) | |
| 150 return false; | |
|
davidben
2015/08/11 20:31:56
Nit: It seems better to check certificate_parser b
eroman
2015/08/11 21:13:33
Will do.
eroman
2015/08/12 00:37:10
Done.
| |
| 151 | |
| 152 return true; | |
| 153 } | |
| 154 | |
| 155 // From RFC 5280 section 4.1: | |
| 156 // | |
| 157 // Certificate ::= SEQUENCE { | |
| 158 // tbsCertificate TBSCertificate, | |
| 159 // signatureAlgorithm AlgorithmIdentifier, | |
| 160 // signatureValue BIT STRING } | |
| 161 // | |
| 162 // TBSCertificate ::= SEQUENCE { | |
| 163 // version [0] EXPLICIT Version DEFAULT v1, | |
| 164 // serialNumber CertificateSerialNumber, | |
| 165 // signature AlgorithmIdentifier, | |
| 166 // issuer Name, | |
| 167 // validity Validity, | |
| 168 // subject Name, | |
| 169 // subjectPublicKeyInfo SubjectPublicKeyInfo, | |
| 170 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, | |
| 171 // -- If present, version MUST be v2 or v3 | |
| 172 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, | |
| 173 // -- If present, version MUST be v2 or v3 | |
| 174 // extensions [3] EXPLICIT Extensions OPTIONAL | |
| 175 // -- If present, version MUST be v3 | |
| 176 // } | |
| 177 bool ParseTbsCertificate(const der::Input& tbs_tlv, ParsedTbsCertificate* out) { | |
| 178 der::Parser parser(tbs_tlv); | |
| 179 | |
| 180 // Certificate ::= SEQUENCE { | |
| 181 der::Parser tbs_parser; | |
| 182 if (!parser.ReadSequence(&tbs_parser)) | |
| 183 return false; | |
| 184 | |
| 185 // version [0] EXPLICIT Version DEFAULT v1, | |
| 186 der::Input version; | |
| 187 bool has_version; | |
| 188 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &version, | |
| 189 &has_version)) { | |
| 190 return false; | |
| 191 } | |
| 192 if (has_version) { | |
| 193 if (!ParseVersion(version, &out->version)) | |
| 194 return false; | |
|
davidben
2015/08/11 20:31:56
Although it seems we can't enforce it, there is a
eroman
2015/08/11 21:13:33
Good point!
I should enforce that here, and will d
davidben
2015/08/11 22:39:20
I believe mozilla::pkix doesn't. Although, actuall
eroman
2015/08/12 00:37:10
Acknowledged thanks for the context!
I have added
| |
| 195 } else { | |
| 196 out->version = CertificateVersion::V1; | |
| 197 } | |
| 198 | |
| 199 // serialNumber CertificateSerialNumber, | |
| 200 if (!tbs_parser.ReadTag(der::kInteger, &out->serial_number)) | |
| 201 return false; | |
| 202 if (!VerifySerialNumber(out->serial_number)) | |
| 203 return false; | |
| 204 | |
| 205 // signature AlgorithmIdentifier, | |
| 206 if (!tbs_parser.ReadRawTLV(&out->signature_algorithm_tlv)) | |
| 207 return false; | |
| 208 | |
| 209 // issuer Name, | |
| 210 if (!tbs_parser.ReadRawTLV(&out->issuer_tlv)) | |
| 211 return false; | |
| 212 | |
| 213 // validity Validity, | |
| 214 if (!tbs_parser.ReadRawTLV(&out->validity_tlv)) | |
| 215 return false; | |
| 216 | |
| 217 // subject Name, | |
| 218 if (!tbs_parser.ReadRawTLV(&out->subject_tlv)) | |
| 219 return false; | |
| 220 | |
| 221 // subjectPublicKeyInfo SubjectPublicKeyInfo, | |
| 222 if (!tbs_parser.ReadRawTLV(&out->spki_tlv)) | |
| 223 return false; | |
| 224 | |
| 225 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, | |
| 226 // -- If present, version MUST be v2 or v3 | |
| 227 der::Input issuer_unique_id; | |
| 228 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1), | |
| 229 &issuer_unique_id, | |
| 230 &out->has_issuer_unique_id)) { | |
| 231 return false; | |
| 232 } | |
| 233 if (out->has_issuer_unique_id) { | |
| 234 if (!der::ParseBitString(issuer_unique_id, &out->issuer_unique_id)) | |
|
davidben
2015/08/11 20:31:56
[Verified that ParseBitString does NOT expect a TL
| |
| 235 return false; | |
| 236 if (out->version != CertificateVersion::V2 && | |
| 237 out->version != CertificateVersion::V3) { | |
| 238 return false; | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, | |
| 243 // -- If present, version MUST be v2 or v3 | |
| 244 der::Input subject_unique_id; | |
| 245 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(2), | |
| 246 &subject_unique_id, | |
| 247 &out->has_subject_unique_id)) { | |
| 248 return false; | |
| 249 } | |
| 250 if (out->has_subject_unique_id) { | |
| 251 if (!der::ParseBitString(subject_unique_id, &out->subject_unique_id)) | |
| 252 return false; | |
| 253 if (out->version != CertificateVersion::V2 && | |
| 254 out->version != CertificateVersion::V3) { | |
| 255 return false; | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 // extensions [3] EXPLICIT Extensions OPTIONAL | |
| 260 // -- If present, version MUST be v3 | |
| 261 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(3), | |
| 262 &out->extensions_tlv, &out->has_extensions)) { | |
| 263 return false; | |
| 264 } | |
| 265 if (out->has_extensions) { | |
| 266 if (out->version != CertificateVersion::V3) | |
| 267 return false; | |
| 268 } | |
| 269 | |
| 270 // By definition the input was a single TBSCertificate, so there shouldn't be | |
| 271 // unconsumed data. | |
| 272 if (parser.HasMore()) | |
| 273 return false; | |
| 274 | |
| 275 // Note that there IS an extension point at the end of TBSCertificate | |
| 276 // (according to RFC 5912), so from that interpretation, unconsumed data would | |
| 277 // be allowed in |tbs_parser|. | |
| 278 // | |
| 279 // However because only v1, v2, and v3 certificates are supported by the | |
| 280 // parsing, there shouldn't be any subsequent data in those versions, so | |
| 281 // reject. | |
| 282 if (tbs_parser.HasMore()) | |
| 283 return false; | |
|
davidben
2015/08/11 20:31:56
Nit: Ditto re parser vs tbs_parser order.
eroman
2015/08/12 00:37:10
Done.
| |
| 284 | |
| 285 return true; | |
| 286 } | |
| 287 | |
| 288 } // namespace net | |
| OLD | NEW |