Commit b62c659f authored by dvadym's avatar dvadym Committed by Commit bot

Implementation of sync password reuse checking.

Comparison of hashes of all suffixes of input with the sync password hash is implemented.

BUG=657041

Review-Url: https://codereview.chromium.org/2847743002
Cr-Commit-Position: refs/heads/master@{#467970}
parent aa95dd2a
......@@ -49,7 +49,7 @@ void PasswordReuseDetectionManager::OnKeyPressed(const base::string16& text) {
void PasswordReuseDetectionManager::OnReuseFound(
const base::string16& password,
const std::string& saved_domain,
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
......@@ -57,7 +57,7 @@ void PasswordReuseDetectionManager::OnReuseFound(
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND,
saved_domain);
legitimate_domain);
}
metrics_util::LogPasswordReuse(
......
......@@ -26,7 +26,7 @@ class PasswordReuseDetectionManager : public PasswordReuseDetectorConsumer {
// PasswordReuseDetectorConsumer
void OnReuseFound(const base::string16& password,
const std::string& saved_domain,
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) override;
......
......@@ -7,8 +7,14 @@
#include <algorithm>
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "url/origin.h"
using url::Origin;
namespace password_manager {
......@@ -62,26 +68,67 @@ void PasswordReuseDetector::CheckReuse(
if (input.size() < kMinPasswordLengthToCheck)
return;
if (CheckSyncPasswordReuse(input, domain, consumer))
return;
if (CheckSavedPasswordReuse(input, domain, consumer))
return;
}
bool PasswordReuseDetector::CheckSyncPasswordReuse(
const base::string16& input,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer) {
if (!sync_password_hash_.has_value())
return false;
const Origin gaia_origin(GaiaUrls::GetInstance()->gaia_url().GetOrigin());
if (Origin(GURL(domain)).IsSameOriginWith(gaia_origin))
return false;
// Check that some suffix of |input| has the same hash as the sync password.
for (size_t i = 0; i + kMinPasswordLengthToCheck <= input.size(); ++i) {
base::StringPiece16 input_suffix(input.c_str() + i, input.size() - i);
if (password_manager_util::Calculate37BitsOfSHA256Hash(input_suffix) ==
sync_password_hash_.value()) {
consumer->OnReuseFound(input_suffix.as_string(), gaia_origin.host(), 1,
0);
return true;
}
}
return false;
}
bool PasswordReuseDetector::CheckSavedPasswordReuse(
const base::string16& input,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer) {
const std::string registry_controlled_domain =
GetRegistryControlledDomain(GURL(domain));
auto passwords_iterator = FindSavedPassword(input);
if (passwords_iterator == passwords_.end())
return;
return false;
const std::set<std::string>& domains = passwords_iterator->second;
DCHECK(!domains.empty());
if (domains.find(registry_controlled_domain) == domains.end()) {
// Return only one domain.
const std::string& saved_domain = *domains.begin();
consumer->OnReuseFound(passwords_iterator->first, saved_domain,
const std::string& legitimate_domain = *domains.begin();
consumer->OnReuseFound(passwords_iterator->first, legitimate_domain,
saved_passwords_, domains.size());
return;
return true;
}
return false;
}
void PasswordReuseDetector::SaveSyncPasswordHash(
const base::string16& password) {
// TODO(crbug.com/657041) Implement saving of sync password hash.
sync_password_hash_ =
password_manager_util::Calculate37BitsOfSHA256Hash(password);
// TODO(crbug.com/657041) Implement saving of sync password hash into
// preferences.
}
void PasswordReuseDetector::AddPassword(const autofill::PasswordForm& form) {
......
......@@ -5,6 +5,7 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
......@@ -12,6 +13,7 @@
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
......@@ -43,7 +45,7 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
void OnLoginsChanged(const PasswordStoreChangeList& changes);
// Checks that some suffix of |input| equals to a password saved on another
// registry controlled domain than |domain|.
// registry controlled domain than |domain| or to a sync password.
// If such suffix is found, |consumer|->OnReuseFound() is called on the same
// thread on which this method is called.
// |consumer| should not be null.
......@@ -62,6 +64,18 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
// Add password from |form| to |passwords_|.
void AddPassword(const autofill::PasswordForm& form);
// Returns true iff a reuse of a sync password is found. If reuse is found it
// is reported to |consumer|.
bool CheckSyncPasswordReuse(const base::string16& input,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer);
// Returns true iff a reuse of a saved password is found. If reuse is found it
// is reported to |consumer|.
bool CheckSavedPasswordReuse(const base::string16& input,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer);
// Returns the iterator to |passwords_| that corresponds to the longest key in
// |passwords_| that is a suffix of |input|. Returns passwords_.end() in case
// when no key in |passwords_| is a prefix of |input|.
......@@ -77,6 +91,8 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
// of times how many different sites it's saved on.
int saved_passwords_ = 0;
base::Optional<uint64_t> sync_password_hash_;
DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetector);
};
......
......@@ -21,11 +21,12 @@ class PasswordReuseDetectorConsumer
virtual ~PasswordReuseDetectorConsumer();
// Called when a password reuse is found.
// |saved_domain| is the domain on which |password| is saved.
// |legitimate_domain| is the domain on which |password| is saved or the sync
// domain if |password| is a sync password.
// |saved_passwords| is total number of passwords stored in Password Manager.
// |number_matches| is a number of sites on which |password| is saved.
virtual void OnReuseFound(const base::string16& password,
const std::string& saved_domain,
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) = 0;
};
......
......@@ -184,6 +184,49 @@ TEST(PasswordReuseDetectorTest, CheckLongestPasswordMatchReturn) {
&mockConsumer);
}
TEST(PasswordReuseDetectorTest, SyncPasswordNoReuse) {
PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
reuse_detector.CheckReuse(ASCIIToUTF16("sync_password"),
"https://accounts.google.com", &mockConsumer);
// Only suffixes are verifed.
reuse_detector.CheckReuse(ASCIIToUTF16("sync_password123"),
"https://evil.com", &mockConsumer);
}
TEST(PasswordReuseDetectorTest, SyncPasswordReuseFound) {
PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
EXPECT_CALL(mockConsumer, OnReuseFound(ASCIIToUTF16("sync_password"),
"accounts.google.com", 1, 0));
reuse_detector.CheckReuse(ASCIIToUTF16("sync_password"), "https://evil.com",
&mockConsumer);
}
TEST(PasswordReuseDetectorTest, SavedPasswordsReuseSyncPasswordAvailable) {
// Check that reuse of saved passwords is detected also if the sync password
// hash is saved.
PasswordReuseDetector reuse_detector;
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
reuse_detector.SaveSyncPasswordHash(ASCIIToUTF16("sync_password"));
EXPECT_CALL(mockConsumer,
OnReuseFound(ASCIIToUTF16("password"), "google.com", 5, 1));
reuse_detector.CheckReuse(ASCIIToUTF16("password"), "https://evil.com",
&mockConsumer);
}
} // namespace
} // namespace password_manager
......@@ -67,13 +67,13 @@ PasswordStore::CheckReuseRequest::~CheckReuseRequest() {}
void PasswordStore::CheckReuseRequest::OnReuseFound(
const base::string16& password,
const std::string& saved_domain,
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) {
origin_task_runner_->PostTask(
FROM_HERE,
base::Bind(&PasswordReuseDetectorConsumer::OnReuseFound, consumer_weak_,
password, saved_domain, saved_passwords, number_matches));
password, legitimate_domain, saved_passwords, number_matches));
}
#endif
......
......@@ -294,7 +294,7 @@ class PasswordStore : protected PasswordStoreSync,
// PasswordReuseDetectorConsumer
void OnReuseFound(const base::string16& password,
const std::string& saved_domain,
const std::string& legitimate_domain,
int saved_passwords,
int number_matches) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment