blob: 8f437dfa721fcb9a7c5bcd8dd0eaaa32ccd8494d [file] [log] [blame]
// Copyright 2022 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 <set>
#include <string>
#include <utility>
#include <cryptohome/proto_bindings/UserDataAuth.pb.h>
#include <gtest/gtest.h>
#include <libhwsec-foundation/error/error.h>
#include "cryptohome/error/converter.h"
#include "cryptohome/error/cryptohome_error.h"
namespace cryptohome {
namespace error {
namespace {
using hwsec_foundation::status::MakeStatus;
using hwsec_foundation::status::StatusChain;
constexpr char kTestSanitizedUsername1[] = "Abcdefghijklmnop1234!@#$%^&*()";
// Note that the RepeatedField field in protobuf for PossibleAction uses int,
// thus the need to for 2 template types.
template <typename T, typename S>
std::set<T> ToStdSet(const ::google::protobuf::RepeatedField<S>& input) {
std::vector<T> list;
for (int i = 0; i < input.size(); i++) {
list.push_back(static_cast<T>(input[i]));
}
return std::set<T>(list.begin(), list.end());
}
class ErrorConverterTest : public ::testing::Test {
public:
ErrorConverterTest() {}
~ErrorConverterTest() override = default;
protected:
const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 =
CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1),
std::string("Testing1"));
const CryptohomeError::ErrorLocationPair kErrorLocationForTesting2 =
CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(2),
std::string("Testing2"));
};
TEST_F(ErrorConverterTest, BasicConversionTest) {
StatusChain<CryptohomeError> err1 = MakeStatus<CryptohomeError>(
kErrorLocationForTesting2, ErrorActionSet({ErrorAction::kPowerwash}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
user_data_auth::CryptohomeErrorCode ec =
static_cast<user_data_auth::CryptohomeErrorCode>(
123451234); // Intentionally invalid value.
user_data_auth::CryptohomeErrorInfo info =
CryptohomeErrorToUserDataAuthError(err1, &ec);
EXPECT_EQ(ec, user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
EXPECT_EQ(info.error_id(),
std::to_string(kErrorLocationForTesting2.location()));
EXPECT_EQ(info.primary_action(), user_data_auth::PrimaryAction::PRIMARY_NONE);
ASSERT_EQ(info.possible_actions_size(), 1);
EXPECT_EQ(info.possible_actions(0),
user_data_auth::PossibleAction::POSSIBLY_POWERWASH);
}
TEST_F(ErrorConverterTest, Success) {
hwsec_foundation::status::StatusChain<CryptohomeError> err1;
user_data_auth::CryptohomeErrorCode ec =
static_cast<user_data_auth::CryptohomeErrorCode>(
123451234); // Intentionally invalid value.
user_data_auth::CryptohomeErrorInfo info =
CryptohomeErrorToUserDataAuthError(err1, &ec);
EXPECT_EQ(ec, user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(info.error_id(), "");
EXPECT_EQ(info.primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
EXPECT_EQ(info.possible_actions_size(), 0);
}
TEST_F(ErrorConverterTest, WrappedPossibleAction) {
StatusChain<CryptohomeError> err1 = MakeStatus<CryptohomeError>(
kErrorLocationForTesting2, ErrorActionSet({ErrorAction::kPowerwash}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
StatusChain<CryptohomeError> err2 =
MakeStatus<CryptohomeError>(kErrorLocationForTesting1,
ErrorActionSet({ErrorAction::kReboot}))
.Wrap(std::move(err1));
user_data_auth::CryptohomeErrorCode ec =
static_cast<user_data_auth::CryptohomeErrorCode>(
123451234); // Intentionally invalid value.
user_data_auth::CryptohomeErrorInfo info =
CryptohomeErrorToUserDataAuthError(err2, &ec);
EXPECT_EQ(ec, user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
EXPECT_EQ(info.error_id(),
std::to_string(kErrorLocationForTesting1.location()) + "-" +
std::to_string(kErrorLocationForTesting2.location()));
EXPECT_EQ(info.primary_action(), user_data_auth::PrimaryAction::PRIMARY_NONE);
EXPECT_EQ(ToStdSet<user_data_auth::PossibleAction>(info.possible_actions()),
std::set<user_data_auth::PossibleAction>(
{user_data_auth::PossibleAction::POSSIBLY_POWERWASH,
user_data_auth::PossibleAction::POSSIBLY_REBOOT}));
}
TEST_F(ErrorConverterTest, WrappedPrimaryAction) {
StatusChain<CryptohomeError> err1 = MakeStatus<CryptohomeError>(
kErrorLocationForTesting2,
ErrorActionSet({ErrorAction::kTpmUpdateRequired}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
StatusChain<CryptohomeError> err2 =
MakeStatus<CryptohomeError>(kErrorLocationForTesting1,
ErrorActionSet({ErrorAction::kReboot}))
.Wrap(std::move(err1));
user_data_auth::CryptohomeErrorCode ec;
user_data_auth::CryptohomeErrorInfo info =
CryptohomeErrorToUserDataAuthError(err2, &ec);
EXPECT_EQ(info.primary_action(),
user_data_auth::PrimaryAction::PRIMARY_TPM_UDPATE_REQUIRED);
EXPECT_EQ(info.possible_actions_size(), 0);
}
TEST_F(ErrorConverterTest, ReplyWithErrorPrimary) {
// Prepare the callback.
bool reply_received = false;
user_data_auth::MountReply received_reply;
auto cb = base::BindOnce(
[](bool* reply_received_ptr,
user_data_auth::MountReply* received_reply_ptr,
const user_data_auth::MountReply& cb_reply) {
ASSERT_FALSE(*reply_received_ptr);
*reply_received_ptr = true;
received_reply_ptr->CopyFrom(cb_reply);
},
base::Unretained(&reply_received), base::Unretained(&received_reply));
// Prepare the status chain.
StatusChain<CryptohomeError> err1 = MakeStatus<CryptohomeError>(
kErrorLocationForTesting2,
ErrorActionSet({ErrorAction::kTpmUpdateRequired}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
StatusChain<CryptohomeError> err2 =
MakeStatus<CryptohomeError>(kErrorLocationForTesting1,
ErrorActionSet({ErrorAction::kReboot}))
.Wrap(std::move(err1));
// Make the call.
user_data_auth::MountReply passedin_reply;
passedin_reply.set_sanitized_username(kTestSanitizedUsername1);
ReplyWithError(std::move(cb), passedin_reply, err2);
// Check results.
ASSERT_TRUE(reply_received);
EXPECT_EQ(received_reply.error(),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
ASSERT_TRUE(received_reply.has_error_info());
EXPECT_EQ(received_reply.sanitized_username(), kTestSanitizedUsername1);
EXPECT_EQ(received_reply.error_info().error_id(),
std::to_string(kErrorLocationForTesting1.location()) + "-" +
std::to_string(kErrorLocationForTesting2.location()));
EXPECT_EQ(received_reply.error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_TPM_UDPATE_REQUIRED);
EXPECT_EQ(received_reply.error_info().possible_actions_size(), 0);
}
TEST_F(ErrorConverterTest, ReplyWithErrorSuccess) {
// Prepare the callback.
bool reply_received = false;
user_data_auth::MountReply received_reply;
auto cb = base::BindOnce(
[](bool* reply_received_ptr,
user_data_auth::MountReply* received_reply_ptr,
const user_data_auth::MountReply& cb_reply) {
ASSERT_FALSE(*reply_received_ptr);
*reply_received_ptr = true;
received_reply_ptr->CopyFrom(cb_reply);
},
base::Unretained(&reply_received), base::Unretained(&received_reply));
// Prepare the status chain.
hwsec_foundation::status::StatusChain<CryptohomeError> err1;
// Make the call.
user_data_auth::MountReply passedin_reply;
passedin_reply.set_sanitized_username(kTestSanitizedUsername1);
ReplyWithError(std::move(cb), passedin_reply, err1);
// Check results.
ASSERT_TRUE(reply_received);
EXPECT_FALSE(received_reply.has_error_info());
EXPECT_EQ(received_reply.error(),
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(received_reply.sanitized_username(), kTestSanitizedUsername1);
}
} // namespace
} // namespace error
} // namespace cryptohome