blob: f7522f210c336af008750d8abc0f06ccd0e0ac7b [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "cryptohome/challenge_credentials/challenge_credentials_helper_impl.h"
#include <utility>
#include <base/bind.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include <libhwsec/status.h>
#include "cryptohome/challenge_credentials/challenge_credentials_decrypt_operation.h"
#include "cryptohome/challenge_credentials/challenge_credentials_generate_new_operation.h"
#include "cryptohome/challenge_credentials/challenge_credentials_operation.h"
#include "cryptohome/challenge_credentials/challenge_credentials_verify_key_operation.h"
#include "cryptohome/key_challenge_service.h"
#include "cryptohome/signature_sealing_backend.h"
using brillo::Blob;
using cryptohome::error::CryptohomeTPMError;
using hwsec::TPMError;
using hwsec::TPMErrorBase;
using hwsec::TPMRetryAction;
using hwsec_foundation::error::CreateError;
using hwsec_foundation::error::WrapError;
using hwsec_foundation::status::StatusChain;
namespace cryptohome {
namespace {
bool IsOperationFailureTransient(
const StatusChain<CryptohomeTPMError>& status) {
TPMRetryAction action = status->ToTPMRetryAction();
return action == TPMRetryAction::kCommunication ||
action == TPMRetryAction::kLater;
}
} // namespace
ChallengeCredentialsHelperImpl::ChallengeCredentialsHelperImpl(
Tpm* tpm, const Blob& delegate_blob, const Blob& delegate_secret)
: tpm_(tpm),
delegate_blob_(delegate_blob),
delegate_secret_(delegate_secret) {
DCHECK(tpm_);
}
ChallengeCredentialsHelperImpl::~ChallengeCredentialsHelperImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void ChallengeCredentialsHelperImpl::GenerateNew(
const std::string& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
const std::string& obfuscated_username,
std::unique_ptr<KeyChallengeService> key_challenge_service,
GenerateNewCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
CancelRunningOperation();
key_challenge_service_ = std::move(key_challenge_service);
operation_ = std::make_unique<ChallengeCredentialsGenerateNewOperation>(
key_challenge_service_.get(), tpm_, delegate_blob_, delegate_secret_,
account_id, public_key_info, obfuscated_username,
base::BindOnce(&ChallengeCredentialsHelperImpl::OnGenerateNewCompleted,
base::Unretained(this), std::move(callback)));
operation_->Start();
}
void ChallengeCredentialsHelperImpl::Decrypt(
const std::string& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
const structure::SignatureChallengeInfo& keyset_challenge_info,
bool locked_to_single_user,
std::unique_ptr<KeyChallengeService> key_challenge_service,
DecryptCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
CancelRunningOperation();
key_challenge_service_ = std::move(key_challenge_service);
StartDecryptOperation(account_id, public_key_info, keyset_challenge_info,
locked_to_single_user, 1 /* attempt_number */,
std::move(callback));
}
void ChallengeCredentialsHelperImpl::VerifyKey(
const std::string& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
std::unique_ptr<KeyChallengeService> key_challenge_service,
VerifyKeyCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
CancelRunningOperation();
key_challenge_service_ = std::move(key_challenge_service);
operation_ = std::make_unique<ChallengeCredentialsVerifyKeyOperation>(
key_challenge_service_.get(), tpm_, account_id, public_key_info,
base::BindOnce(&ChallengeCredentialsHelperImpl::OnVerifyKeyCompleted,
base::Unretained(this), std::move(callback)));
operation_->Start();
}
void ChallengeCredentialsHelperImpl::StartDecryptOperation(
const std::string& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
const structure::SignatureChallengeInfo& keyset_challenge_info,
bool locked_to_single_user,
int attempt_number,
DecryptCallback callback) {
DCHECK(!operation_);
operation_ = std::make_unique<ChallengeCredentialsDecryptOperation>(
key_challenge_service_.get(), tpm_, delegate_blob_, delegate_secret_,
account_id, public_key_info, keyset_challenge_info, locked_to_single_user,
base::BindOnce(&ChallengeCredentialsHelperImpl::OnDecryptCompleted,
base::Unretained(this), account_id, public_key_info,
keyset_challenge_info, locked_to_single_user,
attempt_number, std::move(callback)));
operation_->Start();
}
void ChallengeCredentialsHelperImpl::CancelRunningOperation() {
// Destroy the previous Operation before instantiating a new one, to keep the
// resource usage constrained (for example, there must be only one instance of
// SignatureSealingBackend::UnsealingSession at a time).
if (operation_) {
DLOG(INFO) << "Cancelling an old challenge-response credentials operation";
operation_->Abort();
operation_.reset();
// It's illegal for the consumer code to request a new operation in
// immediate response to completion of a previous one.
DCHECK(!operation_);
}
}
void ChallengeCredentialsHelperImpl::OnGenerateNewCompleted(
GenerateNewCallback original_callback,
std::unique_ptr<structure::SignatureChallengeInfo> signature_challenge_info,
std::unique_ptr<brillo::SecureBlob> passkey) {
DCHECK(thread_checker_.CalledOnValidThread());
CancelRunningOperation();
std::move(original_callback)
.Run(std::move(signature_challenge_info), std::move(passkey));
}
void ChallengeCredentialsHelperImpl::OnDecryptCompleted(
const std::string& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
const structure::SignatureChallengeInfo& keyset_challenge_info,
bool locked_to_single_user,
int attempt_number,
DecryptCallback original_callback,
StatusChain<CryptohomeTPMError> status,
std::unique_ptr<brillo::SecureBlob> passkey) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(passkey == nullptr, !status.ok());
CancelRunningOperation();
if (!status.ok() && IsOperationFailureTransient(status) &&
attempt_number < kRetryAttemptCount) {
LOG(WARNING) << "Retrying the decryption operation after transient error: "
<< status;
StartDecryptOperation(account_id, public_key_info, keyset_challenge_info,
locked_to_single_user, attempt_number + 1,
std::move(original_callback));
} else {
if (!status.ok()) {
LOG(ERROR) << "Decryption completed with error: " << status;
}
std::move(original_callback).Run(std::move(passkey));
}
}
void ChallengeCredentialsHelperImpl::OnVerifyKeyCompleted(
VerifyKeyCallback original_callback, bool is_key_valid) {
DCHECK(thread_checker_.CalledOnValidThread());
CancelRunningOperation();
std::move(original_callback).Run(is_key_valid);
}
} // namespace cryptohome