Chromium Code Reviews| 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/numerics/safe_math.h" | |
| 9 #include "base/strings/string_number_conversions.h" | |
| 10 #include "base/strings/string_piece.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "net/http/http_util.h" | |
| 13 | |
| 14 namespace gcm { | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // The default record size in bytes, as defined in section two of | |
| 19 // https://tools.ietf.org/html/draft-thomson-http-encryption-01. | |
| 20 const uint64_t kDefaultRecordSizeBytes = 4096; | |
| 21 | |
| 22 // TODO(peter): Unify the base64url implementations. https://crbug.com/536745 | |
| 23 bool Base64URLDecode(const base::StringPiece& input, std::string* output) { | |
| 24 // Bail on malformed strings, which already contain a '+' or a '/'. All valid | |
| 25 // strings should escape these special characters as '-' and '_', | |
| 26 // respectively. | |
| 27 if (input.find_first_of("+/") != std::string::npos) | |
| 28 return false; | |
| 29 | |
| 30 base::CheckedNumeric<size_t> checked_padded_size = input.size(); | |
| 31 if (input.size() % 4) | |
| 32 checked_padded_size += 4 - (input.size() % 4); | |
| 33 | |
| 34 // Add padding to |input|. | |
| 35 std::string padded_input(input.begin(), input.end()); | |
| 36 padded_input.resize(checked_padded_size.ValueOrDie(), '='); | |
| 37 | |
| 38 // Convert to standard base64 alphabet. | |
| 39 base::ReplaceChars(padded_input, "-", "+", &padded_input); | |
| 40 base::ReplaceChars(padded_input, "_", "/", &padded_input); | |
| 41 | |
| 42 return base::Base64Decode(padded_input, output); | |
| 43 } | |
| 44 | |
| 45 bool ValueToDecodedString(const std::string::const_iterator& begin, | |
| 46 const std::string::const_iterator& end, | |
| 47 std::string* salt) { | |
| 48 const base::StringPiece value(begin, end); | |
| 49 if (value.empty()) | |
| 50 return false; | |
| 51 | |
| 52 return Base64URLDecode(value, salt); | |
| 53 } | |
| 54 | |
| 55 bool RecordSizeToInt(const std::string::const_iterator& begin, | |
|
Ryan Sleevi
2015/10/01 22:50:45
Document
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 56 const std::string::const_iterator& end, | |
| 57 uint64_t* rs) { | |
| 58 const base::StringPiece value(begin, end); | |
| 59 if (value.empty()) | |
| 60 return false; | |
| 61 | |
| 62 // Parsing the "rs" parameter uses stricter semantics than parsing rules for | |
| 63 // normal integers, in that we want to reject values such as "+5" for | |
| 64 // compatibility with UAs that use other number parsing mechanisms. | |
| 65 if (value[0] == '+') | |
| 66 return false; | |
| 67 | |
| 68 if (!base::StringToUint64(value, rs)) | |
| 69 return false; | |
| 70 | |
| 71 // The record size MUST be greater than 1. | |
| 72 return *rs > 1; | |
| 73 } | |
| 74 | |
| 75 bool ParseEncryptionHeaderValuesImpl(std::string::const_iterator input_begin, | |
|
Ryan Sleevi
2015/10/01 22:50:45
Document
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 76 std::string::const_iterator input_end, | |
| 77 EncryptionHeaderValues* values) { | |
| 78 net::HttpUtil::NameValuePairsIterator name_value_pairs( | |
| 79 input_begin, input_end, ';', | |
| 80 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | |
| 81 | |
| 82 while (name_value_pairs.GetNext()) { | |
| 83 const base::StringPiece name(name_value_pairs.name_begin(), | |
| 84 name_value_pairs.name_end()); | |
| 85 | |
| 86 if (base::LowerCaseEqualsASCII(name, "keyid")) { | |
| 87 values->keyid.assign(name_value_pairs.value_begin(), | |
| 88 name_value_pairs.value_end()); | |
| 89 } else if (base::LowerCaseEqualsASCII(name, "salt")) { | |
| 90 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
| 91 name_value_pairs.value_end(), &values->salt)) { | |
| 92 return false; | |
| 93 } | |
| 94 } else if (base::LowerCaseEqualsASCII(name, "rs")) { | |
| 95 if (!RecordSizeToInt(name_value_pairs.value_begin(), | |
| 96 name_value_pairs.value_end(), &values->rs)) { | |
| 97 return false; | |
| 98 } | |
| 99 } else { | |
| 100 // Silently ignore unknown directives for forward compatibility. | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 return name_value_pairs.valid(); | |
| 105 } | |
| 106 | |
| 107 bool ParseEncryptionKeyHeaderValuesImpl(std::string::const_iterator input_begin, | |
|
Ryan Sleevi
2015/10/01 22:50:45
Document
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 108 std::string::const_iterator input_end, | |
| 109 EncryptionKeyHeaderValues* values) { | |
| 110 net::HttpUtil::NameValuePairsIterator name_value_pairs( | |
| 111 input_begin, input_end, ';', | |
| 112 net::HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL); | |
| 113 | |
| 114 while (name_value_pairs.GetNext()) { | |
| 115 const base::StringPiece name(name_value_pairs.name_begin(), | |
| 116 name_value_pairs.name_end()); | |
| 117 | |
| 118 if (base::LowerCaseEqualsASCII(name, "keyid")) { | |
| 119 values->keyid.assign(name_value_pairs.value_begin(), | |
| 120 name_value_pairs.value_end()); | |
| 121 } else if (base::LowerCaseEqualsASCII(name, "key")) { | |
| 122 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
| 123 name_value_pairs.value_end(), &values->key)) { | |
| 124 return false; | |
| 125 } | |
| 126 } else if (base::LowerCaseEqualsASCII(name, "dh")) { | |
| 127 if (!ValueToDecodedString(name_value_pairs.value_begin(), | |
| 128 name_value_pairs.value_end(), &values->dh)) { | |
| 129 return false; | |
| 130 } | |
| 131 } else { | |
| 132 // Silently ignore unknown directives for forward compatibility. | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 return name_value_pairs.valid(); | |
| 137 } | |
| 138 | |
| 139 } // namespace | |
| 140 | |
| 141 // "Encryption" ":" | |
| 142 // [ "keyid" "=" string ] | |
| 143 // [ ";" "salt" "=" base64url ] | |
| 144 // [ ";" "rs" "=" octet-count ] | |
|
Ryan Sleevi
2015/10/01 22:50:45
Seems like this comment should be moved to 75?
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 145 bool ParseEncryptionHeader(const std::string& input, | |
| 146 std::vector<EncryptionHeaderValues>* values) { | |
| 147 DCHECK(values); | |
| 148 | |
| 149 std::vector<EncryptionHeaderValues> candidate_values; | |
| 150 | |
| 151 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ','); | |
|
Ryan Sleevi
2015/10/01 22:50:45
Worth documenting this header follows #list rule a
Peter Beverloo
2015/10/02 13:10:22
Done in the header, so that this function doesn't
| |
| 152 while (value_iterator.GetNext()) { | |
| 153 EncryptionHeaderValues candidate_value; | |
| 154 candidate_value.rs = kDefaultRecordSizeBytes; | |
| 155 | |
| 156 if (!ParseEncryptionHeaderValuesImpl(value_iterator.value_begin(), | |
| 157 value_iterator.value_end(), | |
| 158 &candidate_value)) { | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 candidate_values.push_back(candidate_value); | |
| 163 } | |
| 164 | |
| 165 values->swap(candidate_values); | |
| 166 return true; | |
| 167 } | |
| 168 | |
| 169 // "Encryption-Key" ":" | |
| 170 // [ "keyid" "=" string ] | |
| 171 // [ ";" "key" "=" base64url ] | |
| 172 // [ ";" "dh" "=" base64url ] | |
|
Ryan Sleevi
2015/10/01 22:50:45
And this to 107?
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 173 bool ParseEncryptionKeyHeader(const std::string& input, | |
| 174 std::vector<EncryptionKeyHeaderValues>* values) { | |
| 175 DCHECK(values); | |
| 176 | |
| 177 std::vector<EncryptionKeyHeaderValues> candidate_values; | |
| 178 | |
| 179 net::HttpUtil::ValuesIterator value_iterator(input.begin(), input.end(), ','); | |
|
Ryan Sleevi
2015/10/01 22:50:45
Ditto :)
Peter Beverloo
2015/10/02 13:10:22
Done.
| |
| 180 while (value_iterator.GetNext()) { | |
| 181 EncryptionKeyHeaderValues candidate_value; | |
| 182 if (!ParseEncryptionKeyHeaderValuesImpl(value_iterator.value_begin(), | |
| 183 value_iterator.value_end(), | |
| 184 &candidate_value)) { | |
| 185 return false; | |
| 186 } | |
| 187 | |
| 188 candidate_values.push_back(candidate_value); | |
| 189 } | |
| 190 | |
| 191 values->swap(candidate_values); | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 } // namespace gcm | |
| OLD | NEW |