blob: 88a371e845e1e8b3bb54bc4cfd7ffb108db1a00b [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.
// Tests for the ChallengeCredentialsHelperImpl class.
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/check.h>
#include <brillo/secure_blob.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cryptohome/challenge_credentials/challenge_credentials_constants.h"
#include "cryptohome/challenge_credentials/challenge_credentials_helper_impl.h"
#include "cryptohome/challenge_credentials/challenge_credentials_test_utils.h"
#include "cryptohome/credentials.h"
#include "cryptohome/crypto/sha.h"
#include "cryptohome/key.pb.h"
#include "cryptohome/mock_key_challenge_service.h"
#include "cryptohome/mock_signature_sealing_backend.h"
#include "cryptohome/mock_tpm.h"
#include "cryptohome/rpc.pb.h"
#include "cryptohome/signature_sealed_data.pb.h"
#include "cryptohome/signature_sealing_backend.h"
#include "cryptohome/signature_sealing_backend_test_utils.h"
#include "cryptohome/vault_keyset.pb.h"
using brillo::Blob;
using brillo::BlobToString;
using brillo::CombineBlobs;
using brillo::SecureBlob;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
using testing::StrictMock;
using testing::Values;
namespace cryptohome {
using KeysetSignatureChallengeInfo =
SerializedVaultKeyset::SignatureChallengeInfo;
namespace {
KeyData MakeKeyData(
const Blob& set_public_key_spki_der,
const std::vector<ChallengeSignatureAlgorithm>& key_algorithms) {
KeyData key_data;
key_data.set_type(KeyData::KEY_TYPE_CHALLENGE_RESPONSE);
ChallengePublicKeyInfo* const public_key_info =
key_data.add_challenge_response_key();
public_key_info->set_public_key_spki_der(
BlobToString(set_public_key_spki_der));
for (auto key_algorithm : key_algorithms)
public_key_info->add_signature_algorithm(key_algorithm);
return key_data;
}
KeysetSignatureChallengeInfo MakeFakeKeysetChallengeInfo(
const Blob& public_key_spki_der,
const Blob& salt,
ChallengeSignatureAlgorithm salt_challenge_algorithm) {
KeysetSignatureChallengeInfo keyset_challenge_info;
keyset_challenge_info.set_public_key_spki_der(
BlobToString(public_key_spki_der));
*keyset_challenge_info.mutable_sealed_secret() =
MakeFakeSignatureSealedData(public_key_spki_der);
keyset_challenge_info.set_salt(BlobToString(salt));
keyset_challenge_info.set_salt_signature_algorithm(salt_challenge_algorithm);
return keyset_challenge_info;
}
// Base fixture class that provides some common constants, helpers and mocks for
// testing ChallengeCredentialsHelperImpl.
class ChallengeCredentialsHelperImplTestBase : public testing::Test {
protected:
ChallengeCredentialsHelperImplTestBase()
: challenge_credentials_helper_(&tpm_, kDelegateBlob, kDelegateSecret) {}
void PrepareSignatureSealingBackend(bool enabled) {
SignatureSealingBackend* const return_value =
enabled ? &sealing_backend_ : nullptr;
EXPECT_CALL(tpm_, GetSignatureSealingBackend())
.WillRepeatedly(Return(return_value));
}
// Starts the asynchronous GenerateNew() operation. The result, once the
// operation completes, will be stored in |generate_new_result|.
void CallGenerateNew(
const std::vector<ChallengeSignatureAlgorithm>& key_algorithms,
std::unique_ptr<ChallengeCredentialsGenerateNewResult>*
generate_new_result) {
DCHECK(challenge_service_);
const KeyData key_data = MakeKeyData(kPublicKeySpkiDer, key_algorithms);
challenge_credentials_helper_.GenerateNew(
kUserEmail, key_data, kPcrRestrictions, std::move(challenge_service_),
MakeChallengeCredentialsGenerateNewResultWriter(generate_new_result));
}
// Starts the asynchronous Decrypt() operation. The result, once the
// operation completes, will be stored in |decrypt_result|.
void CallDecrypt(
const std::vector<ChallengeSignatureAlgorithm>& key_algorithms,
ChallengeSignatureAlgorithm salt_challenge_algorithm,
const Blob& salt,
std::unique_ptr<ChallengeCredentialsDecryptResult>* decrypt_result) {
DCHECK(challenge_service_);
const KeyData key_data = MakeKeyData(kPublicKeySpkiDer, key_algorithms);
const KeysetSignatureChallengeInfo keyset_challenge_info =
MakeFakeKeysetChallengeInfo(kPublicKeySpkiDer, salt,
salt_challenge_algorithm);
challenge_credentials_helper_.Decrypt(
kUserEmail, key_data, keyset_challenge_info,
std::move(challenge_service_),
MakeChallengeCredentialsDecryptResultWriter(decrypt_result));
}
// Starts the Decrypt() operation without observing the challenge requests it
// makes or its result. Intended to be used for testing the corner case of
// starting an operation before the previous one is completed.
void StartSurplusOperation() {
// Use different parameters here, to avoid clashing with mocks set up for
// the normal operation.
constexpr ChallengeSignatureAlgorithm kLocalAlgorithm =
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256;
const Blob kLocalPublicKeySpkiDer =
CombineBlobs({kPublicKeySpkiDer, Blob(1)});
auto unsealing_mocker =
MakeUnsealingMocker({kLocalAlgorithm} /* key_algorithms */,
kLocalAlgorithm /* unsealing_algorithm */);
unsealing_mocker->set_public_key_spki_der(kLocalPublicKeySpkiDer);
unsealing_mocker->SetUpUnsealingNotCalledMock();
auto mock_key_challenge_service =
std::make_unique<MockKeyChallengeService>();
EXPECT_CALL(*mock_key_challenge_service, ChallengeKeyMovable(_, _, _))
.Times(AnyNumber());
const KeyData key_data = MakeKeyData(
kLocalPublicKeySpkiDer, {kLocalAlgorithm} /* key_algorithms */);
const KeysetSignatureChallengeInfo keyset_challenge_info =
MakeFakeKeysetChallengeInfo(
kLocalPublicKeySpkiDer, kSalt,
kLocalAlgorithm /* salt_challenge_algorithm */);
challenge_credentials_helper_.Decrypt(
kUserEmail, key_data, keyset_challenge_info,
std::move(mock_key_challenge_service),
base::Bind([](std::unique_ptr<Credentials>) {}));
}
// Assert that the given GenerateNew() operation result is a valid success
// result.
void VerifySuccessfulGenerateNewResult(
const ChallengeCredentialsGenerateNewResult& generate_new_result) const {
VerifySuccessfulChallengeCredentialsGenerateNewResult(
generate_new_result, kUserEmail,
SecureBlob(kPasskey.begin(), kPasskey.end()));
}
// Assert that the given Decrypt() operation result is a valid success result.
void VerifySuccessfulDecryptResult(
const ChallengeCredentialsDecryptResult& decrypt_result) const {
VerifySuccessfulChallengeCredentialsDecryptResult(
decrypt_result, kUserEmail,
SecureBlob(kPasskey.begin(), kPasskey.end()));
}
// Returns a helper object that aids mocking of the sealed secret creation
// functionality (SignatureSealingBackend::CreateSealedSecret()).
std::unique_ptr<SignatureSealedCreationMocker> MakeSealedCreationMocker(
const std::vector<ChallengeSignatureAlgorithm>& key_algorithms) {
auto mocker =
std::make_unique<SignatureSealedCreationMocker>(&sealing_backend_);
mocker->set_public_key_spki_der(kPublicKeySpkiDer);
mocker->set_key_algorithms(key_algorithms);
mocker->set_pcr_restrictions(kPcrRestrictions);
mocker->set_delegate_blob(kDelegateBlob);
mocker->set_delegate_secret(kDelegateSecret);
mocker->set_secret_value(kTpmProtectedSecret);
return mocker;
}
// Returns a helper object that aids mocking of the secret unsealing
// functionality (SignatureSealingBackend::CreateUnsealingSession() et al.).
std::unique_ptr<SignatureSealedUnsealingMocker> MakeUnsealingMocker(
const std::vector<ChallengeSignatureAlgorithm>& key_algorithms,
ChallengeSignatureAlgorithm unsealing_algorithm) {
auto mocker =
std::make_unique<SignatureSealedUnsealingMocker>(&sealing_backend_);
mocker->set_public_key_spki_der(kPublicKeySpkiDer);
mocker->set_key_algorithms(key_algorithms);
mocker->set_delegate_blob(kDelegateBlob);
mocker->set_delegate_secret(kDelegateSecret);
mocker->set_chosen_algorithm(unsealing_algorithm);
mocker->set_challenge_value(kUnsealingChallengeValue);
mocker->set_challenge_signature(kUnsealingChallengeSignature);
mocker->set_secret_value(kTpmProtectedSecret);
return mocker;
}
// Sets up an expectation that the salt challenge request will be issued via
// |challenge_service_|.
void ExpectSaltChallenge(
ChallengeSignatureAlgorithm salt_challenge_algorithm) {
salt_challenge_mock_controller_.ExpectSignatureChallenge(
kUserEmail, kPublicKeySpkiDer, kSalt, salt_challenge_algorithm);
}
// Whether the salt challenge request has been started.
bool is_salt_challenge_requested() const {
return salt_challenge_mock_controller_.is_challenge_requested();
}
// Injects a simulated successful response for the currently running salt
// challenge request.
void SimulateSaltChallengeResponse() {
salt_challenge_mock_controller_.SimulateSignatureChallengeResponse(
kSaltSignature);
}
// Injects a simulated failure response for the currently running salt
// challenge request.
void SimulateSaltChallengeFailure() {
salt_challenge_mock_controller_.SimulateFailureResponse();
}
// Sets up an expectation that the secret unsealing challenge request will be
// issued via |challenge_service_|.
void ExpectUnsealingChallenge(
ChallengeSignatureAlgorithm unsealing_algorithm) {
unsealing_challenge_mock_controller_.ExpectSignatureChallenge(
kUserEmail, kPublicKeySpkiDer, kUnsealingChallengeValue,
unsealing_algorithm);
}
// Whether the secret unsealing challenge request has been started.
bool is_unsealing_challenge_requested() const {
return unsealing_challenge_mock_controller_.is_challenge_requested();
}
// Injects a simulated successful response for the currently running secret
// unsealing challenge request.
void SimulateUnsealingChallengeResponse() {
unsealing_challenge_mock_controller_.SimulateSignatureChallengeResponse(
kUnsealingChallengeSignature);
}
// Injects a simulated failure response for the currently running secret
// unsealing challenge request.
void SimulateUnsealingChallengeFailure() {
unsealing_challenge_mock_controller_.SimulateFailureResponse();
}
// Sets up a mock for the successful salt generation.
void SetSuccessfulSaltGenerationMock() {
EXPECT_CALL(tpm_,
GetRandomDataBlob(kChallengeCredentialsSaltRandomByteCount, _))
.WillOnce(DoAll(SetArgPointee<1>(kSaltRandomPart), Return(true)));
}
// Sets up a mock for the failure during salt generation.
void SetFailingSaltGenerationMock() {
EXPECT_CALL(tpm_,
GetRandomDataBlob(kChallengeCredentialsSaltRandomByteCount, _))
.WillOnce(Return(false));
}
protected:
// Constants which are passed as fake data inputs to the
// ChallengeCredentialsHelperImpl methods:
// Fake TPM delegate. It's supplied to the ChallengeCredentialsHelperImpl
// constructor. Then it's verified to be passed into SignatureSealingBackend
// methods.
const Blob kDelegateBlob{{1, 1, 1}};
const Blob kDelegateSecret{{2, 2, 2}};
// Fake user e-mail. It's supplied to the ChallengeCredentialsHelperImpl
// operation methods. Then it's verified to be passed alongside challenge
// requests made via KeyChallengeService, and to be present in the resulting
// Credentials.
const std::string kUserEmail = "foo@example.com";
// Fake Subject Public Key Information of the challenged cryptographic key.
// It's supplied to the ChallengeCredentialsHelperImpl operation methods as a
// field of both |key_data| and |keyset_challenge_info| parameters. Then it's
// verified to be passed into SignatureSealingBackend methods and to be used
// for challenge requests made via KeyChallengeService.
const Blob kPublicKeySpkiDer{{3, 3, 3}};
// Fake random part of the salt. When testing the GenerateNew() operation,
// it's injected as a fake result of the TPM GetRandomDataBlob(). It's also
// used as part of the |kSalt| constant in a few other places.
const Blob kSaltRandomPart = Blob(20, 4);
// Fake salt value. It's supplied to the ChallengeCredentialsHelperImpl
// operation methods as a field of the |keyset_challenge_info| parameter. Then
// it's verified to be used as the challenge value for one of requests made
// via KeyChallengeService.
const Blob kSalt = CombineBlobs(
{GetChallengeCredentialsSaltConstantPrefix(), kSaltRandomPart});
// Fake PCR restrictions: a list of maps from PCR indexes to PCR values. It's
// supplied to the GenerateNew() operation. Then it's verified to be passed
// into the SignatureSealingBackend::CreateSealedSecret() method.
const std::vector<std::map<uint32_t, Blob>> kPcrRestrictions{
std::map<uint32_t, Blob>{{0, {9, 9, 9}}, {10, {11, 11, 11}}},
std::map<uint32_t, Blob>{{0, {9, 9, 9}}, {10, {12, 12, 12}}}};
// Constants which are injected as fake data into intermediate steps of the
// ChallengeCredentialsHelperImpl operations:
// Fake signature of |kSalt| using the |salt_challenge_algorithm_| algorithm.
// It's injected as a fake response to the salt challenge request made via
// KeyChallengeService. Then it's implicitly verified to be used for the
// generation of the passkey in the resulting Credentials - see the |kPasskey|
// constant.
const Blob kSaltSignature{{5, 5, 5}};
// Fake challenge value for unsealing the secret. It's injected as a fake
// value returned from SignatureSealingBackend::UnsealingSession. Then it's
// verified to be used as the challenge value for one of requests made via
// KeyChallengeService.
const Blob kUnsealingChallengeValue{{6, 6, 6}};
// Fake signature of |kUnsealingChallengeValue| using the
// |unsealing_algorithm_| algorithm. It's injected as a fake response to the
// unsealing challenge request made via KeyChallengeService. Then it's
// verified to be passed to the Unseal() method of
// SignatureSealingBackend::UnsealingSession.
const Blob kUnsealingChallengeSignature{{7, 7, 7}};
// Fake TPM-protected secret. When testing the GenerateNew() operation, it's
// injected as a fake result of SignatureSealingBackend::CreateSealedSecret()
// method. When testing the Decrypt() operation, it's injected as a fake
// result of the Unseal() method of SignatureSealingBackend::UnsealingSession.
// Also this constant is implicitly verified to be used for the generation of
// the passkey in the resulting Credentials - see the |kPasskey| constant.
const Blob kTpmProtectedSecret{{8, 8, 8}};
// The expected passkey of the resulting Credentials returned from the
// ChallengeCredentialsHelperImpl operations. Its value is derived from the
// injected fake data.
const Blob kPasskey =
CombineBlobs({kTpmProtectedSecret, Sha256(kSaltSignature)});
private:
// Mock objects:
StrictMock<MockSignatureSealingBackend> sealing_backend_;
StrictMock<MockTpm> tpm_;
std::unique_ptr<StrictMock<MockKeyChallengeService>> challenge_service_ =
std::make_unique<StrictMock<MockKeyChallengeService>>();
KeyChallengeServiceMockController salt_challenge_mock_controller_{
challenge_service_.get()};
KeyChallengeServiceMockController unsealing_challenge_mock_controller_{
challenge_service_.get()};
// The tested instance.
ChallengeCredentialsHelperImpl challenge_credentials_helper_;
};
// Base fixture class that uses a single algorithm for simplicity.
class ChallengeCredentialsHelperImplSingleAlgorithmTestBase
: public ChallengeCredentialsHelperImplTestBase {
protected:
// The single algorithm to be used in this test.
static constexpr ChallengeSignatureAlgorithm kAlgorithm =
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256;
};
// Base fixture class that uses a single algorithm and have the sealing backend
// available.
class ChallengeCredentialsHelperImplBasicTest
: public ChallengeCredentialsHelperImplSingleAlgorithmTestBase {
protected:
// The single algorithm to be used in this test.
static constexpr ChallengeSignatureAlgorithm kAlgorithm =
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256;
ChallengeCredentialsHelperImplBasicTest() {
PrepareSignatureSealingBackend(true /* enabled */);
}
};
} // namespace
// Test success of the GenerateNew() operation.
TEST_F(ChallengeCredentialsHelperImplBasicTest, GenerateNewSuccess) {
SetSuccessfulSaltGenerationMock();
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
MakeSealedCreationMocker({kAlgorithm} /* key_algorithms */)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsGenerateNewResult> generate_new_result;
CallGenerateNew({kAlgorithm} /* key_algorithms */, &generate_new_result);
EXPECT_FALSE(generate_new_result);
EXPECT_TRUE(is_salt_challenge_requested());
SimulateSaltChallengeResponse();
ASSERT_TRUE(generate_new_result);
VerifySuccessfulGenerateNewResult(*generate_new_result);
}
// Test failure of the GenerateNew() operation due to failure in salt
// generation.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
GenerateNewFailureInSaltGeneration) {
SetFailingSaltGenerationMock();
std::unique_ptr<ChallengeCredentialsGenerateNewResult> generate_new_result;
CallGenerateNew({kAlgorithm} /* key_algorithms */, &generate_new_result);
ASSERT_TRUE(generate_new_result);
VerifyFailedChallengeCredentialsGenerateNewResult(*generate_new_result);
}
// Test failure of the GenerateNew() operation due to failure of salt challenge
// request.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
GenerateNewFailureInSaltChallenge) {
SetSuccessfulSaltGenerationMock();
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
MakeSealedCreationMocker({kAlgorithm} /* key_algorithms */)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsGenerateNewResult> generate_new_result;
CallGenerateNew({kAlgorithm} /* key_algorithms */, &generate_new_result);
EXPECT_FALSE(generate_new_result);
EXPECT_TRUE(is_salt_challenge_requested());
SimulateSaltChallengeFailure();
ASSERT_TRUE(generate_new_result);
VerifyFailedChallengeCredentialsGenerateNewResult(*generate_new_result);
}
// Test failure of the GenerateNew() operation due to failure of sealed secret
// creation.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
GenerateNewFailureInSealedCreation) {
SetSuccessfulSaltGenerationMock();
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
MakeSealedCreationMocker({kAlgorithm} /* key_algorithms */)
->SetUpFailingMock();
std::unique_ptr<ChallengeCredentialsGenerateNewResult> generate_new_result;
CallGenerateNew({kAlgorithm} /* key_algorithms */, &generate_new_result);
ASSERT_TRUE(generate_new_result);
VerifyFailedChallengeCredentialsGenerateNewResult(*generate_new_result);
}
// Test failure of the Decrypt() operation due to the input salt being empty.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptFailureInSaltCheckEmpty) {
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, Blob() /* salt */,
&decrypt_result);
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
// Test failure of the Decrypt() operation due to the input salt not starting
// with the expected constant prefix.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptFailureInSaltCheckNotPrefixed) {
Blob salt = kSalt;
salt[GetChallengeCredentialsSaltConstantPrefix().size() - 1] ^= 1;
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, salt, &decrypt_result);
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
// Test failure of the Decrypt() operation due to the input salt containing
// nothing besides the expected constant prefix.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptFailureInSaltCheckNothingBesidesPrefix) {
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */,
GetChallengeCredentialsSaltConstantPrefix() /* salt */,
&decrypt_result);
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
// Test success of the Decrypt() operation in scenario when the salt challenge
// response comes before the unsealing challenge response.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptSuccessSaltThenUnsealing) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
SimulateSaltChallengeResponse();
EXPECT_FALSE(decrypt_result);
SimulateUnsealingChallengeResponse();
ASSERT_TRUE(decrypt_result);
VerifySuccessfulDecryptResult(*decrypt_result);
}
// Test success of the Decrypt() operation in scenario when the unsealing
// challenge response comes before the salt challenge response.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptSuccessUnsealingThenSalt) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
SimulateUnsealingChallengeResponse();
EXPECT_FALSE(decrypt_result);
SimulateSaltChallengeResponse();
ASSERT_TRUE(decrypt_result);
VerifySuccessfulDecryptResult(*decrypt_result);
}
// Test failure of the Decrypt() operation due to failure of unsealing session
// creation.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptFailureInUnsealingSessionCreation) {
for (int attempt_number = 0;
attempt_number < ChallengeCredentialsHelperImpl::kRetryAttemptCount;
++attempt_number) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
}
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpCreationFailingMock(true /* mock_repeatedly */);
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
// Responding to the salt challenge shouldn't have any effect.
SimulateSaltChallengeResponse();
}
// Test failure of the Decrypt() operation due to failure of unsealing.
TEST_F(ChallengeCredentialsHelperImplBasicTest, DecryptFailureInUnsealing) {
for (int attempt_number = 0;
attempt_number < ChallengeCredentialsHelperImpl::kRetryAttemptCount;
++attempt_number) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpUsealingFailingMock();
}
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
for (int attempt_number = 0;
attempt_number < ChallengeCredentialsHelperImpl::kRetryAttemptCount;
++attempt_number) {
EXPECT_TRUE(is_unsealing_challenge_requested());
EXPECT_FALSE(decrypt_result);
SimulateUnsealingChallengeResponse();
}
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
// Responding to the salt challenge shouldn't have any effect.
SimulateSaltChallengeResponse();
}
// Test failure of the Decrypt() operation due to failure of salt challenge
// request.
TEST_F(ChallengeCredentialsHelperImplBasicTest, DecryptFailureInSaltChallenge) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpUnsealingNotCalledMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
EXPECT_FALSE(decrypt_result);
SimulateSaltChallengeFailure();
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
// Responding to the unsealing challenge shouldn't have any effect.
SimulateUnsealingChallengeResponse();
}
// Test failure of the Decrypt() operation due to failure of unsealing challenge
// request.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptFailureInUnsealingChallenge) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpUnsealingNotCalledMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
EXPECT_FALSE(decrypt_result);
SimulateUnsealingChallengeFailure();
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
// Responding to the salt challenge shouldn't have any effect.
SimulateSaltChallengeResponse();
}
// Test failure of the Decrypt() operation due to its abortion before any of the
// challenges is completed.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptAbortionBeforeChallenges) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpUnsealingNotCalledMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
EXPECT_FALSE(decrypt_result);
// Abort the first operation by starting a new one.
StartSurplusOperation();
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
// Test failure of the Decrypt() operation due to its abortion after the salt
// challenge completes.
TEST_F(ChallengeCredentialsHelperImplBasicTest,
DecryptAbortionAfterSaltChallenge) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpUnsealingNotCalledMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
SimulateSaltChallengeResponse();
EXPECT_FALSE(decrypt_result);
// Abort the first operation by starting a new one.
StartSurplusOperation();
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
// Test failure of the Decrypt() operation due to its abortion after the
// unsealing completes.
TEST_F(ChallengeCredentialsHelperImplBasicTest, DecryptAbortionAfterUnsealing) {
ExpectSaltChallenge(kAlgorithm /* salt_challenge_algorithm */);
ExpectUnsealingChallenge(kAlgorithm /* unsealing_algorithm */);
MakeUnsealingMocker({kAlgorithm} /* key_algorithms */,
kAlgorithm /* unsealing_algorithm */)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
SimulateUnsealingChallengeResponse();
EXPECT_FALSE(decrypt_result);
// Abort the first operation by starting a new one.
StartSurplusOperation();
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
namespace {
// Tests with simulation of SignatureSealingBackend absence.
class ChallengeCredentialsHelperImplNoBackendTest
: public ChallengeCredentialsHelperImplSingleAlgorithmTestBase {
protected:
ChallengeCredentialsHelperImplNoBackendTest() {
PrepareSignatureSealingBackend(false /* enabled */);
}
};
} // namespace
// Test failure of the Decrypt() operation due to the absence of the sealing
// backend.
TEST_F(ChallengeCredentialsHelperImplNoBackendTest, DecryptFailure) {
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt({kAlgorithm} /* key_algorithms */,
kAlgorithm /* salt_challenge_algorithm */, kSalt,
&decrypt_result);
ASSERT_TRUE(decrypt_result);
VerifyFailedChallengeCredentialsDecryptResult(*decrypt_result);
}
namespace {
// Test parameters for ChallengeCredentialsHelperImplAlgorithmsTest.
struct AlgorithmsTestParam {
std::vector<ChallengeSignatureAlgorithm> key_algorithms;
ChallengeSignatureAlgorithm salt_challenge_algorithm;
ChallengeSignatureAlgorithm unsealing_algorithm;
};
// Tests various combinations of multiple algorithms.
class ChallengeCredentialsHelperImplAlgorithmsTest
: public ChallengeCredentialsHelperImplTestBase,
public testing::WithParamInterface<AlgorithmsTestParam> {
protected:
ChallengeCredentialsHelperImplAlgorithmsTest() {
PrepareSignatureSealingBackend(true /* enabled */);
}
};
} // namespace
// Test success of the Decrypt() operation with the specified combination of
// algorithms.
TEST_P(ChallengeCredentialsHelperImplAlgorithmsTest, DecryptSuccess) {
ExpectSaltChallenge(GetParam().salt_challenge_algorithm);
ExpectUnsealingChallenge(GetParam().unsealing_algorithm);
MakeUnsealingMocker(GetParam().key_algorithms, GetParam().unsealing_algorithm)
->SetUpSuccessfulMock();
std::unique_ptr<ChallengeCredentialsDecryptResult> decrypt_result;
CallDecrypt(GetParam().key_algorithms, GetParam().salt_challenge_algorithm,
kSalt, &decrypt_result);
EXPECT_TRUE(is_salt_challenge_requested());
EXPECT_TRUE(is_unsealing_challenge_requested());
SimulateSaltChallengeResponse();
EXPECT_FALSE(decrypt_result);
SimulateUnsealingChallengeResponse();
ASSERT_TRUE(decrypt_result);
VerifySuccessfulDecryptResult(*decrypt_result);
}
// Test that SHA-1 algorithms are the least preferred and chosen only if there's
// no other option.
INSTANTIATE_TEST_SUITE_P(
LowPriorityOfSha1,
ChallengeCredentialsHelperImplAlgorithmsTest,
Values(
AlgorithmsTestParam{
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA1,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256} /* key_algorithms */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256 /* salt_challenge_algorithm */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256 /* unsealing_algorithm */},
AlgorithmsTestParam{
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA1} /* key_algorithms */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1 /* salt_challenge_algorithm */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1 /* unsealing_algorithm */}));
// Test prioritization of algorithms according to their order in the input.
INSTANTIATE_TEST_SUITE_P(
InputPrioritization,
ChallengeCredentialsHelperImplAlgorithmsTest,
Values(
AlgorithmsTestParam{
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA256,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512} /* key_algorithms */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256 /* salt_challenge_algorithm */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256 /* unsealing_algorithm */},
AlgorithmsTestParam{
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA512,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256} /* key_algorithms */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512 /* salt_challenge_algorithm */,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512 /* unsealing_algorithm */}));
} // namespace cryptohome