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

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>;
Ryan Sleevi 2015/08/03 18:59:24 This seems like a lot of unnecessary copying
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("'\"");
Ryan Sleevi 2015/08/03 18:59:24 Seems like this is hardcoding what properly belong
36
37 while (tokenizer.GetNext()) {
38 NameValueMap name_value_map;
39
40 net::HttpUtil::NameValuePairsIterator parser(tokenizer.token_begin(),
41 tokenizer.token_end(), ';');
42 while (parser.GetNext()) {
43 if (!parser.valid()) {
44 output->clear();
45 return false;
46 }
47
48 name_value_map[base::StringToLowerASCII(parser.name())] =
49 parser.value();
50 }
51
52 if (name_value_map.size())
53 output->push_back(name_value_map);
54 }
55
56 return true;
57 }
58
59 // TODO(peter): Generalize a base64url implementation.
60 // See https://tools.ietf.org/html/rfc4648#section-5
61 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
62 if (input.find_first_of("+/") != std::string::npos)
63 return false;
64
65 // Add padding.
66 size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4;
67 std::string padded_input(input);
68 padded_input.resize(padded_size, '=');
69
70 // Convert to standard base64 alphabet.
71 base::ReplaceChars(padded_input, "-", "+", &padded_input);
72 base::ReplaceChars(padded_input, "_", "/", &padded_input);
73
74 return base::Base64Decode(padded_input, output);
75 }
76
77 // Parses the "salt" field of the Encryption header. Must be a base64url
78 // encoded string that decodes to a string exactly 16 bytes in length.
79 bool ParseSalt(const std::string& value, std::string* output) {
80 std::string decoded_value;
81 if (!Base64DecodeUrlSafe(value, &decoded_value))
82 return false;
83
84 if (decoded_value.size() != 16)
85 return false;
86
87 output->swap(decoded_value);
88 return true;
89 }
90
91 // Parses the "rs" field of the Encryption header. When present, the value must
92 // be a positive decimal integer greater than 1.
93 bool ParseRecordSize(const std::string& value, int64_t* output) {
94 int64_t decimal_value = 0;
95 if (!base::StringToInt64(value, &decimal_value))
96 return false;
97
98 if (decimal_value <= 1)
99 return false;
100
101 *output = decimal_value;
102 return true;
103 }
104
105 // Parses the "key" field of the Encryption-Key header. When present, the value
106 // must be a base64url encoded string that decodes to a string at least 16
107 // bytes in length.
108 bool ParseKey(const std::string& value, std::string* output) {
109 std::string decoded_value;
110 if (!Base64DecodeUrlSafe(value, &decoded_value))
111 return false;
112
113 if (decoded_value.size() < 16)
114 return false;
115
116 output->swap(decoded_value);
117 return true;
118 }
119
120 } // namespace
121
122 bool ParseEncryptionHeader(const std::string& input,
123 std::vector<EncryptionHeaderValue>* result) {
Ryan Sleevi 2015/08/03 18:59:24 This all seems very inefficient
124 DCHECK(result);
125
126 NameValueMapVector parsed_input;
127 if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
128 return false;
129
130 for (const auto& value_map : parsed_input) {
131 EncryptionHeaderValue value;
132
133 // Optional field: "keyid". May contain any string.
134 const auto& keyid_iter = value_map.find(kKeyIdName);
135 if (keyid_iter != value_map.end())
136 value.keyid = keyid_iter->second;
137
138 // Required field: "salt". Must contain a base64url-encoded string that
139 // decodes to a string that is exactly 16-bytes in length.
140 const auto& salt_iter = value_map.find(kSaltName);
141 if (salt_iter == value_map.end())
142 return false;
143
144 if (!ParseSalt(salt_iter->second, &value.salt))
145 return false;
146
147 // Optional field: "rs". Must contain a positive decimal integer greater
148 // than one when supplied.
149 const auto& record_size_iter = value_map.find(kRecordSizeName);
150 if (record_size_iter != value_map.end()) {
151 if (!ParseRecordSize(record_size_iter->second, &value.rs))
152 return false;
153 }
154
155 result->push_back(value);
156 }
157
158 return true;
159 }
160
161 bool ParseEncryptionKeyHeader(const std::string& input,
162 std::vector<EncryptionKeyHeaderValue>* result) {
163 DCHECK(result);
164
165 NameValueMapVector parsed_input;
166 if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
167 return false;
168
169 for (const auto& value_map : parsed_input) {
170 EncryptionKeyHeaderValue value;
171
172 // Optional field: "keyid". May contain any string.
173 const auto& keyid_iter = value_map.find(kKeyIdName);
174 if (keyid_iter != value_map.end())
175 value.keyid = keyid_iter->second;
176
177 // Optional field: "key". Must contain a base64url-encoded string with the
178 // explicit encryption key.
179 const auto& key_iter = value_map.find(kKeyName);
180 if (key_iter != value_map.end()) {
181 if (!ParseKey(key_iter->second, &value.key))
182 return false;
183 }
184
185 // Optional field: "dh". Must contain a base64url-encoded string with the
186 // Diffie-Hellman share (either modp or elliptic curve).
187 const auto& dh_iter = value_map.find(kDiffieHellmanName);
188 if (dh_iter != value_map.end()) {
189 if (!Base64DecodeUrlSafe(dh_iter->second, &value.dh))
190 return false;
191 }
192
193 result->push_back(value);
194 }
195
196 return true;
197 }
198
199 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698