blob: ad45665a65394ad97e7df310cee5e91fa581b2bf [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 "hammerd/pair_utils.h"
#include <string.h>
#include <string>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/threading/platform_thread.h>
#include <chromeos/dbus/service_constants.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
namespace hammerd {
// Implementation of PairManager.
ChallengeStatus PairManager::PairChallenge(FirmwareUpdaterInterface* fw_updater,
DBusWrapperInterface* dbus_wrapper) {
// Generate Challenge request.
PairChallengeRequest request;
uint8_t private_key[X25519_PRIVATE_KEY_LEN];
GenerateChallenge(&request, private_key);
std::string request_payload(reinterpret_cast<const char*>(&request),
sizeof(request));
// Send the request to the hammer.
PairChallengeResponse response;
if (!fw_updater->SendSubcommandReceiveResponse(
UpdateExtraCommand::kPairChallenge, request_payload,
reinterpret_cast<void*>(&response), sizeof(response))) {
if (response.status ==
static_cast<uint8_t>(EcResponseStatus::kUnavailable)) {
LOG(ERROR) << "Need to inject the entropy.";
// Because we will inject entropy and try to pair again, we don't send
// kPairChallengeFailed signal here.
return ChallengeStatus::kNeedInjectEntropy;
}
// If the base is disconnected, then do not send DBus message.
// There is a short delay between device disconnected and kernel react to
// it. Add a short delay before check.
constexpr int kernel_delay_ms = 100;
base::PlatformThread::Sleep(
base::TimeDelta::FromMilliseconds(kernel_delay_ms));
if (!fw_updater->UsbSysfsExists()) {
LOG(ERROR) << "USB device is disconnected.";
return ChallengeStatus::kConnectionError;
}
LOG(ERROR) << "Unknown error! The status of response: "
<< static_cast<int>(response.status);
dbus_wrapper->SendSignal(kPairChallengeFailedSignal);
return ChallengeStatus::kUnknownError;
}
// Verify the response.
if (VerifyChallenge(request, private_key, response)) {
LOG(INFO) << "The pair challenge passed.";
dbus_wrapper->SendSignalWithArg(kPairChallengeSucceededSignal,
response.public_key,
sizeof(response.public_key));
return ChallengeStatus::kChallengePassed;
}
LOG(ERROR) << "The pair challenge failed.";
dbus_wrapper->SendSignal(kPairChallengeFailedSignal);
return ChallengeStatus::kChallengeFailed;
}
void PairManager::GenerateChallenge(PairChallengeRequest* request,
uint8_t* private_key) {
X25519_keypair(request->public_key, private_key);
RAND_bytes(request->nonce, sizeof(request->nonce));
}
bool PairManager::VerifyChallenge(const PairChallengeRequest& request,
uint8_t* private_key,
const PairChallengeResponse& resp) {
uint8_t shared[X25519_PRIVATE_KEY_LEN];
uint8_t myauth[SHA256_DIGEST_LENGTH];
X25519(shared, private_key, resp.public_key);
HMAC(EVP_sha256(), shared, sizeof(shared), request.nonce,
sizeof(request.nonce), myauth, nullptr);
LOG(INFO) << "Authenticator (local):\n"
<< base::HexEncode(myauth, sizeof(myauth));
// The authenticator is truncated, so we only compare the remaining part.
static_assert(sizeof(resp.authenticator) <= SHA256_DIGEST_LENGTH,
"size of authenticator must be <= SHA256_DIGEST_LENGTH.");
if (memcmp(myauth, resp.authenticator, sizeof(resp.authenticator)) == 0) {
LOG(INFO) << "Authenticator matches.";
return true;
}
LOG(ERROR) << "Authenticator does not match (remote):"
<< base::HexEncode(resp.authenticator, sizeof(resp.authenticator));
return false;
}
} // namespace hammerd