Commit f13dee83 authored by Javier Ernesto Flores Robles's avatar Javier Ernesto Flores Robles Committed by Commit Bot

[iOS][Credential-Provider] Set encrypted password

Adds to the password form the encrypted password when fetching from the
password store or passing through a change list.

On iOS this corresponds to the UUID used to retrieve the passwords
from the keychain services.

This is needed in order to fetch the passwords in Credential Provider
without duplicating the entries in the keychain.

Bug: 1066803
Change-Id: I921ad33cdc7579835059a4ff8768761ac5e6bf86
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2142112
Commit-Queue: Javier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758173}
parent 8e9f47f2
......@@ -173,6 +173,11 @@ struct PasswordForm {
// When parsing an HTML form, this is typically empty.
base::string16 password_value;
// The current encrypted password. Must be non-empty for PasswordForm
// instances retrieved from the password store or coming in a
// PasswordStoreChange that is not of type REMOVE.
std::string encrypted_password;
// If the form was a sign-up or a change password form, the name of the input
// element corresponding to the new password. Optional, and not persisted.
base::string16 new_password_element;
......
......@@ -160,16 +160,14 @@ struct SQLTableBuilders {
SQLTableBuilder* sync_model_metadata;
};
void BindAddStatement(const PasswordForm& form,
const std::string& encrypted_password,
sql::Statement* s) {
void BindAddStatement(const PasswordForm& form, sql::Statement* s) {
s->BindString(COLUMN_ORIGIN_URL, form.origin.spec());
s->BindString(COLUMN_ACTION_URL, form.action.spec());
s->BindString16(COLUMN_USERNAME_ELEMENT, form.username_element);
s->BindString16(COLUMN_USERNAME_VALUE, form.username_value);
s->BindString16(COLUMN_PASSWORD_ELEMENT, form.password_element);
s->BindBlob(COLUMN_PASSWORD_VALUE, encrypted_password.data(),
static_cast<int>(encrypted_password.length()));
s->BindBlob(COLUMN_PASSWORD_VALUE, form.encrypted_password.data(),
static_cast<int>(form.encrypted_password.length()));
s->BindString16(COLUMN_SUBMIT_ELEMENT, form.submit_element);
s->BindString(COLUMN_SIGNON_REALM, form.signon_realm);
// The "preferred" column has been deprecated in M81.
......@@ -1016,17 +1014,21 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form,
}
return list;
}
PasswordForm form_with_encrypted_password = form;
form_with_encrypted_password.encrypted_password = encrypted_password;
DCHECK(!add_statement_.empty());
sql::Statement s(
db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str()));
BindAddStatement(form, encrypted_password, &s);
BindAddStatement(form_with_encrypted_password, &s);
int sqlite_error_code;
db_.set_error_callback(base::BindRepeating(&AddCallback, &sqlite_error_code));
const bool success = s.Run();
if (success) {
// If success, the row never existed so password was not changed.
list.emplace_back(PasswordStoreChange::ADD, form, db_.GetLastInsertRowId(),
list.emplace_back(PasswordStoreChange::ADD,
std::move(form_with_encrypted_password),
db_.GetLastInsertRowId(),
/*password_changed=*/false);
return list;
}
......@@ -1040,11 +1042,12 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form,
int old_primary_key = GetPrimaryKey(form);
s.Assign(
db_.GetCachedStatement(SQL_FROM_HERE, add_replace_statement_.c_str()));
BindAddStatement(form, encrypted_password, &s);
BindAddStatement(form_with_encrypted_password, &s);
if (s.Run()) {
list.emplace_back(PasswordStoreChange::REMOVE, form, old_primary_key);
list.emplace_back(PasswordStoreChange::ADD, form, db_.GetLastInsertRowId(),
password_changed);
list.emplace_back(PasswordStoreChange::ADD,
std::move(form_with_encrypted_password),
db_.GetLastInsertRowId(), password_changed);
} else if (error) {
if (sqlite_error_code == 19 /*SQLITE_CONSTRAINT*/) {
*error = AddLoginError::kConstraintViolation;
......@@ -1056,25 +1059,6 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form,
return list;
}
PasswordStoreChangeList LoginDatabase::AddBlacklistedLoginForTesting(
const PasswordForm& form) {
DCHECK(form.blacklisted_by_user);
PasswordStoreChangeList list;
std::string encrypted_password;
if (EncryptedString(form.password_value, &encrypted_password) !=
ENCRYPTION_RESULT_SUCCESS)
return list;
DCHECK(!add_statement_.empty());
sql::Statement s(
db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str()));
BindAddStatement(form, encrypted_password, &s);
if (s.Run())
list.emplace_back(PasswordStoreChange::ADD, form, db_.GetLastInsertRowId());
return list;
}
PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form,
UpdateLoginError* error) {
TRACE_EVENT0("passwords", "LoginDatabase::UpdateLogin");
......@@ -1152,8 +1136,11 @@ PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form,
PasswordStoreChangeList list;
if (db_.GetLastChangeCount()) {
list.emplace_back(PasswordStoreChange::UPDATE, form, GetPrimaryKey(form),
password_changed);
PasswordForm form_with_encrypted_password = form;
form_with_encrypted_password.encrypted_password = encrypted_password;
list.emplace_back(PasswordStoreChange::UPDATE,
std::move(form_with_encrypted_password),
GetPrimaryKey(form), password_changed);
} else if (error) {
*error = UpdateLoginError::kNoUpdatedRecords;
}
......@@ -1317,6 +1304,7 @@ LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
form->username_value = s.ColumnString16(COLUMN_USERNAME_VALUE);
form->password_element = s.ColumnString16(COLUMN_PASSWORD_ELEMENT);
form->password_value = decrypted_password;
form->encrypted_password = encrypted_password;
form->submit_element = s.ColumnString16(COLUMN_SUBMIT_ELEMENT);
tmp = s.ColumnString(COLUMN_SIGNON_REALM);
form->signon_realm = tmp;
......
......@@ -84,12 +84,6 @@ class LoginDatabase : public PasswordStoreSync::MetadataStore {
AddLoginError* error = nullptr)
WARN_UNUSED_RESULT;
// This function does the same thing as AddLogin() with the difference that
// doesn't check if a site is already blacklisted before adding it. This is
// needed for tests that will require to have duplicates in the database.
PasswordStoreChangeList AddBlacklistedLoginForTesting(
const autofill::PasswordForm& form) WARN_UNUSED_RESULT;
// Updates existing password form. Returns the list of applied changes ({},
// {UPDATE}). The password is looked up by the tuple {origin,
// username_element, username_value, password_element, signon_realm}. These
......
......@@ -103,6 +103,37 @@ TEST_F(LoginDatabaseIOSTest, KeychainStorage) {
}
}
TEST_F(LoginDatabaseIOSTest, AddLogin) {
ASSERT_EQ(0U, GetKeychainSize());
PasswordForm form;
form.origin = GURL("http://0.com");
form.signon_realm = "http://www.example.com/";
form.action = GURL("http://www.example.com/action");
form.password_element = base::ASCIIToUTF16("pwd");
form.password_value = base::ASCIIToUTF16("example");
password_manager::PasswordStoreChangeList changes = login_db_->AddLogin(form);
std::string encrypted_password = changes[0].form().encrypted_password;
ASSERT_FALSE(encrypted_password.empty());
ASSERT_EQ(1U, GetKeychainSize());
CFStringRef cf_encrypted_password = CFStringCreateWithCString(
kCFAllocatorDefault, encrypted_password.c_str(), kCFStringEncodingUTF8);
ScopedCFTypeRef<CFMutableDictionaryRef> query(
CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
CFDictionarySetValue(query, kSecAttrAccount, cf_encrypted_password);
CFTypeRef result;
EXPECT_EQ(errSecSuccess, SecItemCopyMatching(query, &result));
CFRelease(cf_encrypted_password);
CFRelease(result);
}
TEST_F(LoginDatabaseIOSTest, UpdateLogin) {
PasswordForm form;
form.origin = GURL("http://0.com");
......
......@@ -1442,14 +1442,12 @@ TEST_F(LoginDatabaseTest, ReportMetricsTest) {
password_form.origin = GURL("http://rsolomakhin.github.io/autofill/123");
password_form.signon_realm = "http://rsolomakhin.github.io/";
password_form.blacklisted_by_user = true;
EXPECT_EQ(AddChangeForForm(password_form),
db().AddBlacklistedLoginForTesting(password_form));
EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
password_form.origin = GURL("https://rsolomakhin.github.io/autofill/1234");
password_form.signon_realm = "https://rsolomakhin.github.io/";
password_form.blacklisted_by_user = true;
EXPECT_EQ(AddChangeForForm(password_form),
db().AddBlacklistedLoginForTesting(password_form));
EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
StatisticsTable& stats_table = db().stats_table();
InteractionsStats stats;
......@@ -2433,4 +2431,75 @@ TEST_F(LoginDatabaseTest, GetLoginsByPassword) {
EXPECT_THAT(forms, UnorderedElementsAre(Pointee(form1), Pointee(form3)));
}
// Test encrypted passwords are present in add change lists.
TEST_F(LoginDatabaseTest, EncryptedPasswordAdd) {
PasswordForm form;
form.origin = GURL("http://0.com");
form.signon_realm = "http://www.example.com/";
form.action = GURL("http://www.example.com/action");
form.password_element = base::ASCIIToUTF16("pwd");
form.password_value = base::ASCIIToUTF16("example");
password_manager::PasswordStoreChangeList changes = db().AddLogin(form);
ASSERT_EQ(1u, changes.size());
ASSERT_FALSE(changes[0].form().encrypted_password.empty());
}
// Test encrypted passwords are present in add change lists, when the password
// is already in the DB.
TEST_F(LoginDatabaseTest, EncryptedPasswordAddWithReplaceSemantics) {
PasswordForm form;
form.origin = GURL("http://0.com");
form.signon_realm = "http://www.example.com/";
form.action = GURL("http://www.example.com/action");
form.password_element = base::ASCIIToUTF16("pwd");
form.password_value = base::ASCIIToUTF16("example");
ignore_result(db().AddLogin(form));
form.password_value = base::ASCIIToUTF16("secret");
password_manager::PasswordStoreChangeList changes = db().AddLogin(form);
ASSERT_EQ(2u, changes.size());
ASSERT_EQ(password_manager::PasswordStoreChange::Type::ADD,
changes[1].type());
ASSERT_FALSE(changes[1].form().encrypted_password.empty());
}
// Test encrypted passwords are present in update change lists.
TEST_F(LoginDatabaseTest, EncryptedPasswordUpdate) {
PasswordForm form;
form.origin = GURL("http://0.com");
form.signon_realm = "http://www.example.com/";
form.action = GURL("http://www.example.com/action");
form.password_element = base::ASCIIToUTF16("pwd");
form.password_value = base::ASCIIToUTF16("example");
ignore_result(db().AddLogin(form));
form.password_value = base::ASCIIToUTF16("secret");
password_manager::PasswordStoreChangeList changes = db().UpdateLogin(form);
ASSERT_EQ(1u, changes.size());
ASSERT_FALSE(changes[0].form().encrypted_password.empty());
}
// Test encrypted passwords are present when retrieving from DB.
TEST_F(LoginDatabaseTest, GetLoginsEncryptedPassword) {
PasswordForm form;
form.origin = GURL("http://0.com");
form.signon_realm = "http://www.example.com/";
form.action = GURL("http://www.example.com/action");
form.password_element = base::ASCIIToUTF16("pwd");
form.password_value = base::ASCIIToUTF16("example");
password_manager::PasswordStoreChangeList changes = db().AddLogin(form);
ASSERT_EQ(1u, changes.size());
ASSERT_FALSE(changes[0].form().encrypted_password.empty());
std::vector<std::unique_ptr<PasswordForm>> forms;
EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &forms));
ASSERT_EQ(1U, forms.size());
ASSERT_FALSE(forms[0]->encrypted_password.empty());
}
} // namespace password_manager
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