OLD | NEW |
---|---|
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; |
Ryan Sleevi
2015/12/16 21:21:27
Do you need the temporary? Shouldn't the API contr
Peter Beverloo
2015/12/16 21:35:35
Doh. I wrote that API. Fixed.
| |
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 a positive decimal integer greater than one 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 // Reject a leading plus, as the fact that the value must be positive is |
45 // normal integers, in that we want to reject values such as "+5" for | 45 // dictated by the specification. |
46 // compatibility with UAs that use other number parsing mechanisms. | |
47 if (value[0] == '+') | 46 if (value[0] == '+') |
48 return false; | 47 return false; |
49 | 48 |
50 if (!base::StringToUint64(value, rs)) | 49 uint64_t candidate_rs; |
50 if (!base::StringToUint64(value, &candidate_rs)) | |
51 return false; | 51 return false; |
52 | 52 |
53 // The record size MUST be greater than 1. | 53 // The record size MUST be greater than one byte. |
54 return *rs > 1; | 54 if (candidate_rs <= 1) |
55 return false; | |
56 | |
57 *rs = candidate_rs; | |
58 return true; | |
55 } | 59 } |
56 | 60 |
57 // Parses the string between |input_begin| and |input_end| according to the | 61 } // namespace |
58 // extended ABNF syntax for the Encryption HTTP header, per the "parameter" | 62 |
59 // rule from RFC 7231 (https://tools.ietf.org/html/rfc7231). | 63 EncryptionHeaderIterator::EncryptionHeaderIterator( |
60 // | 64 std::string::const_iterator header_begin, |
61 // encryption_params = [ parameter *( ";" parameter ) ] | 65 std::string::const_iterator header_end) |
62 // | 66 : iterator_(header_begin, header_end, ','), |
63 // This implementation applies the parameters defined in section 3.1 of the | 67 rs_(kDefaultRecordSizeBytes) {} |
64 // HTTP encryption encoding document: | 68 |
65 // | 69 EncryptionHeaderIterator::~EncryptionHeaderIterator() {} |
66 // https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-3.1 | 70 |
67 // | 71 bool EncryptionHeaderIterator::GetNext() { |
68 // This means that the three supported parameters are: | 72 keyid_.clear(); |
69 // | 73 salt_.clear(); |
70 // [ "keyid" "=" string ] | 74 rs_ = kDefaultRecordSizeBytes; |
71 // [ ";" "salt" "=" base64url ] | 75 |
72 // [ ";" "rs" "=" octet-count ] | 76 if (!iterator_.GetNext()) |
73 bool ParseEncryptionHeaderValuesImpl(std::string::const_iterator input_begin, | 77 return false; |
74 std::string::const_iterator input_end, | 78 |
75 EncryptionHeaderValues* values) { | |
76 net::HttpUtil::NameValuePairsIterator name_value_pairs( | 79 net::HttpUtil::NameValuePairsIterator name_value_pairs( |
77 input_begin, input_end, ';', | 80 iterator_.value_begin(), iterator_.value_end(), ';', |
78 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | 81 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); |
79 | 82 |
80 while (name_value_pairs.GetNext()) { | 83 while (name_value_pairs.GetNext()) { |
81 const base::StringPiece name(name_value_pairs.name_begin(), | 84 const base::StringPiece name(name_value_pairs.name_begin(), |
82 name_value_pairs.name_end()); | 85 name_value_pairs.name_end()); |
86 const base::StringPiece value(name_value_pairs.value_begin(), | |
87 name_value_pairs.value_end()); | |
83 | 88 |
84 if (base::LowerCaseEqualsASCII(name, "keyid")) { | 89 if (base::LowerCaseEqualsASCII(name, "keyid")) { |
85 values->keyid.assign(name_value_pairs.value_begin(), | 90 value.CopyToString(&keyid_); |
86 name_value_pairs.value_end()); | |
87 } else if (base::LowerCaseEqualsASCII(name, "salt")) { | 91 } else if (base::LowerCaseEqualsASCII(name, "salt")) { |
88 if (!ValueToDecodedString(name_value_pairs.value_begin(), | 92 if (!ValueToDecodedString(value, &salt_)) |
89 name_value_pairs.value_end(), &values->salt)) { | |
90 return false; | 93 return false; |
91 } | |
92 } else if (base::LowerCaseEqualsASCII(name, "rs")) { | 94 } else if (base::LowerCaseEqualsASCII(name, "rs")) { |
93 if (!RecordSizeToInt(name_value_pairs.value_begin(), | 95 if (!RecordSizeToInt(value, &rs_)) |
94 name_value_pairs.value_end(), &values->rs)) { | |
95 return false; | 96 return false; |
96 } | |
97 } else { | 97 } else { |
98 // Silently ignore unknown directives for forward compatibility. | 98 // Silently ignore unknown directives for forward compatibility. |
99 } | 99 } |
100 } | 100 } |
Ryan Sleevi
2015/12/16 21:21:27
If your PR is accepted, won't this be non-spec-com
Peter Beverloo
2015/12/16 21:35:35
Yes, but it'll be an easy fix. I'm not in a hurry
| |
101 | 101 |
102 return name_value_pairs.valid(); | 102 return name_value_pairs.valid(); |
103 } | 103 } |
104 | 104 |
105 // Parses the string between |input_begin| and |input_end| according to the | 105 CryptoKeyHeaderIterator::CryptoKeyHeaderIterator( |
106 // extended ABNF syntax for the Crypto-Key HTTP header, per the "parameter" rule | 106 std::string::const_iterator header_begin, |
107 // from RFC 7231 (https://tools.ietf.org/html/rfc7231). | 107 std::string::const_iterator header_end) |
108 // | 108 : iterator_(header_begin, header_end, ',') {} |
109 // encryption_params = [ parameter *( ";" parameter ) ] | 109 |
110 // | 110 CryptoKeyHeaderIterator::~CryptoKeyHeaderIterator() {} |
111 // This implementation applies the parameters defined in section 4 of the | 111 |
112 // HTTP encryption encoding document: | 112 bool CryptoKeyHeaderIterator::GetNext() { |
113 // | 113 keyid_.clear(); |
114 //https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-4 | 114 aesgcm128_.clear(); |
115 // | 115 dh_.clear(); |
116 // This means that the three supported parameters are: | 116 |
117 // | 117 if (!iterator_.GetNext()) |
118 // [ "keyid" "=" string ] | 118 return false; |
119 // [ ";" "aesgcm128" "=" base64url ] | 119 |
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( | 120 net::HttpUtil::NameValuePairsIterator name_value_pairs( |
125 input_begin, input_end, ';', | 121 iterator_.value_begin(), iterator_.value_end(), ';', |
126 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | 122 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); |
127 | 123 |
128 while (name_value_pairs.GetNext()) { | 124 while (name_value_pairs.GetNext()) { |
129 const base::StringPiece name(name_value_pairs.name_begin(), | 125 const base::StringPiece name(name_value_pairs.name_begin(), |
130 name_value_pairs.name_end()); | 126 name_value_pairs.name_end()); |
127 const base::StringPiece value(name_value_pairs.value_begin(), | |
128 name_value_pairs.value_end()); | |
131 | 129 |
132 if (base::LowerCaseEqualsASCII(name, "keyid")) { | 130 if (base::LowerCaseEqualsASCII(name, "keyid")) { |
133 values->keyid.assign(name_value_pairs.value_begin(), | 131 value.CopyToString(&keyid_); |
134 name_value_pairs.value_end()); | |
135 } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) { | 132 } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) { |
136 if (!ValueToDecodedString(name_value_pairs.value_begin(), | 133 if (!ValueToDecodedString(value, &aesgcm128_)) |
137 name_value_pairs.value_end(), | |
138 &values->aesgcm128)) { | |
139 return false; | 134 return false; |
140 } | |
141 } else if (base::LowerCaseEqualsASCII(name, "dh")) { | 135 } else if (base::LowerCaseEqualsASCII(name, "dh")) { |
142 if (!ValueToDecodedString(name_value_pairs.value_begin(), | 136 if (!ValueToDecodedString(value, &dh_)) |
143 name_value_pairs.value_end(), &values->dh)) { | |
144 return false; | 137 return false; |
145 } | |
146 } else { | 138 } else { |
147 // Silently ignore unknown directives for forward compatibility. | 139 // Silently ignore unknown directives for forward compatibility. |
148 } | 140 } |
149 } | 141 } |
150 | 142 |
151 return name_value_pairs.valid(); | 143 return name_value_pairs.valid(); |
152 } | 144 } |
153 | 145 |
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 | 146 } // namespace gcm |
OLD | NEW |