| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/gcm_driver/crypto/encryption_header_parsers.h" |
| |
| #include "base/base64url.h" |
| #include "base/strings/string_number_conversions.h" |
| |
| #include "base/strings/string_util.h" |
| |
| |
| namespace gcm { |
| |
| namespace { |
| |
| // The default record size in bytes, as defined in section two of |
| // https://tools.ietf.org/html/draft-thomson-http-encryption. |
| const uint64_t kDefaultRecordSizeBytes = 4096; |
| |
| // Decodes the string in |value| using base64url and writes the decoded value to |
| // |*salt|. Returns whether the string is not empty and could be decoded. |
| bool ValueToDecodedString(base::StringPiece value, std::string* salt) { |
| if (value.empty()) |
| return false; |
| |
| return base::Base64UrlDecode( |
| value, base::Base64UrlDecodePolicy::IGNORE_PADDING, salt); |
| } |
| |
| // Parses the record size in |value| and writes the value to |*rs|. The value |
| // must be a positive decimal integer greater than one that does not start |
| // with a plus. Returns whether the record size was valid. |
| bool RecordSizeToInt(base::StringPiece value, uint64_t* rs) { |
| if (value.empty()) |
| return false; |
| |
| // Reject a leading plus, as the fact that the value must be positive is |
| // dictated by the specification. |
| if (value[0] == '+') |
| return false; |
| |
| uint64_t candidate_rs; |
| if (!base::StringToUint64(value, &candidate_rs)) |
| return false; |
| |
| // The record size MUST be greater than one byte. |
| if (candidate_rs <= 1) |
| return false; |
| |
| *rs = candidate_rs; |
| return true; |
| } |
| |
| } // namespace |
| |
| EncryptionHeaderIterator::EncryptionHeaderIterator( |
| std::string::const_iterator header_begin, |
| std::string::const_iterator header_end) |
| : iterator_(header_begin, header_end, ','), |
| rs_(kDefaultRecordSizeBytes) {} |
| |
| EncryptionHeaderIterator::~EncryptionHeaderIterator() {} |
| |
| bool EncryptionHeaderIterator::GetNext() { |
| keyid_.clear(); |
| salt_.clear(); |
| rs_ = kDefaultRecordSizeBytes; |
| |
| if (!iterator_.GetNext()) |
| return false; |
| |
| net::HttpUtil::NameValuePairsIterator name_value_pairs( |
| iterator_.value_begin(), iterator_.value_end(), ';', |
| net::HttpUtil::NameValuePairsIterator::Values::REQUIRED, |
| net::HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT); |
| |
| bool found_keyid = false; |
| bool found_salt = false; |
| bool found_rs = false; |
| |
| while (name_value_pairs.GetNext()) { |
| const base::StringPiece name(name_value_pairs.name_begin(), |
| name_value_pairs.name_end()); |
| const base::StringPiece value(name_value_pairs.value_begin(), |
| name_value_pairs.value_end()); |
| |
| if (base::LowerCaseEqualsASCII(name, "keyid")) { |
| if (found_keyid) |
| return false; |
| value.CopyToString(&keyid_); |
| found_keyid = true; |
| } else if (base::LowerCaseEqualsASCII(name, "salt")) { |
| if (found_salt || !ValueToDecodedString(value, &salt_)) |
| return false; |
| found_salt = true; |
| } else if (base::LowerCaseEqualsASCII(name, "rs")) { |
| if (found_rs || !RecordSizeToInt(value, &rs_)) |
| return false; |
| found_rs = true; |
| } else { |
| // Silently ignore unknown directives for forward compatibility. |
| } |
| } |
| |
| return name_value_pairs.valid(); |
| } |
| |
| CryptoKeyHeaderIterator::CryptoKeyHeaderIterator( |
| std::string::const_iterator header_begin, |
| std::string::const_iterator header_end) |
| : iterator_(header_begin, header_end, ',') {} |
| |
| CryptoKeyHeaderIterator::~CryptoKeyHeaderIterator() {} |
| |
| bool CryptoKeyHeaderIterator::GetNext() { |
| keyid_.clear(); |
| aesgcm128_.clear(); |
| dh_.clear(); |
| |
| if (!iterator_.GetNext()) |
| return false; |
| |
| net::HttpUtil::NameValuePairsIterator name_value_pairs( |
| iterator_.value_begin(), iterator_.value_end(), ';', |
| net::HttpUtil::NameValuePairsIterator::Values::REQUIRED, |
| net::HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT); |
| |
| bool found_keyid = false; |
| bool found_aesgcm128 = false; |
| bool found_dh = false; |
| |
| while (name_value_pairs.GetNext()) { |
| const base::StringPiece name(name_value_pairs.name_begin(), |
| name_value_pairs.name_end()); |
| const base::StringPiece value(name_value_pairs.value_begin(), |
| name_value_pairs.value_end()); |
| |
| if (base::LowerCaseEqualsASCII(name, "keyid")) { |
| if (found_keyid) |
| return false; |
| value.CopyToString(&keyid_); |
| found_keyid = true; |
| } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) { |
| if (found_aesgcm128 || !ValueToDecodedString(value, &aesgcm128_)) |
| return false; |
| found_aesgcm128 = true; |
| } else if (base::LowerCaseEqualsASCII(name, "dh")) { |
| if (found_dh || !ValueToDecodedString(value, &dh_)) |
| return false; |
| found_dh = true; |
| } else { |
| // Silently ignore unknown directives for forward compatibility. |
| } |
| } |
| |
| return name_value_pairs.valid(); |
| } |
| |
| } // namespace gcm |