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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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/password_manager/core/browser/affiliation_utils.h"
6
7 #include <algorithm>
8
9 #include "base/base64.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_util.h"
12 #include "net/base/escape.h"
13 #include "url/third_party/mozilla/url_parse.h"
14 #include "url/url_canon_stdstring.h"
15 #include "url/url_util.h"
16
17 namespace password_manager {
18
19 namespace {
20
21 // The scheme used for identifying Android applications.
22 const char kAndroidAppScheme[] = "android";
23
24 // Returns a StringPiece corresponding to |component| in |uri|, or the empty
25 // string in case there is no such component.
26 base::StringPiece ComponentString(const std::string& uri,
27 const url::Component& component) {
28 if (!component.is_valid())
29 return base::StringPiece();
30 return base::StringPiece(uri.c_str() + component.begin, component.len);
31 }
32
33 // Extracts the scheme of an unparsed |uri| as a StringPiece, or returns the
34 // empty string on failure.
35 base::StringPiece ExtractScheme(const std::string& uri) {
36 url::Component scheme_component;
37 if (url::ExtractScheme(uri.c_str(), uri.size(), &scheme_component))
38 return ComponentString(uri, scheme_component);
39 return base::StringPiece();
40 }
41
42 // Returns true if the passed ASCII |input| string contains nothing else than
43 // alphanumeric characters and those in |other_characters|.
44 bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
45 const base::StringPiece& other_characters) {
46 for (char c : input) {
47 if (!IsAsciiAlpha(c) && !IsAsciiDigit(c) &&
48 other_characters.find(c) == base::StringPiece::npos)
49 return false;
50 }
51 return true;
52 }
53
54 // Canonicalizes a Web facet URI, and returns true if canonicalization was
55 // successful and produced a valid URI.
56 bool CanonicalizeWebFacetURI(const std::string& input_uri,
57 const url::Parsed& input_parsed,
58 std::string* canonical_uri) {
59 url::Parsed canonical_parsed;
60 url::StdStringCanonOutput canonical_output(canonical_uri);
61
62 bool canonicalization_succeeded = url::CanonicalizeStandardURL(
63 input_uri.c_str(), input_uri.size(), input_parsed, NULL,
64 &canonical_output, &canonical_parsed);
65 canonical_output.Complete();
66
67 if (canonicalization_succeeded &&
68 canonical_parsed.host.is_nonempty() &&
69 !canonical_parsed.username.is_valid() &&
70 !canonical_parsed.password.is_valid() &&
71 ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
72 !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
73 // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
74 DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
75 canonical_uri->erase(canonical_parsed.path.begin,
76 canonical_parsed.path.len);
77 return true;
78 }
79 return false;
80 }
81
82 // Adds padding until the length of the base64-encoded |data| becomes a multiple
83 // of 4, and returns true if the thusly obtained |data| is now correctly padded,
84 // i.e., there are at most 2 padding characters ('=') at the very end.
85 bool CanonicalizeBase64Padding(std::string* data) {
86 while (data->size() % 4u != 0u)
87 data->push_back('=');
88
89 size_t first_padding = data->find_first_of('=');
90 return first_padding == std::string::npos ||
91 (data->size() - first_padding <= 2u &&
92 data->find_first_not_of('=', first_padding) == std::string::npos);
93 }
94
95 // Canonicalizes the username component in an Android facet URI (containing the
96 // certificate hash), and returns true if canonicalization was successful and
97 // produced a valid non-empty component.
98 bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
99 url::CanonOutput* canonical_output) {
100 // Characters other than alphanumeric that are used in the "URL and filename
101 // safe" base64 alphabet; plus the padding ('=').
102 const char kBase64NonAlphanumericChars[] = "-_=";
103
104 // We need net::UnescapeRule::URL_SPECIAL_CHARS to unescape the padding ('=').
105 std::string base64_encoded_hash = net::UnescapeURLComponent(
106 input_hash.as_string(), net::UnescapeRule::URL_SPECIAL_CHARS);
107
108 if (!base64_encoded_hash.empty() &&
109 CanonicalizeBase64Padding(&base64_encoded_hash) &&
110 ContainsOnlyAlphanumericAnd(
111 base64_encoded_hash, kBase64NonAlphanumericChars)) {
112 canonical_output->Append(base64_encoded_hash.data(),
113 base64_encoded_hash.size());
114 canonical_output->push_back('@');
115 return true;
116 }
117 return false;
118 }
119
120 // Canonicalizes the host component in an Android facet URI (containing the
121 // package name), and returns true if canonicalization was successful and
122 // produced a valid non-empty component.
123 bool CanonicalizePackageNameComponent(
124 const base::StringPiece& input_package_name,
125 url::CanonOutput* canonical_output) {
126 // Characters other than alphanumeric that are permitted in the package names.
127 const char kPackageNameNonAlphanumericChars[] = "._";
128
129 std::string package_name = net::UnescapeURLComponent(
130 input_package_name.as_string(), net::UnescapeRule::NORMAL);
131
132 // TODO(engedy): We might want to use a regex to check this more throughly.
133 if (!package_name.empty() &&
134 ContainsOnlyAlphanumericAnd(
135 package_name, kPackageNameNonAlphanumericChars)) {
136 canonical_output->Append(package_name.data(), package_name.size());
137 return true;
138 }
139 return false;
140 }
141
142 // Canonicalizes an Android facet URI, and returns true if canonicalization was
143 // successful and produced a valid URI.
144 bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
145 const url::Parsed& input_parsed,
146 std::string* canonical_uri) {
147 url::StdStringCanonOutput canonical_output(canonical_uri);
148
149 url::Component unused;
150 bool success = url::CanonicalizeScheme(
151 input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
152
153 canonical_output.push_back('/');
154 canonical_output.push_back('/');
155
156 // We cannot use url::CanonicalizeUserInfo as that would percent encode the
157 // base64 padding characters ('=').
158 success &= CanonicalizeHashComponent(
159 ComponentString(input_uri, input_parsed.username), &canonical_output);
160
161 // We cannot use url::CanonicalizeHost as that would convert the package name
162 // to lower case, but the package name is case sensitive.
163 success &= CanonicalizePackageNameComponent(
164 ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
165
166 canonical_output.Complete();
167
168 return success && !input_parsed.password.is_nonempty() &&
169 (!input_parsed.path.is_nonempty() ||
170 ComponentString(input_uri, input_parsed.path) == "/") &&
171 !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
172 !input_parsed.ref.is_valid();
173 }
174
175 // Computes the canonicalized form of |uri| into |canonical_uri|, and returns
176 // true if canonicalization was successful and produced a valid URI.
177 bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
178 std::string* canonical_uri) {
179 DCHECK(canonical_uri);
180 canonical_uri->clear();
181 canonical_uri->reserve(input_uri.size() + 32);
182
183 url::Parsed input_parsed;
184 url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
185
186 base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
187 if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
188 url::kHttpsScheme)) {
189 return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
190 } else if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
191 kAndroidAppScheme)) {
192 return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
193 }
194 return false;
195 }
196
197 } // namespace
198
199 FacetURI::FacetURI() : is_valid_(false) {
200 }
201
202 // static
203 FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
204 std::string canonical_spec;
205 bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
206 return FacetURI(canonical_spec, is_valid);
207 }
208
209 // static
210 FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
211 return FacetURI(canonical_spec, true);
212 }
213
214 bool FacetURI::operator==(const FacetURI& other) const {
215 DCHECK(is_valid_);
216 DCHECK(other.is_valid_);
217 return canonical_spec_ == other.canonical_spec_;
218 }
219
220 bool FacetURI::operator!=(const FacetURI& other) const {
221 DCHECK(is_valid_);
222 DCHECK(other.is_valid_);
223 return canonical_spec_ != other.canonical_spec_;
224 }
225
226 bool FacetURI::operator<(const FacetURI& other) const {
227 DCHECK(is_valid_);
228 DCHECK(other.is_valid_);
229 return canonical_spec_ < other.canonical_spec_;
230 }
231
232 bool FacetURI::operator>(const FacetURI& other) const {
233 DCHECK(is_valid_);
234 DCHECK(other.is_valid_);
235 return canonical_spec_ > other.canonical_spec_;
236 }
237
238 bool FacetURI::IsValidWebFacetURI() const {
239 return is_valid_ && ExtractScheme(canonical_spec_) == url::kHttpsScheme;
240 }
241
242 bool FacetURI::IsValidAndroidFacetURI() const {
243 return is_valid_ && ExtractScheme(canonical_spec_) == kAndroidAppScheme;
244 }
245
246 FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
247 : is_valid_(is_valid), canonical_spec_(canonical_spec) {
248 }
249
250 bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
251 const AffiliatedFacets& b) {
252 if (a.size() != b.size())
253 return false;
254
255 std::vector<FacetURI> a_sorted(a.begin(), a.end());
256 std::vector<FacetURI> b_sorted(b.begin(), b.end());
257 std::sort(a_sorted.begin(), a_sorted.end());
258 std::sort(b_sorted.begin(), b_sorted.end());
259 return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
260 }
261
262 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698