1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
// Copyright 2015 The Chromium Authors
// 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 <string_view>
#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(std::string_view 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(std::string_view 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_(std::string_view(header_begin, header_end), /*delimiter=*/','),
rs_(kDefaultRecordSizeBytes) {}
EncryptionHeaderIterator::~EncryptionHeaderIterator() = default;
bool EncryptionHeaderIterator::GetNext() {
keyid_.clear();
salt_.clear();
rs_ = kDefaultRecordSizeBytes;
if (!iterator_.GetNext())
return false;
net::HttpUtil::NameValuePairsIterator name_value_pairs(
iterator_.value(), /*delimiter=*/';',
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 std::string_view name = name_value_pairs.name();
const std::string_view value = name_value_pairs.value();
if (base::EqualsCaseInsensitiveASCII(name, "keyid")) {
if (found_keyid)
return false;
keyid_ = value;
found_keyid = true;
} else if (base::EqualsCaseInsensitiveASCII(name, "salt")) {
if (found_salt || !ValueToDecodedString(value, &salt_))
return false;
found_salt = true;
} else if (base::EqualsCaseInsensitiveASCII(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_(std::string_view(header_begin, header_end), /*delimiter=*/',') {
}
CryptoKeyHeaderIterator::~CryptoKeyHeaderIterator() = default;
bool CryptoKeyHeaderIterator::GetNext() {
keyid_.clear();
aesgcm128_.clear();
dh_.clear();
if (!iterator_.GetNext())
return false;
net::HttpUtil::NameValuePairsIterator name_value_pairs(
iterator_.value(), /*delimiter=*/';',
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 std::string_view name = name_value_pairs.name();
const std::string_view value = name_value_pairs.value();
if (base::EqualsCaseInsensitiveASCII(name, "keyid")) {
if (found_keyid)
return false;
keyid_ = value;
found_keyid = true;
} else if (base::EqualsCaseInsensitiveASCII(name, "aesgcm128")) {
if (found_aesgcm128 || !ValueToDecodedString(value, &aesgcm128_))
return false;
found_aesgcm128 = true;
} else if (base::EqualsCaseInsensitiveASCII(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
|