OLD | NEW |
---|---|
(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/strings/string_number_conversions.h" | |
9 #include "base/strings/string_piece.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "net/http/http_util.h" | |
12 | |
13 namespace gcm { | |
14 | |
15 namespace { | |
16 | |
17 // The default record size in bytes, as defined in section two of | |
18 // https://tools.ietf.org/html/draft-thomson-http-encryption-01. | |
19 const uint64_t kDefaultRecordSizeBytes = 4096; | |
20 | |
21 // TODO(peter): Unify the base64url implementations. https://crbug.com/536745 | |
22 bool Base64URLDecode(const base::StringPiece& input, std::string* output) { | |
23 // Bail on malformed strings, which already contain a '+' or a '/'. All valid | |
24 // strings should escape these special characters as '-' and '_', | |
25 // respectively. | |
26 if (input.find_first_of("+/") != std::string::npos) | |
27 return false; | |
28 | |
29 size_t padded_size = | |
30 input.size() % 4 ? input.size() + 4 - (input.size() % 4) : input.size(); | |
Ryan Sleevi
2015/09/28 19:35:48
Erm, can't this overflow in some (extremely pedant
Peter Beverloo
2015/10/01 14:56:20
Yes.
However, the only scenario in which *this* w
| |
31 | |
32 // Add padding to |input|. | |
33 std::string padded_input(input.begin(), input.end()); | |
34 padded_input.resize(padded_size, '='); | |
Ryan Sleevi
2015/09/28 19:35:48
Blergh; having to copy this string makes it all th
Peter Beverloo
2015/10/01 14:56:20
Acknowledged.
| |
35 | |
36 // Convert to standard base64 alphabet. | |
37 base::ReplaceChars(padded_input, "-", "+", &padded_input); | |
38 base::ReplaceChars(padded_input, "_", "/", &padded_input); | |
39 | |
40 return base::Base64Decode(padded_input, output); | |
41 } | |
42 | |
43 bool ValueToDecodedString(const std::string::const_iterator& begin, | |
44 const std::string::const_iterator& end, | |
45 std::string* salt) { | |
46 const base::StringPiece value(begin, end); | |
47 if (value.empty()) | |
48 return false; | |
49 | |
50 return Base64URLDecode(value, salt); | |
51 } | |
52 | |
53 bool RecordSizeToInt(const std::string::const_iterator& begin, | |
54 const std::string::const_iterator& end, | |
55 uint64_t* rs) { | |
56 const base::StringPiece value(begin, end); | |
57 if (value.empty()) | |
58 return false; | |
59 | |
60 if (!base::StringToUint64(value, rs)) | |
61 return false; | |
62 | |
63 // The record size MUST be greater than 1. | |
64 return *rs > 1; | |
65 } | |
66 | |
67 bool ParseEncryptionHeaderImpl(const std::string& input, | |
68 std::string* keyid, | |
69 std::string* salt, | |
70 uint64_t* rs) { | |
71 net::HttpUtil::NameValuePairsIterator name_value_pairs( | |
72 input.begin(), input.end(), ';', | |
73 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | |
74 | |
75 while (name_value_pairs.GetNext()) { | |
76 const base::StringPiece name(name_value_pairs.name_begin(), | |
77 name_value_pairs.name_end()); | |
78 | |
79 if (base::LowerCaseEqualsASCII(name, "keyid")) { | |
80 keyid->assign(name_value_pairs.value_begin(), | |
81 name_value_pairs.value_end()); | |
82 } else if (base::LowerCaseEqualsASCII(name, "salt")) { | |
83 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
84 name_value_pairs.value_end(), salt)) { | |
85 return false; | |
86 } | |
87 } else if (base::LowerCaseEqualsASCII(name, "rs")) { | |
88 if (!RecordSizeToInt(name_value_pairs.value_begin(), | |
89 name_value_pairs.value_end(), rs)) { | |
90 return false; | |
91 } | |
92 } else { | |
93 // Silently ignore unknown directives for forward compatibility. | |
94 } | |
95 } | |
96 | |
97 return name_value_pairs.valid(); | |
98 } | |
99 | |
100 bool ParseEncryptionKeyHeaderImpl(const std::string& input, | |
101 std::string* keyid, | |
102 std::string* key, | |
103 std::string* dh) { | |
104 net::HttpUtil::NameValuePairsIterator name_value_pairs( | |
105 input.begin(), input.end(), ';', | |
106 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | |
107 | |
108 while (name_value_pairs.GetNext()) { | |
109 const base::StringPiece name(name_value_pairs.name_begin(), | |
110 name_value_pairs.name_end()); | |
111 | |
112 if (base::LowerCaseEqualsASCII(name, "keyid")) { | |
113 keyid->assign(name_value_pairs.value_begin(), | |
114 name_value_pairs.value_end()); | |
115 } else if (base::LowerCaseEqualsASCII(name, "key")) { | |
116 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
117 name_value_pairs.value_end(), key)) { | |
118 return false; | |
119 } | |
120 } else if (base::LowerCaseEqualsASCII(name, "dh")) { | |
121 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
122 name_value_pairs.value_end(), dh)) { | |
123 return false; | |
124 } | |
125 } else { | |
126 // Silently ignore unknown directives for forward compatibility. | |
127 } | |
128 } | |
129 | |
130 return name_value_pairs.valid(); | |
131 } | |
132 | |
133 } // namespace | |
134 | |
135 // "Encryption" ":" | |
136 // [ "keyid" "=" string ] | |
137 // [ ";" "salt" "=" base64url ] | |
138 // [ ";" "rs" "=" octet-count ] | |
Ryan Sleevi
2015/09/28 19:35:48
Comment gripe: So, this isn't what the spec says,
Peter Beverloo
2015/10/01 14:56:20
Done.
| |
139 bool ParseEncryptionHeader(const std::string& input, | |
140 std::string* keyid, | |
141 std::string* salt, | |
142 uint64_t* rs) { | |
143 std::string candidate_keyid; | |
144 std::string candidate_salt; | |
145 uint64_t candidate_rs = kDefaultRecordSizeBytes; | |
146 | |
147 if (!ParseEncryptionHeaderImpl(input, &candidate_keyid, &candidate_salt, | |
148 &candidate_rs)) { | |
149 return false; | |
150 } | |
151 | |
152 keyid->swap(candidate_keyid); | |
153 salt->swap(candidate_salt); | |
154 *rs = candidate_rs; | |
155 return true; | |
156 } | |
157 | |
158 // "Encryption-Key" ":" | |
159 // [ "keyid" "=" string ] | |
160 // [ ";" "key" "=" base64url ] | |
161 // [ ";" "dh" "=" base64url ] | |
162 bool ParseEncryptionKeyHeader(const std::string& input, | |
163 std::string* keyid, | |
164 std::string* key, | |
165 std::string* dh) { | |
166 std::string candidate_keyid; | |
167 std::string candidate_key; | |
168 std::string candidate_dh; | |
169 | |
170 if (!ParseEncryptionKeyHeaderImpl(input, &candidate_keyid, &candidate_key, | |
171 &candidate_dh)) { | |
172 return false; | |
173 } | |
174 | |
175 keyid->swap(candidate_keyid); | |
176 key->swap(candidate_key); | |
177 dh->swap(candidate_dh); | |
178 return true; | |
179 } | |
180 | |
181 } // namespace gcm | |
OLD | NEW |