Chromium Code Reviews| 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; |
| 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 |
| OLD | NEW |