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

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

Issue 1509683002: Convert the encryption header parsers to be iterator-based. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years 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
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 "components/gcm_driver/crypto/encryption_header_parsers.h" 5 #include "components/gcm_driver/crypto/encryption_header_parsers.h"
6 6
7 #include "base/base64url.h" 7 #include "base/base64url.h"
8 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_piece.h" 9
10 #include "base/strings/string_util.h" 10 #include "base/strings/string_util.h"
11 #include "net/http/http_util.h" 11
12 12
13 namespace gcm { 13 namespace gcm {
14 14
15 namespace { 15 namespace {
16 16
17 // The default record size in bytes, as defined in section two of 17 // The default record size in bytes, as defined in section two of
18 // https://tools.ietf.org/html/draft-thomson-http-encryption-02. 18 // https://tools.ietf.org/html/draft-thomson-http-encryption.
19 const uint64_t kDefaultRecordSizeBytes = 4096; 19 const uint64_t kDefaultRecordSizeBytes = 4096;
20 20
21 // Decodes the string between |begin| and |end| using base64url, and writes the 21 // Decodes the string in |value| using base64url and writes the decoded value to
22 // decoded value to |*salt|. Returns whether the string could be decoded. 22 // |*salt|. Returns whether the string could be decoded.
23 bool ValueToDecodedString(const std::string::const_iterator& begin, 23 bool ValueToDecodedString(base::StringPiece value, std::string* salt) {
24 const std::string::const_iterator& end,
25 std::string* salt) {
26 const base::StringPiece value(begin, end);
27 if (value.empty()) 24 if (value.empty())
28 return false; 25 return false;
29 26
30 return base::Base64UrlDecode( 27 std::string candidate_salt;
31 value, base::Base64UrlDecodePolicy::IGNORE_PADDING, salt); 28 if (!base::Base64UrlDecode(value, base::Base64UrlDecodePolicy::IGNORE_PADDING,
29 &candidate_salt)) {
30 return false;
31 }
32
33 salt->swap(candidate_salt);
34 return true;
32 } 35 }
33 36
34 // Parses the record size between |begin| and |end|, and writes the value to 37 // Parses the record size in |value| and writes the value to |*rs|. The value
35 // |*rs|. The value must be an unsigned, 64-bit integer greater than zero that 38 // must be an unsigned, 64-bit integer greater than zero that does not start
36 // does not start with a plus. Returns whether the record size was valid. 39 // with a plus. Returns whether the record size was valid.
37 bool RecordSizeToInt(const std::string::const_iterator& begin, 40 bool RecordSizeToInt(base::StringPiece value, uint64_t* rs) {
38 const std::string::const_iterator& end,
39 uint64_t* rs) {
40 const base::StringPiece value(begin, end);
41 if (value.empty()) 41 if (value.empty())
42 return false; 42 return false;
43 43
44 // Parsing the "rs" parameter uses stricter semantics than parsing rules for 44 // Parsing the "rs" parameter uses stricter semantics than parsing rules for
45 // normal integers, in that we want to reject values such as "+5" for 45 // normal integers, in that we want to reject values such as "+5" for
46 // compatibility with UAs that use other number parsing mechanisms. 46 // compatibility with UAs that use other number parsing mechanisms.
Ryan Sleevi 2015/12/08 00:22:00 This comment leaves me a bit confused. Do we reje
Peter Beverloo 2015/12/08 01:02:57 Will rephrase. We're being strict here, the spec d
Peter Beverloo 2015/12/16 21:09:57 Done.
47 if (value[0] == '+') 47 if (value[0] == '+')
Ryan Sleevi 2015/12/08 00:22:00 But is '-' allowed? Or are you relying on the Uint
Peter Beverloo 2015/12/08 01:02:58 IteratorRangeToNumber::Invoke will consider the in
48 return false; 48 return false;
49 49
50 if (!base::StringToUint64(value, rs)) 50 uint64_t candidate_rs;
51 if (!base::StringToUint64(value, &candidate_rs) ||
52 candidate_rs <= 1 /* the record size MUST be greater than 1 */)
Ryan Sleevi 2015/12/08 00:22:00 Please add braces to this conditional if kept as m
Peter Beverloo 2015/12/16 21:09:57 Done.
51 return false; 53 return false;
52 54
53 // The record size MUST be greater than 1. 55 *rs = candidate_rs;
54 return *rs > 1; 56 return true;
55 } 57 }
56 58
57 // Parses the string between |input_begin| and |input_end| according to the 59 } // namespace
58 // extended ABNF syntax for the Encryption HTTP header, per the "parameter" 60
59 // rule from RFC 7231 (https://tools.ietf.org/html/rfc7231). 61 EncryptionHeaderIterator::EncryptionHeaderIterator(
60 // 62 std::string::const_iterator header_begin,
61 // encryption_params = [ parameter *( ";" parameter ) ] 63 std::string::const_iterator header_end)
62 // 64 : iterator_(header_begin, header_end, ','),
63 // This implementation applies the parameters defined in section 3.1 of the 65 rs_(kDefaultRecordSizeBytes) {}
64 // HTTP encryption encoding document: 66
65 // 67 EncryptionHeaderIterator::~EncryptionHeaderIterator() {}
66 // https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-3.1 68
67 // 69 bool EncryptionHeaderIterator::GetNext() {
68 // This means that the three supported parameters are: 70 keyid_.clear();
69 // 71 salt_.clear();
70 // [ "keyid" "=" string ] 72 rs_ = kDefaultRecordSizeBytes;
71 // [ ";" "salt" "=" base64url ] 73
72 // [ ";" "rs" "=" octet-count ] 74 if (!iterator_.GetNext())
73 bool ParseEncryptionHeaderValuesImpl(std::string::const_iterator input_begin, 75 return false;
74 std::string::const_iterator input_end, 76
75 EncryptionHeaderValues* values) {
76 net::HttpUtil::NameValuePairsIterator name_value_pairs( 77 net::HttpUtil::NameValuePairsIterator name_value_pairs(
77 input_begin, input_end, ';', 78 iterator_.value_begin(), iterator_.value_end(), ';',
78 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); 79 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL);
79 80
80 while (name_value_pairs.GetNext()) { 81 while (name_value_pairs.GetNext()) {
81 const base::StringPiece name(name_value_pairs.name_begin(), 82 const base::StringPiece name(name_value_pairs.name_begin(),
82 name_value_pairs.name_end()); 83 name_value_pairs.name_end());
84 const base::StringPiece value(name_value_pairs.value_begin(),
85 name_value_pairs.value_end());
83 86
Ryan Sleevi 2015/12/08 00:22:00 SPEC BUG: The spec is unclear whether multiple par
Peter Beverloo 2015/12/08 01:02:58 I will take an action item to generate a PR agains
Peter Beverloo 2015/12/16 21:09:57 https://github.com/martinthomson/http-encryption/p
84 if (base::LowerCaseEqualsASCII(name, "keyid")) { 87 if (base::LowerCaseEqualsASCII(name, "keyid")) {
85 values->keyid.assign(name_value_pairs.value_begin(), 88 value.CopyToString(&keyid_);
86 name_value_pairs.value_end());
87 } else if (base::LowerCaseEqualsASCII(name, "salt")) { 89 } else if (base::LowerCaseEqualsASCII(name, "salt")) {
88 if (!ValueToDecodedString(name_value_pairs.value_begin(), 90 if (!ValueToDecodedString(value, &salt_))
89 name_value_pairs.value_end(), &values->salt)) {
90 return false; 91 return false;
91 }
92 } else if (base::LowerCaseEqualsASCII(name, "rs")) { 92 } else if (base::LowerCaseEqualsASCII(name, "rs")) {
93 if (!RecordSizeToInt(name_value_pairs.value_begin(), 93 if (!RecordSizeToInt(value, &rs_))
94 name_value_pairs.value_end(), &values->rs)) {
95 return false; 94 return false;
96 }
97 } else { 95 } else {
98 // Silently ignore unknown directives for forward compatibility. 96 // Silently ignore unknown directives for forward compatibility.
99 } 97 }
100 } 98 }
101 99
102 return name_value_pairs.valid(); 100 return name_value_pairs.valid();
103 } 101 }
104 102
105 // Parses the string between |input_begin| and |input_end| according to the 103 CryptoKeyHeaderIterator::CryptoKeyHeaderIterator(
106 // extended ABNF syntax for the Crypto-Key HTTP header, per the "parameter" rule 104 std::string::const_iterator header_begin,
107 // from RFC 7231 (https://tools.ietf.org/html/rfc7231). 105 std::string::const_iterator header_end)
108 // 106 : iterator_(header_begin, header_end, ',') {}
109 // encryption_params = [ parameter *( ";" parameter ) ] 107
110 // 108 CryptoKeyHeaderIterator::~CryptoKeyHeaderIterator() {}
111 // This implementation applies the parameters defined in section 4 of the 109
112 // HTTP encryption encoding document: 110 bool CryptoKeyHeaderIterator::GetNext() {
113 // 111 keyid_.clear();
114 //https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-4 112 aesgcm128_.clear();
115 // 113 dh_.clear();
116 // This means that the three supported parameters are: 114
117 // 115 if (!iterator_.GetNext())
118 // [ "keyid" "=" string ] 116 return false;
119 // [ ";" "aesgcm128" "=" base64url ] 117
120 // [ ";" "dh" "=" base64url ]
121 bool ParseCryptoKeyHeaderValuesImpl(std::string::const_iterator input_begin,
122 std::string::const_iterator input_end,
123 CryptoKeyHeaderValues* values) {
124 net::HttpUtil::NameValuePairsIterator name_value_pairs( 118 net::HttpUtil::NameValuePairsIterator name_value_pairs(
125 input_begin, input_end, ';', 119 iterator_.value_begin(), iterator_.value_end(), ';',
126 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); 120 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL);
127 121
128 while (name_value_pairs.GetNext()) { 122 while (name_value_pairs.GetNext()) {
129 const base::StringPiece name(name_value_pairs.name_begin(), 123 const base::StringPiece name(name_value_pairs.name_begin(),
130 name_value_pairs.name_end()); 124 name_value_pairs.name_end());
125 const base::StringPiece value(name_value_pairs.value_begin(),
126 name_value_pairs.value_end());
131 127
132 if (base::LowerCaseEqualsASCII(name, "keyid")) { 128 if (base::LowerCaseEqualsASCII(name, "keyid")) {
133 values->keyid.assign(name_value_pairs.value_begin(), 129 value.CopyToString(&keyid_);
134 name_value_pairs.value_end());
135 } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) { 130 } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) {
136 if (!ValueToDecodedString(name_value_pairs.value_begin(), 131 if (!ValueToDecodedString(value, &aesgcm128_))
137 name_value_pairs.value_end(),
138 &values->aesgcm128)) {
139 return false; 132 return false;
140 }
141 } else if (base::LowerCaseEqualsASCII(name, "dh")) { 133 } else if (base::LowerCaseEqualsASCII(name, "dh")) {
142 if (!ValueToDecodedString(name_value_pairs.value_begin(), 134 if (!ValueToDecodedString(value, &dh_))
143 name_value_pairs.value_end(), &values->dh)) {
144 return false; 135 return false;
145 }
146 } else { 136 } else {
147 // Silently ignore unknown directives for forward compatibility. 137 // Silently ignore unknown directives for forward compatibility.
148 } 138 }
149 } 139 }
150 140
151 return name_value_pairs.valid(); 141 return name_value_pairs.valid();
152 } 142 }
153 143
154 } // namespace
155
156 bool ParseEncryptionHeader(const std::string& input,
157 std::vector<EncryptionHeaderValues>* values) {
158 DCHECK(values);
159
160 std::vector<EncryptionHeaderValues> candidate_values;
161
162 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ',');
163 while (value_iterator.GetNext()) {
164 EncryptionHeaderValues candidate_value;
165 candidate_value.rs = kDefaultRecordSizeBytes;
166
167 if (!ParseEncryptionHeaderValuesImpl(value_iterator.value_begin(),
168 value_iterator.value_end(),
169 &candidate_value)) {
170 return false;
171 }
172
173 candidate_values.push_back(candidate_value);
174 }
175
176 values->swap(candidate_values);
177 return true;
178 }
179
180 bool ParseCryptoKeyHeader(const std::string& input,
181 std::vector<CryptoKeyHeaderValues>* values) {
182 DCHECK(values);
183
184 std::vector<CryptoKeyHeaderValues> candidate_values;
185
186 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ',');
187 while (value_iterator.GetNext()) {
188 CryptoKeyHeaderValues candidate_value;
189 if (!ParseCryptoKeyHeaderValuesImpl(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 } // namespace gcm 144 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698