Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/strings/string_util.h" | |
| 5 #include "net/cert/internal/verify_name_match.h" | 6 #include "net/cert/internal/verify_name_match.h" |
| 6 #include "net/der/input.h" | 7 #include "net/der/input.h" |
| 8 #include "net/der/parser.h" | |
| 9 #include "net/der/tag.h" | |
| 7 | 10 |
| 8 namespace net { | 11 namespace net { |
| 9 | 12 |
| 13 namespace { | |
| 14 | |
| 15 // Advance |reader| so that the next read will return a non-space character, or | |
| 16 // the end of the input. Return true if non-space characters remain in |reader|. | |
| 17 bool SkipASCIISpace(der::ByteReader* reader) { | |
| 18 der::ByteReader peeker(*reader); | |
| 19 while (true) { | |
| 20 uint8_t c; | |
| 21 if (!peeker.ReadByte(&c)) | |
| 22 return false; | |
| 23 if (c == ' ') { | |
| 24 if (!reader->ReadByte(&c)) | |
| 25 NOTREACHED(); | |
| 26 } else { | |
| 27 return true; | |
| 28 } | |
| 29 } | |
| 30 } | |
| 31 | |
| 32 // Normalize a PrintableString value according to RFC 2459 section 4.1.2.4. | |
| 33 bool NormalizePrintableString(const der::Input& in, std::string* output) { | |
| 34 der::ByteReader reader(in); | |
| 35 | |
| 36 // Normalized version will always be equal or shorter than input, pre-reserve | |
| 37 // the space to avoid re-allocations. | |
| 38 output->reserve(in.Length()); | |
| 39 | |
| 40 // Ignore leading whitespace. | |
| 41 SkipASCIISpace(&reader); | |
| 42 | |
| 43 uint8_t c; | |
| 44 while (reader.ReadByte(&c)) { | |
| 45 if (c == ' ') { | |
| 46 // If there is non-whitespace characters remaining in input, compress | |
| 47 // multiple whitespace chars to a single space, otherwise ignore trailing | |
| 48 // whitespace. | |
| 49 if (SkipASCIISpace(&reader)) | |
| 50 *output += ' '; | |
| 51 } else if (c >= 'A' && c <= 'Z') { | |
| 52 // Fold case. | |
| 53 *output += base::ToLowerASCII(c); | |
| 54 } else if ((c >= 'a' && c <= 'z') || (c >= '\'' && c <= ')') || | |
| 55 (c >= '+' && c <= ':') || c == '=' || c == '?') { | |
| 56 // Accept remaining allowed characters: | |
| 57 // a-z | |
| 58 // ' ( ) | |
| 59 // + , - . / 0 1 2 3 4 5 6 7 8 9 : | |
| 60 // = ? | |
| 61 *output += c; | |
| 62 } else { | |
| 63 // Fail on any characters that are not valid for PrintableString. | |
| 64 // TODO(mattm): will we need to include '*'? | |
| 65 return false; | |
| 66 } | |
| 67 } | |
|
Ryan Sleevi
2015/05/21 02:20:17
Do we know what the performance implications of th
mattm
2015/06/17 03:36:18
I did some simple benchmark (release build, run un
| |
| 68 | |
| 69 return true; | |
| 70 } | |
| 71 | |
| 72 bool VerifyAttributeValueMatch(der::Parser* a, der::Parser* b) { | |
| 73 der::Input a_value, b_value; | |
| 74 | |
| 75 // Read the attribute types, which must be OBJECT IDENTIFIERs. | |
| 76 if (!a->ReadTag(der::kOid, &a_value)) | |
| 77 return false; | |
| 78 if (!b->ReadTag(der::kOid, &b_value)) | |
| 79 return false; | |
| 80 // Attribute types must be equal. | |
| 81 if (!a_value.Equals(b_value)) | |
| 82 return false; | |
| 83 | |
| 84 // Read the attribute value. | |
| 85 der::Tag a_tag, b_tag; | |
| 86 if (!a->ReadTagAndValue(&a_tag, &a_value)) | |
| 87 return false; | |
| 88 if (!b->ReadTagAndValue(&b_tag, &b_value)) | |
| 89 return false; | |
| 90 | |
| 91 // There should be no more elements in the sequence after reading the | |
| 92 // attribute type and value. | |
| 93 if (a->HasMore() || b->HasMore()) | |
| 94 return false; | |
| 95 | |
| 96 // TODO(mattm): use normalization as specified in RFC 5280 section 7. | |
| 97 | |
| 98 // RFC 2459 section 4.1.2.4 comparison rules: | |
| 99 // Attributes encoded with different types may be assumed to be unequal. | |
| 100 if (a_tag != b_tag) | |
| 101 return false; | |
| 102 if (a_tag == der::kPrintableString) { | |
| 103 // PrintableString values should be compared case insenstive and ignoring | |
| 104 // extraneous whitespace. | |
| 105 std::string a_normalized, b_normalized; | |
| 106 if (!NormalizePrintableString(a_value, &a_normalized) || | |
| 107 !NormalizePrintableString(b_value, &b_normalized)) | |
| 108 return false; | |
| 109 return a_normalized == b_normalized; | |
| 110 } else { | |
|
Ryan Sleevi
2015/05/21 02:20:17
don't else after a return
mattm
2015/06/17 03:36:19
Done.
| |
| 111 // Types other than PrintableString use binary comparison. | |
| 112 return a_value.Equals(b_value); | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 bool VerifyRDNMatch(der::Parser* a, der::Parser* b) { | |
| 117 // Must have at least one AttributeTypeAndValue. | |
| 118 if (!a->HasMore() || !b->HasMore()) | |
| 119 return false; | |
| 120 | |
| 121 while (a->HasMore() && b->HasMore()) { | |
| 122 der::Parser a_attr_type_and_value; | |
| 123 der::Parser b_attr_type_and_value; | |
| 124 if (!a->ReadSequence(&a_attr_type_and_value) || | |
| 125 !b->ReadSequence(&b_attr_type_and_value)) | |
| 126 return false; | |
| 127 if (!VerifyAttributeValueMatch(&a_attr_type_and_value, | |
| 128 &b_attr_type_and_value)) | |
| 129 return false; | |
| 130 } | |
| 131 | |
| 132 // If one of the RDNs has more elements than the other, not a match. | |
| 133 if (a->HasMore() || b->HasMore()) | |
| 134 return false; | |
| 135 | |
| 136 return true; | |
| 137 } | |
| 138 | |
| 139 } // namespace | |
| 140 | |
| 141 // TODO(mattm): is returning false on parsing errors ok, or should it try to | |
| 142 // fall back to binary comparison on unexpected input? | |
| 10 bool VerifyNameMatch(const der::Input& a, const der::Input& b) { | 143 bool VerifyNameMatch(const der::Input& a, const der::Input& b) { |
| 11 // TODO(mattm): use normalization as specified in RFC 5280 section 7. | 144 der::Parser a_parser(a); |
| 12 return a.Equals(b); | 145 der::Parser b_parser(b); |
| 146 der::Parser a_rdn_sequence; | |
| 147 der::Parser b_rdn_sequence; | |
| 148 | |
| 149 if (!a_parser.ReadSequence(&a_rdn_sequence) || | |
| 150 !b_parser.ReadSequence(&b_rdn_sequence)) { | |
| 151 return false; | |
| 152 } | |
| 153 | |
| 154 // No data should remain in the inputs after the RDN sequence. | |
| 155 if (a_parser.HasMore() || b_parser.HasMore()) | |
| 156 return false; | |
| 157 | |
| 158 // Must have at least one RDN. | |
| 159 if (!a_rdn_sequence.HasMore() || !b_rdn_sequence.HasMore()) | |
| 160 return false; | |
| 161 | |
| 162 while (a_rdn_sequence.HasMore() && b_rdn_sequence.HasMore()) { | |
| 163 der::Parser a_rdn, b_rdn; | |
| 164 if (!a_rdn_sequence.ReadConstructed(der::kSet, &a_rdn) || | |
| 165 !b_rdn_sequence.ReadConstructed(der::kSet, &b_rdn)) { | |
| 166 return false; | |
| 167 } | |
| 168 if (!VerifyRDNMatch(&a_rdn, &b_rdn)) | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 // If one of the sequences has more elements than the other, not a match. | |
| 173 if (a_rdn_sequence.HasMore() || b_rdn_sequence.HasMore()) | |
| 174 return false; | |
| 175 | |
| 176 return true; | |
| 13 } | 177 } |
| 14 | 178 |
| 15 } // namespace net | 179 } // namespace net |
| OLD | NEW |