blob: 0f0b24029009daf5abf6222bed8b96e30d9e9672 [file] [log] [blame]
// Copyright 2021 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 <memory>
#include <optional>
#include <string>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <gtest/gtest.h>
#include <libhwsec-foundation/crypto/big_num_util.h>
#include <libhwsec-foundation/crypto/elliptic_curve.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <openssl/ec.h>
#include <tpm_manager/client/mock_tpm_manager_utility.h>
#include <trunks/mock_authorization_delegate.h>
#include <trunks/mock_hmac_session.h>
#include <trunks/mock_tpm_utility.h>
#include <trunks/openssl_utility.h>
#include <trunks/tpm_constants.h>
#include <trunks/tpm_generated.h>
#include <trunks/trunks_factory.h>
#include <trunks/trunks_factory_for_test.h>
#include "cryptohome/cryptorecovery/recovery_crypto_tpm2_backend_impl.h"
#include "cryptohome/mock_tpm.h"
#include "cryptohome/tpm2_impl.h"
using hwsec_foundation::CreateBigNumContext;
using hwsec_foundation::EllipticCurve;
using hwsec_foundation::ScopedBN_CTX;
using testing::_;
using testing::DoAll;
using testing::Exactly;
using testing::NiceMock;
using testing::Return;
using testing::SetArgPointee;
using trunks::TPM_RC_FAILURE;
using trunks::TPM_RC_SUCCESS;
namespace cryptohome {
namespace cryptorecovery {
class RecoveryCryptoTpm2BackendTest : public testing::Test {
public:
RecoveryCryptoTpm2BackendTest() {
trunks_factory_.set_tpm_utility(&mock_tpm_utility_);
trunks_factory_.set_hmac_session(&mock_hmac_session_);
tpm_ = std::make_unique<Tpm2Impl>(&trunks_factory_,
&mock_tpm_manager_utility_);
recovery_crypto_tpm2_backend_ = tpm_->GetRecoveryCryptoBackend();
}
~RecoveryCryptoTpm2BackendTest() = default;
void SetUp() override {
context_ = CreateBigNumContext();
ASSERT_TRUE(context_);
ec_256_ = EllipticCurve::Create(EllipticCurve::CurveType::kPrime256,
context_.get());
ASSERT_TRUE(ec_256_);
ec_521_ = EllipticCurve::Create(EllipticCurve::CurveType::kPrime521,
context_.get());
ASSERT_TRUE(ec_521_);
}
protected:
std::unique_ptr<cryptohome::Tpm2Impl> tpm_;
cryptorecovery::RecoveryCryptoTpmBackend* recovery_crypto_tpm2_backend_;
NiceMock<trunks::MockAuthorizationDelegate> mock_authorization_delegate_;
NiceMock<trunks::MockTpmUtility> mock_tpm_utility_;
NiceMock<trunks::MockHmacSession> mock_hmac_session_;
NiceMock<tpm_manager::MockTpmManagerUtility> mock_tpm_manager_utility_;
ScopedBN_CTX context_;
std::optional<EllipticCurve> ec_256_;
std::optional<EllipticCurve> ec_521_;
private:
trunks::TrunksFactoryForTest trunks_factory_;
};
TEST_F(RecoveryCryptoTpm2BackendTest, GenerateKeyAuthValue) {
EXPECT_EQ(recovery_crypto_tpm2_backend_->GenerateKeyAuthValue().to_string(),
"");
}
TEST_F(RecoveryCryptoTpm2BackendTest, EncryptEccPrivateKeySuccess) {
// Set up inputs to the test.
crypto::ScopedEC_KEY own_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(own_key_pair);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_hmac_session_, GetDelegate())
.WillOnce(Return(&mock_authorization_delegate_));
std::string expected_encrypted_own_priv_key("encrypted_private_key");
EXPECT_CALL(mock_tpm_utility_, ImportECCKey(_, _, _, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<7>(expected_encrypted_own_priv_key),
Return(TPM_RC_SUCCESS)));
brillo::SecureBlob encrypted_privated_key;
EXPECT_TRUE(recovery_crypto_tpm2_backend_->EncryptEccPrivateKey(
ec_256_.value(), own_key_pair,
/*auth_value=*/std::nullopt, &encrypted_privated_key));
EXPECT_EQ(encrypted_privated_key.to_string(),
expected_encrypted_own_priv_key);
}
TEST_F(RecoveryCryptoTpm2BackendTest,
EncryptEccPrivateKeyWithInvalidOwnKeyPair) {
// Set up inputs to the test.
crypto::ScopedEC_KEY own_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(own_key_pair);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.Times(Exactly(0));
EXPECT_CALL(mock_hmac_session_, GetDelegate()).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, ImportECCKey(_, _, _, _, _, _, _, _))
.Times(Exactly(0));
brillo::SecureBlob encrypted_privated_key;
// the input key pair is not on the elliptic curve 521
EXPECT_FALSE(recovery_crypto_tpm2_backend_->EncryptEccPrivateKey(
ec_521_.value(), own_key_pair,
/*auth_value=*/std::nullopt, &encrypted_privated_key));
EXPECT_EQ(encrypted_privated_key.to_string(), "");
}
TEST_F(RecoveryCryptoTpm2BackendTest, EncryptEccPrivateKeyWithSessionFailure) {
// Set up inputs to the test.
crypto::ScopedEC_KEY own_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(own_key_pair);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_FAILURE));
EXPECT_CALL(mock_hmac_session_, GetDelegate()).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, ImportECCKey(_, _, _, _, _, _, _, _))
.Times(Exactly(0));
brillo::SecureBlob encrypted_privated_key;
EXPECT_FALSE(recovery_crypto_tpm2_backend_->EncryptEccPrivateKey(
ec_256_.value(), own_key_pair,
/*auth_value=*/std::nullopt, &encrypted_privated_key));
EXPECT_EQ(encrypted_privated_key.to_string(), "");
}
TEST_F(RecoveryCryptoTpm2BackendTest,
EncryptEccPrivateKeyWithImportEccKeyFailure) {
// Set up inputs to the test.
crypto::ScopedEC_KEY own_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(own_key_pair);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_hmac_session_, GetDelegate())
.WillOnce(Return(&mock_authorization_delegate_));
EXPECT_CALL(mock_tpm_utility_, ImportECCKey(_, _, _, _, _, _, _, _))
.WillOnce(Return(TPM_RC_FAILURE));
brillo::SecureBlob encrypted_privated_key;
EXPECT_FALSE(recovery_crypto_tpm2_backend_->EncryptEccPrivateKey(
ec_256_.value(), own_key_pair,
/*auth_value=*/std::nullopt, &encrypted_privated_key));
EXPECT_EQ(encrypted_privated_key.to_string(), "");
}
TEST_F(RecoveryCryptoTpm2BackendTest,
GenerateDiffieHellmanSharedSecretSuccess) {
// Set up inputs to the test.
crypto::ScopedEC_KEY others_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(others_key_pair);
const EC_POINT* others_pub_key =
EC_KEY_get0_public_key(others_key_pair.get());
ASSERT_TRUE(others_pub_key);
// Generated shared point has to be on the curve to be converted to
// TPMS_ECC_POINT, EC_KEY. Therefore, it cannot be dummy value.
crypto::ScopedEC_KEY expected_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(expected_key_pair);
const EC_POINT* expected_shared_secret_point =
EC_KEY_get0_public_key(expected_key_pair.get());
ASSERT_TRUE(expected_shared_secret_point);
trunks::TPMS_ECC_POINT expected_shared_secret_ecc_point;
ASSERT_TRUE(trunks::OpensslToTpmEccPoint(
*ec_256_->GetGroup(), *expected_shared_secret_point,
ec_256_->AffineCoordinateSizeInBytes(),
&expected_shared_secret_ecc_point));
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_hmac_session_, GetDelegate())
.Times(Exactly(2))
.WillRepeatedly(Return(&mock_authorization_delegate_));
EXPECT_CALL(mock_tpm_utility_, LoadKey(_, _, _))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_tpm_utility_, ECDHZGen(_, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(trunks::Make_TPM2B_ECC_POINT(
expected_shared_secret_ecc_point)),
Return(TPM_RC_SUCCESS)));
crypto::ScopedEC_POINT shared_secret_point =
recovery_crypto_tpm2_backend_->GenerateDiffieHellmanSharedSecret(
ec_256_.value(), /*encrypted_own_priv_key=*/brillo::SecureBlob(),
/*auth_value=*/std::nullopt, *others_pub_key);
EXPECT_NE(nullptr, shared_secret_point);
EXPECT_TRUE(ec_256_->AreEqual(*shared_secret_point,
*expected_shared_secret_point, context_.get()));
}
TEST_F(RecoveryCryptoTpm2BackendTest,
GenerateDiffieHellmanSharedSecretWithInvalidOthersPublicPoint) {
// Set up inputs to the test.
crypto::ScopedEC_KEY others_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(others_key_pair);
const EC_POINT* others_pub_key =
EC_KEY_get0_public_key(others_key_pair.get());
ASSERT_TRUE(others_pub_key);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.Times(Exactly(0));
EXPECT_CALL(mock_hmac_session_, GetDelegate()).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, LoadKey(_, _, _)).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, ECDHZGen(_, _, _, _)).Times(Exactly(0));
// the input key pair is not on the elliptic curve 521
crypto::ScopedEC_POINT shared_secret_point =
recovery_crypto_tpm2_backend_->GenerateDiffieHellmanSharedSecret(
ec_521_.value(), /*encrypted_own_priv_key=*/brillo::SecureBlob(),
/*auth_value=*/std::nullopt, *others_pub_key);
EXPECT_EQ(nullptr, shared_secret_point);
}
TEST_F(RecoveryCryptoTpm2BackendTest,
GenerateDiffieHellmanSharedSecretWithSessionFailure) {
// Set up inputs to the test.
crypto::ScopedEC_KEY others_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(others_key_pair);
const EC_POINT* others_pub_key =
EC_KEY_get0_public_key(others_key_pair.get());
ASSERT_TRUE(others_pub_key);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_FAILURE));
EXPECT_CALL(mock_hmac_session_, GetDelegate()).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, LoadKey(_, _, _)).Times(Exactly(0));
EXPECT_CALL(mock_tpm_utility_, ECDHZGen(_, _, _, _)).Times(Exactly(0));
crypto::ScopedEC_POINT shared_secret_point =
recovery_crypto_tpm2_backend_->GenerateDiffieHellmanSharedSecret(
ec_256_.value(), /*encrypted_own_priv_key=*/brillo::SecureBlob(),
/*auth_value=*/std::nullopt, *others_pub_key);
EXPECT_EQ(nullptr, shared_secret_point);
}
TEST_F(RecoveryCryptoTpm2BackendTest,
GenerateDiffieHellmanSharedSecretWithLoadKeyFailure) {
// Set up inputs to the test.
crypto::ScopedEC_KEY others_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(others_key_pair);
const EC_POINT* others_pub_key =
EC_KEY_get0_public_key(others_key_pair.get());
ASSERT_TRUE(others_pub_key);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_hmac_session_, GetDelegate())
.WillRepeatedly(Return(&mock_authorization_delegate_));
EXPECT_CALL(mock_tpm_utility_, LoadKey(_, _, _))
.WillOnce(Return(TPM_RC_FAILURE));
EXPECT_CALL(mock_tpm_utility_, ECDHZGen(_, _, _, _)).Times(Exactly(0));
crypto::ScopedEC_POINT shared_secret_point =
recovery_crypto_tpm2_backend_->GenerateDiffieHellmanSharedSecret(
ec_256_.value(), /*encrypted_own_priv_key=*/brillo::SecureBlob(),
/*auth_value=*/std::nullopt, *others_pub_key);
EXPECT_EQ(nullptr, shared_secret_point);
}
TEST_F(RecoveryCryptoTpm2BackendTest,
GenerateDiffieHellmanSharedSecretWithECDHZGenFailure) {
// Set up inputs to the test.
crypto::ScopedEC_KEY others_key_pair = ec_256_->GenerateKey(context_.get());
ASSERT_TRUE(others_key_pair);
const EC_POINT* others_pub_key =
EC_KEY_get0_public_key(others_key_pair.get());
ASSERT_TRUE(others_pub_key);
// Set up the mock expectations.
EXPECT_CALL(mock_hmac_session_, StartUnboundSession(true, false))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_hmac_session_, GetDelegate())
.Times(Exactly(2))
.WillRepeatedly(Return(&mock_authorization_delegate_));
EXPECT_CALL(mock_tpm_utility_, LoadKey(_, _, _))
.WillOnce(Return(TPM_RC_SUCCESS));
EXPECT_CALL(mock_tpm_utility_, ECDHZGen(_, _, _, _))
.WillOnce(Return(TPM_RC_FAILURE));
crypto::ScopedEC_POINT shared_secret_point =
recovery_crypto_tpm2_backend_->GenerateDiffieHellmanSharedSecret(
ec_256_.value(), /*encrypted_own_priv_key=*/brillo::SecureBlob(),
/*auth_value=*/std::nullopt, *others_pub_key);
EXPECT_EQ(nullptr, shared_secret_point);
}
} // namespace cryptorecovery
} // namespace cryptohome