1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/osauth/impl/engines/prefs_pin_engine.h"
#include <memory>
#include "ash/constants/ash_pref_names.h"
#include "base/logging.h"
#include "chromeos/ash/components/cryptohome/auth_factor.h"
#include "chromeos/ash/components/login/auth/public/auth_factors_configuration.h"
#include "chromeos/ash/components/login/auth/public/key.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "chromeos/ash/components/osauth/public/common_types.h"
#include "chromeos/ash/components/osauth/public/cryptohome_core.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user_manager.h"
namespace ash {
PrefsPinEngine::PrefsPinEngine(CryptohomeCore& core, PrefService& pref_service)
: core_(&core), pref_service_(&pref_service) {}
PrefsPinEngine::~PrefsPinEngine() = default;
void PrefsPinEngine::PerformPinAttempt(const std::string& raw_pin) {
// Ignore the attempt if use is not currently enabled.
if (usage_allowed_ != UsageAllowed::kEnabled) {
LOG(ERROR) << "Ignoring legacy PIN attempt as factor is disabled";
return;
}
// Ignore the attempt if this engine is not supported.
if (!is_supported_) {
LOG(ERROR) << "Ignoring legacy PIN attempt as factor is not supported";
return;
}
// Ignore the attempt if the PIN is locked out.
if (IsLockedOut()) {
LOG(ERROR) << "Ignoring legacy PIN attempt as factor is locked out";
return;
}
// Extract the stored PIN & salt. Ignore the attempt if they don't exist.
const std::string salt = pref_service_->GetString(prefs::kQuickUnlockPinSalt);
if (salt.empty()) {
LOG(ERROR) << "Ignoring legacy PIN attempt as user has no PIN salt";
return;
}
const std::string secret =
pref_service_->GetString(prefs::kQuickUnlockPinSecret);
if (secret.empty()) {
LOG(ERROR) << "Ignoring legacy PIN attempt as user has no PIN secret";
return;
}
// Attempt authentication. Take the raw PIN input, do a salted key derivation,
// and compare it to the stored secret.
observer_->OnFactorAttempt(GetFactor());
bool auth_success = false;
Key key(raw_pin);
key.Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
// Either the secret matches and we flag the attempt as a success, or it
// failed and we increment the accumulated failure count.
if (key.GetSecret() == secret) {
auth_success = true;
} else {
pref_service_->SetInteger(
prefs::kQuickUnlockPinFailedAttempts,
pref_service_->GetInteger(prefs::kQuickUnlockPinFailedAttempts) + 1);
}
// If the attempt failed and we are now locked out, signal this.
if (IsLockedOut()) {
observer_->OnLockoutChanged(GetFactor());
}
observer_->OnFactorAttemptResult(GetFactor(), auth_success);
}
AshAuthFactor PrefsPinEngine::GetFactor() const {
return AshAuthFactor::kLegacyPin;
}
void PrefsPinEngine::InitializeCommon(CommonInitCallback callback) {
core_->WaitForService(base::BindOnce(&PrefsPinEngine::OnCryptohomeReady,
weak_factory_.GetWeakPtr(),
std::move(callback)));
}
void PrefsPinEngine::ShutdownCommon(ShutdownCallback callback) {
std::move(callback).Run(GetFactor());
}
void PrefsPinEngine::StartAuthFlow(const AccountId& account,
AuthPurpose purpose,
FactorEngineObserver* observer) {
observer_ = observer;
usage_allowed_ = UsageAllowed::kDisabled;
// TODO(b/271263584): Add a way to start a session with the core without
// actually requiring an underlying session. Currently this is the only way to
// ensure that a proper user context exists.
core_->StartAuthSession({account, purpose}, this);
}
void PrefsPinEngine::UpdateObserver(FactorEngineObserver* observer) {
observer_ = observer;
}
void PrefsPinEngine::CleanUp(CleanupCallback callback) {
// By default, the cleanup phase is no-op because the majority
// of the auth factors do not need to do anything for cleaning up.
// Simply run the callback with the factor type to indicate
// the end of clean-up.
std::move(callback).Run(GetFactor());
}
void PrefsPinEngine::StopAuthFlow(ShutdownCallback callback) {
observer_ = nullptr;
shutdown_callback_ = std::move(callback);
core_->EndAuthSession(this);
}
AuthProofToken PrefsPinEngine::StoreAuthenticationContext() {
return core_->StoreAuthenticationContext();
}
void PrefsPinEngine::SetUsageAllowed(UsageAllowed usage) {
usage_allowed_ = usage;
}
bool PrefsPinEngine::IsDisabledByPolicy() {
return false;
}
bool PrefsPinEngine::IsLockedOut() {
return pref_service_->GetInteger(prefs::kQuickUnlockPinFailedAttempts) >=
kMaximumUnlockAttempts;
}
bool PrefsPinEngine::IsFactorSpecificRestricted() {
return false;
}
void PrefsPinEngine::OnSuccessfulAuthentiation() {
pref_service_->SetInteger(prefs::kQuickUnlockPinFailedAttempts, 0);
}
void PrefsPinEngine::OnCryptohomeAuthSessionStarted() {
// If cryptohome does not support PINs, then this engine is supported.
const AuthFactorsConfiguration& config =
core_->GetCurrentContext()->GetAuthFactorsConfiguration();
if (!config.get_supported_factors().Has(cryptohome::AuthFactorType::kPin)) {
is_supported_ = true;
observer_->OnFactorPresenceChecked(GetFactor(), true);
return;
}
observer_->OnFactorPresenceChecked(GetFactor(), false);
}
void PrefsPinEngine::OnAuthSessionStartFailure() {}
void PrefsPinEngine::OnAuthFactorUpdate(cryptohome::AuthFactorRef factor) {}
void PrefsPinEngine::OnCryptohomeAuthSessionFinished() {
std::move(shutdown_callback_).Run(GetFactor());
}
void PrefsPinEngine::OnCryptohomeReady(CommonInitCallback callback,
bool service_available) {
if (!service_available) {
LOG(ERROR) << "cryptohomed not started, Factor "
<< static_cast<int>(GetFactor()) << " is not available";
return;
}
std::move(callback).Run(GetFactor());
}
PrefsPinEngineFactory::PrefsPinEngineFactory(PrefService& local_state)
: local_state_(&local_state) {}
AshAuthFactor PrefsPinEngineFactory::GetFactor() {
return AshAuthFactor::kLegacyPin;
}
std::unique_ptr<AuthFactorEngine> PrefsPinEngineFactory::CreateEngine(
AuthHubMode mode) {
// The legacy PIN engine is only available in-session.
if (mode == AuthHubMode::kInSession) {
auto* core = CryptohomeCore::Get();
CHECK(core);
return std::make_unique<PrefsPinEngine>(*core, *local_state_);
}
return nullptr;
}
} // namespace ash
|