blob: d70278d77b37861c97e9f01da403255b2da5f16f [file] [log] [blame]
// Copyright 2017 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 <string.h>
#include <memory>
#include <string>
#include <vector>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include "hammerd/mock_dbus_wrapper.h"
#include "hammerd/mock_pair_utils.h"
#include "hammerd/mock_update_fw.h"
#include "hammerd/pair_utils.h"
namespace hammerd {
namespace {
// Curve25519 test vector from the NaCl distribution.
const char kAlicePrivateStr[] =
"77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A";
const char kAlicePublicStr[] =
"8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A";
const char kBobPrivateStr[] =
"5DAB087E624A8A4B79E17F8B83800EE66F3BB1292618B6FD1C2F8B27FF88E0EB";
const char kBobPublicStr[] =
"DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F";
// Self-created test vector.
const char kNonceStr[] = "19A50A637080D93812AD65C8C5205786";
const char kAuthenticatorStr[] = "61372D572BC47539D1539AE0F395AAE3";
} // namespace
using testing::_;
using testing::DoAll;
using testing::InSequence;
using testing::Return;
// Convert the test vector from Hex-encoded string to data.
class TestVector {
public:
TestVector() {
base::HexStringToBytes(kAlicePrivateStr, &alice_private_);
base::HexStringToBytes(kAlicePublicStr, &alice_public_);
base::HexStringToBytes(kBobPrivateStr, &bob_private_);
base::HexStringToBytes(kBobPublicStr, &bob_public_);
base::HexStringToBytes(kNonceStr, &nonce_);
base::HexStringToBytes(kAuthenticatorStr, &authenticator_);
}
std::vector<uint8_t> alice_private_;
std::vector<uint8_t> alice_public_;
std::vector<uint8_t> bob_private_;
std::vector<uint8_t> bob_public_;
std::vector<uint8_t> nonce_;
std::vector<uint8_t> authenticator_;
};
// Check the size of destination and source is the same, and then copy the data.
void CheckMemcpy(uint8_t* dest, size_t size, const std::vector<uint8_t>& src) {
ASSERT_EQ(size, src.size());
memcpy(dest, src.data(), size);
}
ACTION_P3(SetChallengeRequest, public_key, private_key, nonce) {
CheckMemcpy(arg0->public_key, X25519_PUBLIC_VALUE_LEN, public_key);
CheckMemcpy(arg0->nonce, kHMACNonceLength, nonce);
CheckMemcpy(arg1, X25519_PRIVATE_KEY_LEN, private_key);
}
ACTION_P4(SetChallengeResponse, status, public_key, authenticator, ret) {
auto resp = reinterpret_cast<PairChallengeResponse*>(arg2);
resp->status = static_cast<uint8_t>(status);
if (public_key.size() > 0)
CheckMemcpy(resp->public_key, X25519_PUBLIC_VALUE_LEN, public_key);
if (authenticator.size() > 0)
CheckMemcpy(resp->authenticator, kHMACAuthenticatorLength, authenticator);
return ret;
}
// Verify PairManager method.
// In the normal case, the host side uses Alice's key, and Hammer side users
// Bob's key.
class PairTest : public testing::Test {
public:
void SetUp() override {
// Make the request payload with Alice's key pair and the nonce.
PairChallengeRequest fake_request;
CheckMemcpy(fake_request.public_key, X25519_PUBLIC_VALUE_LEN,
tv_.alice_public_);
CheckMemcpy(fake_request.nonce, kHMACNonceLength, tv_.nonce_);
request_payload_.assign(reinterpret_cast<const char*>(&fake_request),
sizeof(fake_request));
// Always generate the request with Alice's key pair and the nonce.
EXPECT_CALL(pair_manager_, GenerateChallenge(_, _))
.WillRepeatedly(SetChallengeRequest(tv_.alice_public_,
tv_.alice_private_, tv_.nonce_));
// USB device is not disconnected in normal case.
ON_CALL(fw_updater_, UsbSysfsExists()).WillByDefault(Return(true));
}
protected:
MockPairManager pair_manager_;
MockDBusWrapper dbus_wrapper_;
std::string request_payload_;
TestVector tv_;
MockFirmwareUpdater fw_updater_;
};
// Hammer returns a valid response.
TEST_F(PairTest, ChallengePassed) {
EXPECT_CALL(fw_updater_,
SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload_, _,
sizeof(PairChallengeResponse), false))
.WillOnce(SetChallengeResponse(EcResponseStatus::kSuccess,
tv_.bob_public_, tv_.authenticator_,
true));
EXPECT_CALL(
dbus_wrapper_,
SendSignalWithArgHelper(kPairChallengeSucceededSignal, tv_.bob_public_));
EXPECT_EQ(pair_manager_.PairChallenge(&fw_updater_, &dbus_wrapper_),
ChallengeStatus::kChallengePassed);
}
// Hammer returns an invalid response. The correct response should contain Bob's
// public key but it returns Alice's public key.
TEST_F(PairTest, ChallengeFailed) {
EXPECT_CALL(fw_updater_,
SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload_, _,
sizeof(PairChallengeResponse), false))
.WillOnce(SetChallengeResponse(EcResponseStatus::kSuccess,
tv_.alice_public_, tv_.authenticator_,
true));
EXPECT_CALL(dbus_wrapper_, SendSignal(kPairChallengeFailedSignal));
EXPECT_EQ(pair_manager_.PairChallenge(&fw_updater_, &dbus_wrapper_),
ChallengeStatus::kChallengeFailed);
}
// Hammer only returns the kUnavailable status.
TEST_F(PairTest, ChallengeNeedInjectEntropy) {
EXPECT_CALL(fw_updater_,
SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload_, _,
sizeof(PairChallengeResponse), false))
.WillOnce(SetChallengeResponse(EcResponseStatus::kUnavailable,
std::vector<uint8_t>(),
std::vector<uint8_t>(), false));
EXPECT_EQ(pair_manager_.PairChallenge(&fw_updater_, &dbus_wrapper_),
ChallengeStatus::kNeedInjectEntropy);
}
// Do not send DBus signal when the base is disconnected.
TEST_F(PairTest, UsbDisconnection) {
// The base is disconnected.
ON_CALL(fw_updater_, UsbSysfsExists()).WillByDefault(Return(false));
EXPECT_CALL(fw_updater_,
SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload_, _,
sizeof(PairChallengeResponse), false))
.WillOnce(SetChallengeResponse(EcResponseStatus::kInvalidParam,
std::vector<uint8_t>(),
std::vector<uint8_t>(), false));
// The DBus signal is not sent.
EXPECT_CALL(dbus_wrapper_, SendSignal(_)).Times(0);
EXPECT_EQ(pair_manager_.PairChallenge(&fw_updater_, &dbus_wrapper_),
ChallengeStatus::kConnectionError);
}
// Hammer only returns the other error status.
TEST_F(PairTest, ChallengeUnknownError) {
EXPECT_CALL(fw_updater_,
SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload_, _,
sizeof(PairChallengeResponse), false))
.WillOnce(SetChallengeResponse(EcResponseStatus::kInvalidParam,
std::vector<uint8_t>(),
std::vector<uint8_t>(), false));
EXPECT_CALL(dbus_wrapper_, SendSignal(kPairChallengeFailedSignal));
EXPECT_EQ(pair_manager_.PairChallenge(&fw_updater_, &dbus_wrapper_),
ChallengeStatus::kUnknownError);
}
} // namespace hammerd