Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Unified Diff: components/gcm_driver/crypto/encryption_header_parsers.cc

Issue 1244803002: Add parsers for the Encryption and Encryption-Key HTTP headers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/gcm_driver/crypto/encryption_header_parsers.cc
diff --git a/components/gcm_driver/crypto/encryption_header_parsers.cc b/components/gcm_driver/crypto/encryption_header_parsers.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7ab7646dcc31c527a53010792c67dbe9fed22801
--- /dev/null
+++ b/components/gcm_driver/crypto/encryption_header_parsers.cc
@@ -0,0 +1,193 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "net/http/http_util.h"
+
+namespace gcm {
+
+namespace {
+
+const char kKeyIdName[] = "keyid";
+const char kSaltName[] = "salt";
+const char kRecordSizeName[] = "rs";
+const char kKeyName[] = "key";
+const char kDiffieHellmanName[] = "dh";
+
+using NameValueMap = std::map<std::string, std::string>;
+using NameValueMapVector = std::vector<NameValueMap>;
+
+// Parses |input| as a header value containing multiple name-value-lists.
+bool ParseMultipleNameValueListsHeader(const std::string& input,
+ NameValueMapVector* output) {
+ base::StringTokenizer tokenizer(input, ",");
+ tokenizer.set_quote_chars("'\"");
+
+ while (tokenizer.GetNext()) {
+ NameValueMap name_value_map;
+
+ net::HttpUtil::NameValuePairsIterator parser(tokenizer.token_begin(),
johnme 2015/07/21 14:34:43 I notice that NameValuePairsIterator allows arbitr
Peter Beverloo 2015/07/21 17:51:30 I'd like to defer to Ryan for final judgement here
+ tokenizer.token_end(), ';');
+ if (!parser.valid())
johnme 2015/07/21 14:34:43 It's not actually possible for valid to be false b
Peter Beverloo 2015/07/21 17:51:30 Done.
+ return false;
johnme 2015/07/21 14:34:43 It might also make sense to call output->clear() b
Peter Beverloo 2015/07/21 17:51:30 Done.
+
+ while (parser.GetNext())
+ name_value_map[parser.name()] = parser.value();
+
+ output->push_back(name_value_map);
+ }
+
+ return true;
+}
+
+// TODO(peter): Generalize a URL-safe base64 implementation.
johnme 2015/07/21 14:34:43 Please link to https://tools.ietf.org/html/rfc4648
Peter Beverloo 2015/07/21 17:51:30 Done.
+bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
+ if (input.find_first_of("+/=") != std::string::npos)
johnme 2015/07/21 14:34:43 Why do you disallow '='? Whilst padding is technic
Peter Beverloo 2015/07/21 17:51:30 Done.
+ return false;
+
+ // Add padding.
+ size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4;
+ std::string padded_input(input);
+ padded_input.resize(padded_size, '=');
+
+ // Convert to standard base64 alphabet.
+ base::ReplaceChars(padded_input, "-", "+", &padded_input);
+ base::ReplaceChars(padded_input, "_", "/", &padded_input);
+
+ return base::Base64Decode(padded_input, output);
+}
+
+// Parses the "salt" field of the Encryption header. Must be a base64 URL
johnme 2015/07/21 14:34:43 Nit: "base64url" for consistency with the naming i
Peter Beverloo 2015/07/21 17:51:30 Done.
+// encoded string that decodes to a string exactly 16 bytes in length.
+bool ParseSalt(const std::string& value, std::string* output) {
+ std::string decoded_value;
+ if (!Base64DecodeUrlSafe(value, &decoded_value))
+ return false;
+
+ if (decoded_value.size() != 16)
+ return false;
+
+ output->swap(decoded_value);
+ return true;
+}
+
+// Parses the "rs" field of the Encryption header. When present, the value must
+// be a positive decimal integer greater than 1.
+bool ParseRecordSize(const std::string& value, int64_t* output) {
+ int64_t decimal_value = 0;
+ if (!base::StringToInt64(value, &decimal_value))
+ return false;
+
+ if (decimal_value <= 1)
+ return false;
+
+ *output = decimal_value;
+ return true;
+}
+
+// Parses the "key" field of the Encryption-Key header. When present, the value
+// must be a base64 URL encoded string that decodes to a string at least 16
+// bytes in length.
+bool ParseKey(const std::string& value, std::string* output) {
+ std::string decoded_value;
+ if (!Base64DecodeUrlSafe(value, &decoded_value))
+ return false;
+
+ if (decoded_value.size() < 16)
+ return false;
+
+ output->swap(decoded_value);
+ return true;
+}
+
+} // namespace
+
+bool ParseEncryptionHeader(const std::string& input,
+ std::vector<EncryptionHeaderValue>* result) {
+ DCHECK(result);
+
+ NameValueMapVector parsed_input;
+ if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
+ return false;
+
+ for (const auto& value_map : parsed_input) {
+ EncryptionHeaderValue value;
+
+ // Optional field: "keyid". May contain any string.
+ const auto& keyid_iter = value_map.find(kKeyIdName);
+ if (keyid_iter != value_map.end())
+ value.keyid = keyid_iter->second;
+
+ // Required field: "salt". Must contain a base64 URL-encoded string that
+ // decodes to a string that is exactly 16-bytes in length.
+ const auto& salt_iter = value_map.find(kSaltName);
+ if (salt_iter == value_map.end())
+ return false;
johnme 2015/07/21 14:34:43 If value_map is completely empty, it should be ok
Peter Beverloo 2015/07/21 17:51:30 Done.
+
+ if (!ParseSalt(salt_iter->second, &value.salt))
+ return false;
+
+ // Optional field: "rs". Must contain a positive decimal integer greater
+ // than one when supplied.
+ const auto& record_size_iter = value_map.find(kRecordSizeName);
+ if (record_size_iter != value_map.end()) {
+ if (!ParseRecordSize(record_size_iter->second, &value.rs))
+ return false;
+ }
+
+ result->push_back(value);
+ }
+
+ return true;
+}
+
+bool ParseEncryptionKeyHeader(const std::string& input,
+ std::vector<EncryptionKeyHeaderValue>* result) {
+ DCHECK(result);
+
+ NameValueMapVector parsed_input;
+ if (!ParseMultipleNameValueListsHeader(input, &parsed_input))
+ return false;
+
+ for (const auto& value_map : parsed_input) {
+ EncryptionKeyHeaderValue value;
+
+ // Optional field: "keyid". May contain any string.
+ const auto& keyid_iter = value_map.find(kKeyIdName);
+ if (keyid_iter != value_map.end())
+ value.keyid = keyid_iter->second;
+
+ // Optional field: "key". Must contain a base64 URL-encoded string with the
+ // explicit encryption key.
+ const auto& key_iter = value_map.find(kKeyName);
+ if (key_iter != value_map.end()) {
+ if (!ParseKey(key_iter->second, &value.key))
+ return false;
+ }
+
+ // Optional field: "dh". Must contain a base64 URL-encoded string with the
+ // Diffie-Hellman share (either modp or elliptic curve).
+ const auto& dh_iter = value_map.find(kDiffieHellmanName);
+ if (dh_iter != value_map.end()) {
+ if (!Base64DecodeUrlSafe(dh_iter->second, &value.dh))
+ return false;
+ }
+
+ result->push_back(value);
+ }
+
+ return true;
+}
+
+} // namespace gcm

Powered by Google App Engine
This is Rietveld 408576698