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

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, 5 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 <map>
8 #include <string>
9 #include <vector>
10
11 #include "base/base64.h"
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_tokenizer.h"
15 #include "base/strings/string_util.h"
16 #include "net/http/http_util.h"
17
18 namespace gcm {
19
20 namespace {
21
22 const char kKeyIdName[] = "keyid";
23 const char kSaltName[] = "salt";
24 const char kRecordSizeName[] = "rs";
25 const char kKeyName[] = "key";
26 const char kDiffieHellmanName[] = "dh";
27
28 using NameValueMap = std::map<std::string, std::string>;
29 using NameValueMapVector = std::vector<NameValueMap>;
30
31 // Parses |input| as a header value containing multiple name-value-lists.
32 bool ParseMultipleNameValueListsHeader(const std::string& input,
33 NameValueMapVector* output) {
34 base::StringTokenizer tokenizer(input, ",");
35 tokenizer.set_quote_chars("'\"");
36
37 while (tokenizer.GetNext()) {
38 NameValueMap name_value_map;
39
40 net::HttpUtil::NameValuePairsIterator parser(tokenizer.token_begin(),
johnme 2015/07/21 14:34:43 I notice that NameValuePairsIterator allows arbitr
Peter Beverloo 2015/07/21 17:51:30 I'd like to defer to Ryan for final judgement here
41 tokenizer.token_end(), ';');
42 if (!parser.valid())
johnme 2015/07/21 14:34:43 It's not actually possible for valid to be false b
Peter Beverloo 2015/07/21 17:51:30 Done.
43 return false;
johnme 2015/07/21 14:34:43 It might also make sense to call output->clear() b
Peter Beverloo 2015/07/21 17:51:30 Done.
44
45 while (parser.GetNext())
46 name_value_map[parser.name()] = parser.value();
47
48 output->push_back(name_value_map);
49 }
50
51 return true;
52 }
53
54 // TODO(peter): Generalize a URL-safe base64 implementation.
johnme 2015/07/21 14:34:43 Please link to https://tools.ietf.org/html/rfc4648
Peter Beverloo 2015/07/21 17:51:30 Done.
55 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
56 if (input.find_first_of("+/=") != std::string::npos)
johnme 2015/07/21 14:34:43 Why do you disallow '='? Whilst padding is technic
Peter Beverloo 2015/07/21 17:51:30 Done.
57 return false;
58
59 // Add padding.
60 size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4;
61 std::string padded_input(input);
62 padded_input.resize(padded_size, '=');
63
64 // Convert to standard base64 alphabet.
65 base::ReplaceChars(padded_input, "-", "+", &padded_input);
66 base::ReplaceChars(padded_input, "_", "/", &padded_input);
67
68 return base::Base64Decode(padded_input, output);
69 }
70
71 // Parses the "salt" field of the Encryption header. Must be a base64 URL
johnme 2015/07/21 14:34:43 Nit: "base64url" for consistency with the naming i
Peter Beverloo 2015/07/21 17:51:30 Done.
72 // encoded string that decodes to a string exactly 16 bytes in length.
73 bool ParseSalt(const std::string& value, std::string* output) {
74 std::string decoded_value;
75 if (!Base64DecodeUrlSafe(value, &decoded_value))
76 return false;
77
78 if (decoded_value.size() != 16)
79 return false;
80
81 output->swap(decoded_value);
82 return true;
83 }
84
85 // Parses the "rs" field of the Encryption header. When present, the value must
86 // be a positive decimal integer greater than 1.
87 bool ParseRecordSize(const std::string& value, int64_t* output) {
88 int64_t decimal_value = 0;
89 if (!base::StringToInt64(value, &decimal_value))
90 return false;
91
92 if (decimal_value <= 1)
93 return false;
94
95 *output = decimal_value;
96 return true;
97 }
98
99 // Parses the "key" field of the Encryption-Key header. When present, the value
100 // must be a base64 URL encoded string that decodes to a string at least 16
101 // bytes in length.
102 bool ParseKey(const std::string& value, std::string* output) {
103 std::string decoded_value;
104 if (!Base64DecodeUrlSafe(value, &decoded_value))
105 return false;
106
107 if (decoded_value.size() < 16)
108 return false;
109
110 output->swap(decoded_value);
111 return true;
112 }
113
114 } // namespace
115
116 bool ParseEncryptionHeader(const std::string& input,
117 std::vector<EncryptionHeaderValue>* result) {
118 DCHECK(result);
119
120 NameValueMapVector parsed_input;
121 if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
122 return false;
123
124 for (const auto& value_map : parsed_input) {
125 EncryptionHeaderValue value;
126
127 // Optional field: "keyid". May contain any string.
128 const auto& keyid_iter = value_map.find(kKeyIdName);
129 if (keyid_iter != value_map.end())
130 value.keyid = keyid_iter->second;
131
132 // Required field: "salt". Must contain a base64 URL-encoded string that
133 // decodes to a string that is exactly 16-bytes in length.
134 const auto& salt_iter = value_map.find(kSaltName);
135 if (salt_iter == value_map.end())
136 return false;
johnme 2015/07/21 14:34:43 If value_map is completely empty, it should be ok
Peter Beverloo 2015/07/21 17:51:30 Done.
137
138 if (!ParseSalt(salt_iter->second, &value.salt))
139 return false;
140
141 // Optional field: "rs". Must contain a positive decimal integer greater
142 // than one when supplied.
143 const auto& record_size_iter = value_map.find(kRecordSizeName);
144 if (record_size_iter != value_map.end()) {
145 if (!ParseRecordSize(record_size_iter->second, &value.rs))
146 return false;
147 }
148
149 result->push_back(value);
150 }
151
152 return true;
153 }
154
155 bool ParseEncryptionKeyHeader(const std::string& input,
156 std::vector<EncryptionKeyHeaderValue>* result) {
157 DCHECK(result);
158
159 NameValueMapVector parsed_input;
160 if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
161 return false;
162
163 for (const auto& value_map : parsed_input) {
164 EncryptionKeyHeaderValue value;
165
166 // Optional field: "keyid". May contain any string.
167 const auto& keyid_iter = value_map.find(kKeyIdName);
168 if (keyid_iter != value_map.end())
169 value.keyid = keyid_iter->second;
170
171 // Optional field: "key". Must contain a base64 URL-encoded string with the
172 // explicit encryption key.
173 const auto& key_iter = value_map.find(kKeyName);
174 if (key_iter != value_map.end()) {
175 if (!ParseKey(key_iter->second, &value.key))
176 return false;
177 }
178
179 // Optional field: "dh". Must contain a base64 URL-encoded string with the
180 // Diffie-Hellman share (either modp or elliptic curve).
181 const auto& dh_iter = value_map.find(kDiffieHellmanName);
182 if (dh_iter != value_map.end()) {
183 if (!Base64DecodeUrlSafe(dh_iter->second, &value.dh))
184 return false;
185 }
186
187 result->push_back(value);
188 }
189
190 return true;
191 }
192
193 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698