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

Unified Diff: components/password_manager/core/browser/affiliation_utils.cc

Issue 771173002: Added utility functions related to working with "facets". (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix Android compile errors. Created 6 years 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/password_manager/core/browser/affiliation_utils.cc
diff --git a/components/password_manager/core/browser/affiliation_utils.cc b/components/password_manager/core/browser/affiliation_utils.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e03cec91f30cf1266a645b3b4ee14457e353cbf2
--- /dev/null
+++ b/components/password_manager/core/browser/affiliation_utils.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 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/password_manager/core/browser/affiliation_utils.h"
+
+#include <algorithm>
+
+#include "base/base64.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/base/escape.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon_stdstring.h"
+#include "url/url_util.h"
+
+namespace password_manager {
+
+namespace {
+
+// The scheme used for identifying Android applications.
+const char kAndroidAppScheme[] = "android";
+
+// Returns a StringPiece corresponding to |component| in |uri|, or the empty
+// string in case there is no such component.
+base::StringPiece ComponentString(const std::string& uri,
+ const url::Component& component) {
+ if (!component.is_valid())
+ return base::StringPiece();
+ return base::StringPiece(uri.c_str() + component.begin, component.len);
+}
+
+// Extracts the scheme of an unparsed |uri| as a StringPiece, or returns the
+// empty string on failure.
+base::StringPiece ExtractScheme(const std::string& uri) {
+ url::Component scheme_component;
+ if (url::ExtractScheme(uri.c_str(), uri.size(), &scheme_component))
+ return ComponentString(uri, scheme_component);
+ return base::StringPiece();
+}
+
+// Returns true if the passed ASCII |input| string contains nothing else than
+// alphanumeric characters and those in |other_characters|.
+bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
+ const base::StringPiece& other_characters) {
+ for (char c : input) {
+ if (!IsAsciiAlpha(c) && !IsAsciiDigit(c) &&
+ other_characters.find(c) == base::StringPiece::npos)
+ return false;
+ }
+ return true;
+}
+
+// Canonicalizes a Web facet URI, and returns true if canonicalization was
+// successful and produced a valid URI.
+bool CanonicalizeWebFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::Parsed canonical_parsed;
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ bool canonicalization_succeeded = url::CanonicalizeStandardURL(
+ input_uri.c_str(), input_uri.size(), input_parsed, NULL,
+ &canonical_output, &canonical_parsed);
+ canonical_output.Complete();
+
+ if (canonicalization_succeeded &&
+ canonical_parsed.host.is_nonempty() &&
+ !canonical_parsed.username.is_valid() &&
+ !canonical_parsed.password.is_valid() &&
+ ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
+ !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
+ // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
+ DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
+ canonical_uri->erase(canonical_parsed.path.begin,
+ canonical_parsed.path.len);
+ return true;
+ }
+ return false;
+}
+
+// Adds padding until the length of the base64-encoded |data| becomes a multiple
+// of 4, and returns true if the thusly obtained |data| is now correctly padded,
+// i.e., there are at most 2 padding characters ('=') at the very end.
+bool CanonicalizeBase64Padding(std::string* data) {
+ while (data->size() % 4u != 0u)
+ data->push_back('=');
+
+ size_t first_padding = data->find_first_of('=');
+ return first_padding == std::string::npos ||
+ (data->size() - first_padding <= 2u &&
+ data->find_first_not_of('=', first_padding) == std::string::npos);
+}
+
+// Canonicalizes the username component in an Android facet URI (containing the
+// certificate hash), and returns true if canonicalization was successful and
+// produced a valid non-empty component.
+bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
+ url::CanonOutput* canonical_output) {
+ // Characters other than alphanumeric that are used in the "URL and filename
+ // safe" base64 alphabet; plus the padding ('=').
+ const char kBase64NonAlphanumericChars[] = "-_=";
+
+ // We need net::UnescapeRule::URL_SPECIAL_CHARS to unescape the padding ('=').
+ std::string base64_encoded_hash = net::UnescapeURLComponent(
+ input_hash.as_string(), net::UnescapeRule::URL_SPECIAL_CHARS);
+
+ if (!base64_encoded_hash.empty() &&
+ CanonicalizeBase64Padding(&base64_encoded_hash) &&
+ ContainsOnlyAlphanumericAnd(
+ base64_encoded_hash, kBase64NonAlphanumericChars)) {
+ canonical_output->Append(base64_encoded_hash.data(),
+ base64_encoded_hash.size());
+ canonical_output->push_back('@');
+ return true;
+ }
+ return false;
+}
+
+// Canonicalizes the host component in an Android facet URI (containing the
+// package name), and returns true if canonicalization was successful and
+// produced a valid non-empty component.
+bool CanonicalizePackageNameComponent(
+ const base::StringPiece& input_package_name,
+ url::CanonOutput* canonical_output) {
+ // Characters other than alphanumeric that are permitted in the package names.
+ const char kPackageNameNonAlphanumericChars[] = "._";
+
+ std::string package_name = net::UnescapeURLComponent(
+ input_package_name.as_string(), net::UnescapeRule::NORMAL);
+
+ // TODO(engedy): We might want to use a regex to check this more throughly.
+ if (!package_name.empty() &&
+ ContainsOnlyAlphanumericAnd(
+ package_name, kPackageNameNonAlphanumericChars)) {
+ canonical_output->Append(package_name.data(), package_name.size());
+ return true;
+ }
+ return false;
+}
+
+// Canonicalizes an Android facet URI, and returns true if canonicalization was
+// successful and produced a valid URI.
+bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ url::Component unused;
+ bool success = url::CanonicalizeScheme(
+ input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
+
+ canonical_output.push_back('/');
+ canonical_output.push_back('/');
+
+ // We cannot use url::CanonicalizeUserInfo as that would percent encode the
+ // base64 padding characters ('=').
+ success &= CanonicalizeHashComponent(
+ ComponentString(input_uri, input_parsed.username), &canonical_output);
+
+ // We cannot use url::CanonicalizeHost as that would convert the package name
+ // to lower case, but the package name is case sensitive.
+ success &= CanonicalizePackageNameComponent(
+ ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
+
+ canonical_output.Complete();
+
+ return success && !input_parsed.password.is_nonempty() &&
+ (!input_parsed.path.is_nonempty() ||
+ ComponentString(input_uri, input_parsed.path) == "/") &&
+ !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
+ !input_parsed.ref.is_valid();
+}
+
+// Computes the canonicalized form of |uri| into |canonical_uri|, and returns
+// true if canonicalization was successful and produced a valid URI.
+bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
+ std::string* canonical_uri) {
+ DCHECK(canonical_uri);
+ canonical_uri->clear();
+ canonical_uri->reserve(input_uri.size() + 32);
+
+ url::Parsed input_parsed;
+ url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
+
+ base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
+ if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
+ url::kHttpsScheme)) {
+ return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
+ } else if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
+ kAndroidAppScheme)) {
+ return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
+ }
+ return false;
+}
+
+} // namespace
+
+FacetURI::FacetURI() : is_valid_(false) {
+}
+
+// static
+FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
+ std::string canonical_spec;
+ bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
+ return FacetURI(canonical_spec, is_valid);
+}
+
+// static
+FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
+ return FacetURI(canonical_spec, true);
+}
+
+bool FacetURI::operator==(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ == other.canonical_spec_;
+}
+
+bool FacetURI::operator!=(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ != other.canonical_spec_;
+}
+
+bool FacetURI::operator<(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ < other.canonical_spec_;
+}
+
+bool FacetURI::operator>(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ > other.canonical_spec_;
+}
+
+bool FacetURI::IsValidWebFacetURI() const {
+ return is_valid_ && ExtractScheme(canonical_spec_) == url::kHttpsScheme;
+}
+
+bool FacetURI::IsValidAndroidFacetURI() const {
+ return is_valid_ && ExtractScheme(canonical_spec_) == kAndroidAppScheme;
+}
+
+FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
+ : is_valid_(is_valid), canonical_spec_(canonical_spec) {
+}
+
+bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
+ const AffiliatedFacets& b) {
+ if (a.size() != b.size())
+ return false;
+
+ std::vector<FacetURI> a_sorted(a.begin(), a.end());
+ std::vector<FacetURI> b_sorted(b.begin(), b.end());
+ std::sort(a_sorted.begin(), a_sorted.end());
+ std::sort(b_sorted.begin(), b_sorted.end());
+ return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
+}
+
+} // namespace password_manager

Powered by Google App Engine
This is Rietveld 408576698