blob: 61ace5f315a4bf424da93c7ede21f3e1701d5f12 [file] [log] [blame]
// Copyright 2019 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_generate_new_operation.h"
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include <base/optional.h>
#include "cryptohome/challenge_credentials/challenge_credentials_constants.h"
#include "cryptohome/credentials.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/signature_sealed_data.pb.h"
#include "cryptohome/tpm.h"
using brillo::Blob;
using brillo::BlobFromString;
using brillo::BlobToString;
using brillo::CombineBlobs;
using brillo::SecureBlob;
namespace cryptohome {
namespace {
std::vector<ChallengeSignatureAlgorithm> GetSealingAlgorithms(
const ChallengePublicKeyInfo& public_key_info) {
std::vector<ChallengeSignatureAlgorithm> sealing_algorithms;
for (int index = 0; index < public_key_info.signature_algorithm_size();
++index) {
sealing_algorithms.push_back(public_key_info.signature_algorithm(index));
}
return sealing_algorithms;
}
// Returns the signature algorithm that should be used for signing salt from the
// set of algorithms supported by the given key. Returns nullopt when no
// suitable algorithm was found.
base::Optional<ChallengeSignatureAlgorithm> ChooseSaltSignatureAlgorithm(
const ChallengePublicKeyInfo& public_key_info) {
DCHECK(public_key_info.signature_algorithm_size());
base::Optional<ChallengeSignatureAlgorithm> currently_chosen_algorithm;
// Respect the input's algorithm prioritization, with the exception of
// considering SHA-1 as the least preferred option.
for (int index = 0; index < public_key_info.signature_algorithm_size();
++index) {
currently_chosen_algorithm = public_key_info.signature_algorithm(index);
if (*currently_chosen_algorithm != CHALLENGE_RSASSA_PKCS1_V1_5_SHA1)
break;
}
return currently_chosen_algorithm;
}
} // namespace
ChallengeCredentialsGenerateNewOperation::
ChallengeCredentialsGenerateNewOperation(
KeyChallengeService* key_challenge_service,
Tpm* tpm,
const brillo::Blob& delegate_blob,
const brillo::Blob& delegate_secret,
const std::string& account_id,
const KeyData& key_data,
const std::vector<std::map<uint32_t, brillo::Blob>>& pcr_restrictions,
CompletionCallback completion_callback)
: ChallengeCredentialsOperation(key_challenge_service),
tpm_(tpm),
delegate_blob_(delegate_blob),
delegate_secret_(delegate_secret),
account_id_(account_id),
key_data_(key_data),
pcr_restrictions_(pcr_restrictions),
completion_callback_(std::move(completion_callback)),
signature_sealing_backend_(tpm_->GetSignatureSealingBackend()) {
DCHECK_EQ(key_data.type(), KeyData::KEY_TYPE_CHALLENGE_RESPONSE);
}
ChallengeCredentialsGenerateNewOperation::
~ChallengeCredentialsGenerateNewOperation() = default;
void ChallengeCredentialsGenerateNewOperation::Start() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!StartProcessing()) {
LOG(ERROR) << "Failed to start the generation operation";
Abort();
// |this| can be already destroyed at this point.
}
}
void ChallengeCredentialsGenerateNewOperation::Abort() {
DCHECK(thread_checker_.CalledOnValidThread());
// Invalidate weak pointers in order to cancel all jobs that are currently
// waiting, to prevent them from running and consuming resources after our
// abortion (in case |this| doesn't get destroyed immediately).
//
// Note that the already issued challenge requests don't get cancelled, so
// their responses will be just ignored should they arrive later. The request
// cancellation is not supported by the challenges IPC API currently, neither
// it is supported by the API for smart card drivers in Chrome OS.
weak_ptr_factory_.InvalidateWeakPtrs();
Complete(&completion_callback_, nullptr /* credentials */);
// |this| can be already destroyed at this point.
}
bool ChallengeCredentialsGenerateNewOperation::StartProcessing() {
if (!signature_sealing_backend_) {
LOG(ERROR) << "Signature sealing is disabled";
return false;
}
if (!key_data_.challenge_response_key_size()) {
LOG(ERROR) << "Missing challenge-response key information";
return false;
}
if (key_data_.challenge_response_key_size() > 1) {
LOG(ERROR)
<< "Using multiple challenge-response keys at once is unsupported";
return false;
}
public_key_info_ = key_data_.challenge_response_key(0);
if (!public_key_info_.signature_algorithm_size()) {
LOG(ERROR) << "The key does not support any signature algorithm";
return false;
}
if (!GenerateSalt() || !StartGeneratingSaltSignature())
return false;
// TODO(crbug.com/842791): This is buggy: |this| may be already deleted by
// that point, in case when the salt's challenge request failed synchronously.
if (!CreateTpmProtectedSecret())
return false;
ProceedIfComputationsDone();
return true;
}
bool ChallengeCredentialsGenerateNewOperation::GenerateSalt() {
Blob salt_random_bytes;
if (!tpm_->GetRandomDataBlob(kChallengeCredentialsSaltRandomByteCount,
&salt_random_bytes)) {
LOG(ERROR) << "Failed to generate random bytes for the salt";
return false;
}
DCHECK_EQ(kChallengeCredentialsSaltRandomByteCount, salt_random_bytes.size());
// IMPORTANT: Make sure the salt is prefixed with a constant. See the comment
// on GetChallengeCredentialsSaltConstantPrefix() for details.
salt_ = CombineBlobs(
{GetChallengeCredentialsSaltConstantPrefix(), salt_random_bytes});
return true;
}
bool ChallengeCredentialsGenerateNewOperation::StartGeneratingSaltSignature() {
DCHECK(!salt_.empty());
base::Optional<ChallengeSignatureAlgorithm> chosen_salt_signature_algorithm =
ChooseSaltSignatureAlgorithm(public_key_info_);
if (!chosen_salt_signature_algorithm) {
LOG(ERROR) << "Failed to choose salt signature algorithm";
return false;
}
salt_signature_algorithm_ = *chosen_salt_signature_algorithm;
MakeKeySignatureChallenge(
account_id_, BlobFromString(public_key_info_.public_key_spki_der()),
salt_, salt_signature_algorithm_,
base::Bind(
&ChallengeCredentialsGenerateNewOperation::OnSaltChallengeResponse,
weak_ptr_factory_.GetWeakPtr()));
return true;
}
bool ChallengeCredentialsGenerateNewOperation::CreateTpmProtectedSecret() {
SecureBlob local_tpm_protected_secret_value;
if (!signature_sealing_backend_->CreateSealedSecret(
BlobFromString(public_key_info_.public_key_spki_der()),
GetSealingAlgorithms(public_key_info_), pcr_restrictions_,
delegate_blob_, delegate_secret_, &local_tpm_protected_secret_value,
&tpm_sealed_secret_data_)) {
LOG(ERROR) << "Failed to create TPM-protected secret";
return false;
}
DCHECK(local_tpm_protected_secret_value.size());
tpm_protected_secret_value_ =
std::make_unique<SecureBlob>(std::move(local_tpm_protected_secret_value));
return true;
}
void ChallengeCredentialsGenerateNewOperation::OnSaltChallengeResponse(
std::unique_ptr<Blob> salt_signature) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!salt_signature) {
LOG(ERROR) << "Salt signature challenge failed";
Abort();
// |this| can be already destroyed at this point.
return;
}
salt_signature_ = std::move(salt_signature);
ProceedIfComputationsDone();
}
void ChallengeCredentialsGenerateNewOperation::ProceedIfComputationsDone() {
if (!salt_signature_ || !tpm_protected_secret_value_)
return;
auto credentials = std::make_unique<Credentials>(
account_id_,
ConstructPasskey(*tpm_protected_secret_value_, *salt_signature_));
credentials->set_key_data(key_data_);
credentials->set_challenge_credentials_keyset_info(
ConstructKeysetSignatureChallengeInfo());
Complete(&completion_callback_, std::move(credentials));
// |this| can be already destroyed at this point.
}
ChallengeCredentialsGenerateNewOperation::KeysetSignatureChallengeInfo
ChallengeCredentialsGenerateNewOperation::
ConstructKeysetSignatureChallengeInfo() const {
KeysetSignatureChallengeInfo keyset_signature_challenge_info;
keyset_signature_challenge_info.set_public_key_spki_der(
public_key_info_.public_key_spki_der());
*keyset_signature_challenge_info.mutable_sealed_secret() =
tpm_sealed_secret_data_;
keyset_signature_challenge_info.set_salt(BlobToString(salt_));
keyset_signature_challenge_info.set_salt_signature_algorithm(
salt_signature_algorithm_);
return keyset_signature_challenge_info;
}
} // namespace cryptohome