blob: fc879684a002543b4f7356f003d9cc7751032a54 [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_tpm1_backend_impl.h"
#include <map>
#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 <libhwsec/error/tpm1_error.h>
#include <libhwsec/error/tpm_retry_handler.h>
#include <libhwsec/status.h>
#include <libhwsec-foundation/crypto/big_num_util.h>
#include <libhwsec-foundation/crypto/ecdh_hkdf.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/error/error.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <trousers/scoped_tss_type.h>
#include <trousers/tss.h>
#include <trousers/trousers.h> // NOLINT(build/include_alpha) - needs tss.h
#include "cryptohome/tpm.h"
namespace cryptohome {
namespace cryptorecovery {
using hwsec::TPMErrorBase;
using hwsec_foundation::BigNumToSecureBlob;
using hwsec_foundation::CreateBigNumContext;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::EllipticCurve;
using hwsec_foundation::ScopedBN_CTX;
using hwsec_foundation::SecureBlobToBigNum;
namespace {
// Size of the auth_value blob to be randomly generated.
//
// The choice of this constant is dictated by the desire to provide sufficient
// amount of entropy as the authorization secret for the TPM_Seal command (but
// with taking into account that this authorization value is hashed by SHA-1
// by Trousers anyway).
constexpr int kAuthValueSizeBytes = 32;
// Creates a DER encoded RSA public key using SubjectPublicKeyInfo structure.
//
// Parameters
// rsa_public_key_pkcs1_der - A serialized PKCS#1 RSAPublicKey in DER format.
// rsa_public_key_spki_der - The same public key using SubjectPublicKeyInfo
// structure in DER encoded form.
bool ConvertPkcs1DerToSpkiDer(
const brillo::SecureBlob& rsa_public_key_pkcs1_der,
brillo::SecureBlob* rsa_public_key_spki_der) {
const unsigned char* rsa_public_key_pkcs1_der_data =
rsa_public_key_pkcs1_der.data();
crypto::ScopedRSA rsa(d2i_RSAPublicKey(/*RSA=*/nullptr,
&rsa_public_key_pkcs1_der_data,
rsa_public_key_pkcs1_der.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
int der_length = i2d_RSA_PUBKEY(rsa.get(), NULL);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key using SubjectPublicKeyInfo.";
return false;
}
rsa_public_key_spki_der->resize(der_length);
unsigned char* der_buffer = rsa_public_key_spki_der->data();
der_length = i2d_RSA_PUBKEY(rsa.get(), &der_buffer);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key using SubjectPublicKeyInfo.";
return false;
}
rsa_public_key_spki_der->resize(der_length);
return true;
}
} // namespace
RecoveryCryptoTpm1BackendImpl::RecoveryCryptoTpm1BackendImpl(Tpm* tpm_impl)
: tpm_impl_(tpm_impl) {
DCHECK(tpm_impl_);
}
RecoveryCryptoTpm1BackendImpl::~RecoveryCryptoTpm1BackendImpl() = default;
brillo::SecureBlob RecoveryCryptoTpm1BackendImpl::GenerateKeyAuthValue() {
return CreateSecureRandomBlob(kAuthValueSizeBytes);
}
bool RecoveryCryptoTpm1BackendImpl::EncryptEccPrivateKey(
const EllipticCurve& ec,
const crypto::ScopedEC_KEY& own_key_pair,
const std::optional<brillo::SecureBlob>& auth_value,
brillo::SecureBlob* encrypted_own_priv_key) {
const BIGNUM* own_priv_key_bn = EC_KEY_get0_private_key(own_key_pair.get());
if (!own_priv_key_bn || !ec.IsScalarValid(*own_priv_key_bn)) {
LOG(ERROR) << "Scalar is not valid";
return false;
}
// Convert one's 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;
}
// If auth_value is not provided, one's own private key will not be sealed
// and if auth_value is provided, one's own private key will be sealed.
if (!auth_value.has_value()) {
*encrypted_own_priv_key = own_priv_key;
} else if (hwsec::Status err = tpm_impl_->SealToPcrWithAuthorization(
own_priv_key, auth_value.value(), /*pcr_map=*/{{}},
encrypted_own_priv_key)) {
LOG(ERROR) << "Error sealing the blob: " << err;
return false;
}
return true;
}
crypto::ScopedEC_POINT
RecoveryCryptoTpm1BackendImpl::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;
}
// Unseal crypto secret with auth_value
brillo::SecureBlob unencrypted_own_priv_key;
// If auth_value is not provided, one's own private key will not be unsealed
// and if auth_value is provided, one's own private key will be unsealed.
if (!auth_value.has_value()) {
unencrypted_own_priv_key = encrypted_own_priv_key;
} else if (hwsec::Status err = tpm_impl_->UnsealWithAuthorization(
/*preload_handle=*/std::nullopt, encrypted_own_priv_key,
auth_value.value(),
/* pcr_map=*/{}, &unencrypted_own_priv_key)) {
LOG(ERROR) << "Failed to unseal the secret value: " << err;
return nullptr;
}
crypto::ScopedBIGNUM unencrypted_own_priv_key_bn =
SecureBlobToBigNum(unencrypted_own_priv_key);
if (!unencrypted_own_priv_key_bn) {
LOG(ERROR) << "Failed to convert unencrypted_own_priv_key to BIGNUM";
return nullptr;
}
// Calculate the shared secret from one's own private key and the other
// party's public key
crypto::ScopedEC_POINT point_dh = ComputeEcdhSharedSecretPoint(
ec, others_pub_point, *unencrypted_own_priv_key_bn);
if (!point_dh) {
LOG(ERROR) << "Failed to compute shared point from others_pub_point and "
"unencrypted_own_priv_key_bn";
return nullptr;
}
return point_dh;
}
bool RecoveryCryptoTpm1BackendImpl::GenerateRsaKeyPair(
brillo::SecureBlob* encrypted_rsa_private_key,
brillo::SecureBlob* rsa_public_key_spki_der) {
CHECK(encrypted_rsa_private_key);
CHECK(rsa_public_key_spki_der);
brillo::SecureBlob creation_blob;
brillo::SecureBlob rsa_public_key_pkcs1_der;
// Generate RSA key pair
// TODO(b/196191918): Get the real pcr_map
if (!tpm_impl_->CreatePCRBoundKey(
/*pcr_map=*/{{}}, AsymmetricKeyUsage::kSignKey,
encrypted_rsa_private_key, &rsa_public_key_pkcs1_der,
/*creation_blob=*/&creation_blob)) {
LOG(ERROR) << "Error creating PCR bound signing key.";
return false;
}
if (!ConvertPkcs1DerToSpkiDer(rsa_public_key_pkcs1_der,
rsa_public_key_spki_der)) {
LOG(ERROR) << "Error convert RSA public key from PKCS#1 to "
"SubjectPublicKeyInfo structure.";
return false;
}
return true;
}
bool RecoveryCryptoTpm1BackendImpl::SignRequestPayload(
const brillo::SecureBlob& encrypted_rsa_private_key,
const brillo::SecureBlob& request_payload,
brillo::SecureBlob* signature) {
CHECK(signature);
// TODO(b/196191918): Get the real bound_pcr_index
if (!tpm_impl_->Sign(encrypted_rsa_private_key, request_payload,
/*bound_pcr_index=*/kNotBoundToPCR, signature)) {
LOG(ERROR) << "Error signing with PCR bound key.";
return false;
}
return true;
}
} // namespace cryptorecovery
} // namespace cryptohome