Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: components/gcm_driver/crypto/encryption_header_parsers.cc

Issue 1244803002: Add parsers for the Encryption and Encryption-Key HTTP headers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "components/gcm_driver/crypto/encryption_header_parsers.h"
6
7 #include "base/base64.h"
8 #include "base/numerics/safe_math.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_util.h"
12 #include "net/http/http_util.h"
13
14 namespace gcm {
15
16 namespace {
17
18 // The default record size in bytes, as defined in section two of
19 // https://tools.ietf.org/html/draft-thomson-http-encryption-01.
20 const uint64_t kDefaultRecordSizeBytes = 4096;
21
22 // TODO(peter): Unify the base64url implementations. https://crbug.com/536745
23 bool Base64URLDecode(const base::StringPiece& input, std::string* output) {
24 // Bail on malformed strings, which already contain a '+' or a '/'. All valid
25 // strings should escape these special characters as '-' and '_',
26 // respectively.
27 if (input.find_first_of("+/") != std::string::npos)
28 return false;
29
30 base::CheckedNumeric<size_t> checked_padded_size = input.size();
31 if (input.size() % 4)
32 checked_padded_size += 4 - (input.size() % 4);
33
34 // Add padding to |input|.
35 std::string padded_input(input.begin(), input.end());
36 padded_input.resize(checked_padded_size.ValueOrDie(), '=');
37
38 // Convert to standard base64 alphabet.
39 base::ReplaceChars(padded_input, "-", "+", &padded_input);
40 base::ReplaceChars(padded_input, "_", "/", &padded_input);
41
42 return base::Base64Decode(padded_input, output);
43 }
44
45 // Decodes the string between |begin| and |end| using base64url, and writes the
46 // decoded value to |*salt|. Returns whether the string could be decoded.
47 bool ValueToDecodedString(const std::string::const_iterator& begin,
48 const std::string::const_iterator& end,
49 std::string* salt) {
50 const base::StringPiece value(begin, end);
51 if (value.empty())
52 return false;
53
54 return Base64URLDecode(value, salt);
55 }
56
57 // Parses the record size between |begin| and |end|, and writes the value to
58 // |*rs|. The value must be an unsigned, 64-bit integer greater than zero that
59 // does not start with a plus. Returns whether the record size was valid.
60 bool RecordSizeToInt(const std::string::const_iterator& begin,
61 const std::string::const_iterator& end,
62 uint64_t* rs) {
63 const base::StringPiece value(begin, end);
64 if (value.empty())
65 return false;
66
67 // Parsing the "rs" parameter uses stricter semantics than parsing rules for
68 // normal integers, in that we want to reject values such as "+5" for
69 // compatibility with UAs that use other number parsing mechanisms.
70 if (value[0] == '+')
71 return false;
72
73 if (!base::StringToUint64(value, rs))
74 return false;
75
76 // The record size MUST be greater than 1.
77 return *rs > 1;
78 }
79
80 // Parses the string between |input_begin| and |input_end| according to the
81 // extended ABNF syntax for the Encryption HTTP header, per the "parameter"
82 // rule from RFC 7231 (https://tools.ietf.org/html/rfc7231).
83 //
84 // encryption_params = [ parameter *( ";" parameter ) ]
85 //
86 // This implementation applies the parameters defined in section 3.1 of the
87 // HTTP encryption encoding document:
88 //
89 // https://tools.ietf.org/html/draft-thomson-http-encryption-01#section-3.1
90 //
91 // This means that the three supported parameters are:
92 //
93 // [ "keyid" "=" string ]
94 // [ ";" "salt" "=" base64url ]
95 // [ ";" "rs" "=" octet-count ]
96 bool ParseEncryptionHeaderValuesImpl(std::string::const_iterator input_begin,
97 std::string::const_iterator input_end,
98 EncryptionHeaderValues* values) {
99 net::HttpUtil::NameValuePairsIterator name_value_pairs(
100 input_begin, input_end, ';',
101 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL);
102
103 while (name_value_pairs.GetNext()) {
104 const base::StringPiece name(name_value_pairs.name_begin(),
105 name_value_pairs.name_end());
106
107 if (base::LowerCaseEqualsASCII(name, "keyid")) {
108 values->keyid.assign(name_value_pairs.value_begin(),
109 name_value_pairs.value_end());
110 } else if (base::LowerCaseEqualsASCII(name, "salt")) {
111 if (!ValueToDecodedString(name_value_pairs.value_begin(),
112 name_value_pairs.value_end(), &values->salt)) {
113 return false;
114 }
115 } else if (base::LowerCaseEqualsASCII(name, "rs")) {
116 if (!RecordSizeToInt(name_value_pairs.value_begin(),
117 name_value_pairs.value_end(), &values->rs)) {
118 return false;
119 }
120 } else {
121 // Silently ignore unknown directives for forward compatibility.
122 }
123 }
124
125 return name_value_pairs.valid();
126 }
127
128 // Parses the string between |input_begin| and |input_end| according to the
129 // extended ABNF syntax for the Encryption-Key HTTP header, per the "parameter"
130 // rule from RFC 7231 (https://tools.ietf.org/html/rfc7231).
131 //
132 // encryption_params = [ parameter *( ";" parameter ) ]
133 //
134 // This implementation applies the parameters defined in section 4 of the
135 // HTTP encryption encoding document:
136 //
137 //https://tools.ietf.org/html/draft-thomson-http-encryption-01#section-4
138 //
139 // This means that the three supported parameters are:
140 //
141 // [ "keyid" "=" string ]
142 // [ ";" "key" "=" base64url ]
143 // [ ";" "dh" "=" base64url ]
144 bool ParseEncryptionKeyHeaderValuesImpl(std::string::const_iterator input_begin,
145 std::string::const_iterator input_end,
146 EncryptionKeyHeaderValues* values) {
147 net::HttpUtil::NameValuePairsIterator name_value_pairs(
148 input_begin, input_end, ';',
149 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL);
150
151 while (name_value_pairs.GetNext()) {
152 const base::StringPiece name(name_value_pairs.name_begin(),
153 name_value_pairs.name_end());
154
155 if (base::LowerCaseEqualsASCII(name, "keyid")) {
156 values->keyid.assign(name_value_pairs.value_begin(),
157 name_value_pairs.value_end());
158 } else if (base::LowerCaseEqualsASCII(name, "key")) {
159 if (!ValueToDecodedString(name_value_pairs.value_begin(),
160 name_value_pairs.value_end(), &values->key)) {
161 return false;
162 }
163 } else if (base::LowerCaseEqualsASCII(name, "dh")) {
164 if (!ValueToDecodedString(name_value_pairs.value_begin(),
165 name_value_pairs.value_end(), &values->dh)) {
166 return false;
167 }
168 } else {
169 // Silently ignore unknown directives for forward compatibility.
170 }
171 }
172
173 return name_value_pairs.valid();
174 }
175
176 } // namespace
177
178 bool ParseEncryptionHeader(const std::string& input,
179 std::vector<EncryptionHeaderValues>* values) {
180 DCHECK(values);
181
182 std::vector<EncryptionHeaderValues> candidate_values;
183
184 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ',');
185 while (value_iterator.GetNext()) {
186 EncryptionHeaderValues candidate_value;
187 candidate_value.rs = kDefaultRecordSizeBytes;
188
189 if (!ParseEncryptionHeaderValuesImpl(value_iterator.value_begin(),
190 value_iterator.value_end(),
191 &candidate_value)) {
192 return false;
193 }
194
195 candidate_values.push_back(candidate_value);
196 }
197
198 values->swap(candidate_values);
199 return true;
200 }
201
202 bool ParseEncryptionKeyHeader(const std::string& input,
203 std::vector<EncryptionKeyHeaderValues>* values) {
204 DCHECK(values);
205
206 std::vector<EncryptionKeyHeaderValues> candidate_values;
207
208 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ',');
209 while (value_iterator.GetNext()) {
210 EncryptionKeyHeaderValues candidate_value;
211 if (!ParseEncryptionKeyHeaderValuesImpl(value_iterator.value_begin(),
212 value_iterator.value_end(),
213 &candidate_value)) {
214 return false;
215 }
216
217 candidate_values.push_back(candidate_value);
218 }
219
220 values->swap(candidate_values);
221 return true;
222 }
223
224 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698