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 peaker(*reader); | |
nharper
2015/05/14 19:21:44
nit: peeker
mattm
2015/05/14 21:38:56
doh.
| |
19 while (true) { | |
20 uint8_t c; | |
21 if (!peaker.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 == '?') { | |
nharper
2015/05/14 19:21:44
Add comments indicating what the ranges '\'' thru
mattm
2015/05/14 21:38:57
Done.
| |
56 *output += c; | |
57 } else { | |
58 // Fail on any characters that are not valid for PrintableString. | |
59 // TODO(mattm): will we need to include '*'? | |
60 return false; | |
61 } | |
62 } | |
63 | |
64 return true; | |
65 } | |
66 | |
67 bool VerifyAttributeValueMatch(der::Parser* a, der::Parser* b) { | |
68 der::Tag a_tag, b_tag; | |
69 der::Input a_value, b_value; | |
70 | |
71 // Read the attribute type. | |
72 if (!a->ReadTagAndValue(&a_tag, &a_value)) | |
73 return false; | |
74 if (!b->ReadTagAndValue(&b_tag, &b_value)) | |
75 return false; | |
76 // Type of "Attribute type" must be OBJECT IDENTIFIER. | |
77 if (a_tag != der::kOid || b_tag != der::kOid) | |
nharper
2015/05/14 19:21:44
Since you want to match specific tags, why not do
mattm
2015/05/14 21:38:56
Done.
| |
78 return false; | |
79 // Attribute types must be equal. | |
80 if (!a_value.Equals(b_value)) | |
81 return false; | |
82 | |
83 // Read the attribute value. | |
84 if (!a->ReadTagAndValue(&a_tag, &a_value)) | |
85 return false; | |
86 if (!b->ReadTagAndValue(&b_tag, &b_value)) | |
87 return false; | |
nharper
2015/05/14 19:21:44
After reading the attribute type and value, you sh
mattm
2015/05/14 21:38:56
Done.
| |
88 | |
89 // TODO(mattm): use normalization as specified in RFC 5280 section 7. | |
90 | |
91 // RFC 2459 section 4.1.2.4 comparison rules: | |
92 // Attributes encoded with different types may be assumed to be unequal. | |
93 if (a_tag != b_tag) | |
94 return false; | |
95 if (a_tag == der::kPrintableString) { | |
96 // PrintableString values should be compared case insenstive and ignoring | |
97 // extraneous whitespace. | |
98 std::string a_normalized, b_normalized; | |
99 if (!NormalizePrintableString(a_value, &a_normalized) || | |
100 !NormalizePrintableString(b_value, &b_normalized)) | |
101 return false; | |
102 return a_normalized == b_normalized; | |
103 } else { | |
104 // Types other than PrintableString use binary comparison. | |
105 return a_value.Equals(b_value); | |
106 } | |
107 } | |
108 | |
109 bool VerifyRDNMatch(der::Parser* a, der::Parser* b) { | |
110 while (a->HasMore() && b->HasMore()) { | |
111 der::Parser a_attr_type_and_value; | |
112 der::Parser b_attr_type_and_value; | |
113 if (!a->ReadSequence(&a_attr_type_and_value) || | |
114 !b->ReadSequence(&b_attr_type_and_value)) | |
115 return false; | |
116 if (!VerifyAttributeValueMatch(&a_attr_type_and_value, | |
117 &b_attr_type_and_value)) | |
118 return false; | |
119 } | |
120 | |
121 // If one of the RDNs has more elements than the other, not a match. | |
122 if (a->HasMore() || b->HasMore()) | |
123 return false; | |
124 | |
125 return true; | |
126 } | |
127 | |
128 } // namespace | |
129 | |
130 // TODO(mattm): is returning false on parsing errors ok, or should it try to | |
131 // fall back to binary comparison on unexpected input? | |
10 bool VerifyNameMatch(const der::Input& a, const der::Input& b) { | 132 bool VerifyNameMatch(const der::Input& a, const der::Input& b) { |
11 // TODO(mattm): use normalization as specified in RFC 5280 section 7. | 133 der::Parser a_parser(a); |
12 return a.Equals(b); | 134 der::Parser b_parser(b); |
135 der::Parser a_rdn_sequence; | |
136 der::Parser b_rdn_sequence; | |
137 | |
138 if (!a_parser.ReadSequence(&a_rdn_sequence) || | |
139 !b_parser.ReadSequence(&b_rdn_sequence)) { | |
140 return false; | |
141 } | |
nharper
2015/05/14 19:21:44
Add a check that there's nothing in the inputs aft
mattm
2015/05/14 21:38:56
Done.
| |
142 | |
143 while (a_rdn_sequence.HasMore() && b_rdn_sequence.HasMore()) { | |
144 der::Parser a_rdn, b_rdn; | |
145 if (!a_rdn_sequence.ReadConstructed(der::kSet, &a_rdn) || | |
146 !b_rdn_sequence.ReadConstructed(der::kSet, &b_rdn)) { | |
147 return false; | |
148 } | |
149 if (!VerifyRDNMatch(&a_rdn, &b_rdn)) | |
150 return false; | |
151 } | |
152 | |
153 // If one of the sequences has more elements than the other, not a match. | |
154 if (a_rdn_sequence.HasMore() || b_rdn_sequence.HasMore()) | |
155 return false; | |
156 | |
157 return true; | |
13 } | 158 } |
14 | 159 |
15 } // namespace net | 160 } // namespace net |
OLD | NEW |