blob: 85a579a7e31fb5b088438241c495f112f77c26fb [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 "cryptohome/cryptorecovery/recovery_crypto_tpm2_backend_impl.h"
#include <memory>
#include <optional>
#include <string>
#include <base/check.h>
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <libhwsec-foundation/crypto/big_num_util.h>
#include "cryptohome/tpm2_impl.h"
#include "trunks/error_codes.h"
#include "trunks/openssl_utility.h"
#include "trunks/tpm_generated.h"
#include "trunks/tpm_utility.h"
#include "trunks/trunks_factory.h"
using ::hwsec_foundation::BigNumToSecureBlob;
using ::hwsec_foundation::CreateBigNum;
using ::hwsec_foundation::CreateBigNumContext;
using ::hwsec_foundation::EllipticCurve;
using ::hwsec_foundation::ScopedBN_CTX;
namespace cryptohome {
namespace cryptorecovery {
RecoveryCryptoTpm2BackendImpl::RecoveryCryptoTpm2BackendImpl(
Tpm2Impl* tpm2_impl)
: tpm2_impl_(tpm2_impl) {
DCHECK(tpm2_impl_);
}
RecoveryCryptoTpm2BackendImpl::~RecoveryCryptoTpm2BackendImpl() = default;
brillo::SecureBlob RecoveryCryptoTpm2BackendImpl::GenerateKeyAuthValue() {
return brillo::SecureBlob();
}
bool RecoveryCryptoTpm2BackendImpl::EncryptEccPrivateKey(
const EllipticCurve& ec,
const crypto::ScopedEC_KEY& own_key_pair,
const std::optional<brillo::SecureBlob>& /*auth_value*/,
brillo::SecureBlob* encrypted_own_priv_key) {
DCHECK(encrypted_own_priv_key);
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return false;
}
const BIGNUM* own_priv_key_bn = EC_KEY_get0_private_key(own_key_pair.get());
if (!own_priv_key_bn) {
LOG(ERROR) << "Failed to get own_priv_key_bn";
return false;
}
if (!ec.IsScalarValid(*own_priv_key_bn)) {
LOG(ERROR) << "Scalar is not valid";
return false;
}
// Convert own private key to blob.
brillo::SecureBlob own_priv_key;
if (!BigNumToSecureBlob(*own_priv_key_bn, ec.ScalarSizeInBytes(),
&own_priv_key)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
const EC_POINT* pub_point = EC_KEY_get0_public_key(own_key_pair.get());
if (!pub_point) {
LOG(ERROR) << "Failed to get pub_point";
return false;
}
crypto::ScopedBIGNUM pub_point_x_bn = CreateBigNum(),
pub_point_y_bn = CreateBigNum();
if (!pub_point_x_bn || !pub_point_y_bn) {
LOG(ERROR) << "Failed to allocate BIGNUM";
return false;
}
if (!ec.GetAffineCoordinates(*pub_point, context.get(), pub_point_x_bn.get(),
pub_point_y_bn.get())) {
LOG(ERROR) << "Failed to get destination share x coordinate";
return false;
}
brillo::SecureBlob pub_point_x;
if (!BigNumToSecureBlob(*pub_point_x_bn, MAX_ECC_KEY_BYTES, &pub_point_x)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
brillo::SecureBlob pub_point_y;
if (!BigNumToSecureBlob(*pub_point_y_bn, MAX_ECC_KEY_BYTES, &pub_point_y)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return false;
}
// Obtain the Trunks context for sending TPM commands.
Tpm2Impl::TrunksClientContext* trunks = nullptr;
if (!tpm2_impl_->GetTrunksContext(&trunks)) {
LOG(ERROR) << "Failed to get trunks context";
return false;
}
// Create the TPM session.
std::unique_ptr<trunks::HmacSession> hmac_session =
trunks->factory->GetHmacSession();
// TODO(b:196192089): set enable_encryption to true
trunks::TPM_RC tpm_result = hmac_session->StartUnboundSession(
/*salted=*/true, /*enable_encryption=*/false);
if (tpm_result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to start TPM session: "
<< trunks::GetErrorString(tpm_result);
return false;
}
// Translate cryptohome CurveType to trunks curveID
trunks::TPM_ECC_CURVE tpm_curve_id = trunks::TPM_ECC_NONE;
switch (ec.GetCurveType()) {
case EllipticCurve::CurveType::kPrime256:
tpm_curve_id = trunks::TPM_ECC_NIST_P256;
break;
case EllipticCurve::CurveType::kPrime384:
tpm_curve_id = trunks::TPM_ECC_NIST_P384;
break;
case EllipticCurve::CurveType::kPrime521:
tpm_curve_id = trunks::TPM_ECC_NIST_P521;
break;
}
if (tpm_curve_id == trunks::TPM_ECC_NONE) {
LOG(ERROR) << "Invalid tpm2 curve id";
return false;
}
// Encrypt its own private key via the TPM2_Import command.
std::string encrypted_own_priv_key_string;
tpm_result = trunks->tpm_utility->ImportECCKey(
trunks::TpmUtility::AsymmetricKeyUsage::kDecryptKey, tpm_curve_id,
pub_point_x.to_string(), pub_point_y.to_string(),
own_priv_key.to_string(),
/*password=*/"", hmac_session->GetDelegate(),
&encrypted_own_priv_key_string);
if (tpm_result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to import its own private key into TPM: "
<< trunks::GetErrorString(tpm_result);
return false;
}
// Return the share wrapped with the TPM storage key.
*encrypted_own_priv_key = brillo::SecureBlob(encrypted_own_priv_key_string);
return true;
}
crypto::ScopedEC_POINT
RecoveryCryptoTpm2BackendImpl::GenerateDiffieHellmanSharedSecret(
const EllipticCurve& ec,
const brillo::SecureBlob& encrypted_own_priv_key,
const std::optional<brillo::SecureBlob>& /*auth_value*/,
const EC_POINT& others_pub_point) {
ScopedBN_CTX context = CreateBigNumContext();
if (!context.get()) {
LOG(ERROR) << "Failed to allocate BN_CTX structure";
return nullptr;
}
// Obtain coordinates of the publisher public point.
crypto::ScopedBIGNUM others_pub_point_x_bn = CreateBigNum(),
others_pub_point_y_bn = CreateBigNum();
if (!others_pub_point_x_bn || !others_pub_point_y_bn) {
LOG(ERROR) << "Failed to allocate BIGNUM";
return nullptr;
}
if (!ec.GetAffineCoordinates(others_pub_point, context.get(),
others_pub_point_x_bn.get(),
others_pub_point_y_bn.get())) {
LOG(ERROR) << "Failed to get the other party's public point x coordinate";
return nullptr;
}
brillo::SecureBlob others_pub_point_x;
if (!BigNumToSecureBlob(*others_pub_point_x_bn, MAX_ECC_KEY_BYTES,
&others_pub_point_x)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return nullptr;
}
brillo::SecureBlob others_pub_point_y;
if (!BigNumToSecureBlob(*others_pub_point_y_bn, MAX_ECC_KEY_BYTES,
&others_pub_point_y)) {
LOG(ERROR) << "Failed to convert BIGNUM to SecureBlob";
return nullptr;
}
// Obtain the Trunks context for sending TPM commands.
Tpm2Impl::TrunksClientContext* trunks = nullptr;
if (!tpm2_impl_->GetTrunksContext(&trunks)) {
LOG(ERROR) << "Failed to get trunks context";
return nullptr;
}
// Create the TPM session.
std::unique_ptr<trunks::HmacSession> hmac_session =
trunks->factory->GetHmacSession();
// TODO(b:196192089): set enable_encryption to true
trunks::TPM_RC tpm_result = hmac_session->StartUnboundSession(
/*salted=*/true, /*enable_encryption=*/false);
if (tpm_result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to start TPM session: "
<< trunks::GetErrorString(tpm_result);
return nullptr;
}
// Load the destination share (as a key handle) via the TPM2_Load command.
trunks::TPM_HANDLE key_handle;
tpm_result =
trunks->tpm_utility->LoadKey(encrypted_own_priv_key.to_string(),
hmac_session->GetDelegate(), &key_handle);
if (tpm_result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to load TPM key: "
<< trunks::GetErrorString(tpm_result);
return nullptr;
}
trunks::TPMS_ECC_POINT tpm_others_pub_point = {
trunks::Make_TPM2B_ECC_PARAMETER(others_pub_point_x.to_string()),
trunks::Make_TPM2B_ECC_PARAMETER(others_pub_point_y.to_string())};
// Perform the multiplication of the destination share and the other party's
// public point via the TPM2_ECDH_ZGen command.
trunks::TPM2B_ECC_POINT tpm_point_dh;
tpm_result = trunks->tpm_utility->ECDHZGen(
key_handle, trunks::Make_TPM2B_ECC_POINT(tpm_others_pub_point),
hmac_session->GetDelegate(), &tpm_point_dh);
if (tpm_result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "ECDH_ZGen failed: " << trunks::GetErrorString(tpm_result);
return nullptr;
}
// Return the point after converting it from the TPM representation.
crypto::ScopedEC_POINT point_dh = ec.CreatePoint();
if (!point_dh) {
LOG(ERROR) << "Failed to allocate EC_POINT";
return nullptr;
}
if (!trunks::TpmToOpensslEccPoint(tpm_point_dh.point, *ec.GetGroup(),
point_dh.get())) {
LOG(ERROR) << "TPM ECC point conversion failed";
return nullptr;
}
return point_dh;
}
// Generating Rsa Key is only required for TPM1, so the
// implementation of TPM2 would return a dummy true.
bool RecoveryCryptoTpm2BackendImpl::GenerateRsaKeyPair(
brillo::SecureBlob* /*encrypted_rsa_private_key*/,
brillo::SecureBlob* /*rsa_public_key_spki_der*/) {
return true;
}
// Signing the request payload is only required for TPM1, so the
// implementation of TPM2 would return a dummy true.
bool RecoveryCryptoTpm2BackendImpl::SignRequestPayload(
const brillo::SecureBlob& /*encrypted_rsa_private_key*/,
const brillo::SecureBlob& /*request_payload*/,
brillo::SecureBlob* /*signature*/) {
return true;
}
} // namespace cryptorecovery
} // namespace cryptohome