blob: f9aaf1caf68527e6e109d1628c3b17f25d1cc928 [file] [log] [blame]
// Copyright 2016 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 "attestation/common/tpm_utility_v2.h"
#include <ios>
#include <memory>
#include <vector>
#include <base/bind.h>
#include <base/hash/sha1.h>
#include <base/logging.h>
#include <base/optional.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <crypto/sha2.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <tpm_manager-client/tpm_manager/dbus-constants.h>
#include "trunks/authorization_delegate.h"
#include "trunks/error_codes.h"
#include "trunks/tpm_generated.h"
namespace {
using trunks::AuthorizationDelegate;
using trunks::HmacSession;
using trunks::TPM_HANDLE;
using trunks::TPM_RC;
using trunks::TPM_RC_SUCCESS;
const unsigned int kWellKnownExponent = 65537;
// TODO(crbug/916023): move these utility functions to shared library.
inline const uint8_t* StringToByteBuffer(const char* str) {
return reinterpret_cast<const uint8_t*>(str);
}
inline std::string BytesToString(const std::vector<uint8_t>& bytes) {
return std::string(bytes.begin(), bytes.end());
}
inline std::string BytesToString(
const base::Optional<std::vector<uint8_t>>& maybe_bytes) {
return BytesToString(maybe_bytes.value_or(std::vector<uint8_t>()));
}
bool StringToBignum(const std::string& big_integer, BIGNUM* b) {
if (big_integer.empty() || !b)
return false;
return BN_bin2bn(StringToByteBuffer(big_integer.data()), big_integer.length(),
b);
}
crypto::ScopedRSA CreateRSAFromRawModulus(const uint8_t* modulus_buffer,
size_t modulus_size) {
crypto::ScopedRSA rsa(RSA_new());
crypto::ScopedBIGNUM e(BN_new()), n(BN_new());
if (!rsa || !e || !n) {
LOG(ERROR) << __func__ << ": Failed to allocate RSA or BIGNUMs.";
return nullptr;
}
if (!BN_set_word(e.get(), kWellKnownExponent) ||
!BN_bin2bn(modulus_buffer, modulus_size, n.get())) {
LOG(ERROR) << __func__ << ": Failed to generate exponent or modulus.";
return nullptr;
}
if (!RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr)) {
LOG(ERROR) << __func__ << ": Failed to set exponent or modulus.";
return nullptr;
}
return rsa;
}
// Convert TPMT_PUBLIC TPM public area |public_area| of RSA key to a OpenSSL RSA
// key.
crypto::ScopedRSA GetRsaPublicKeyFromTpmPublicArea(
const trunks::TPMT_PUBLIC& public_area) {
if (public_area.type != trunks::TPM_ALG_RSA) {
return nullptr;
}
crypto::ScopedRSA key = CreateRSAFromRawModulus(public_area.unique.rsa.buffer,
public_area.unique.rsa.size);
if (key == nullptr) {
LOG(ERROR) << __func__ << ": Failed to decode public key.";
return nullptr;
}
return key;
}
int TrunksCurveIDToNID(int trunks_curve_id) {
switch (trunks_curve_id) {
case trunks::TPM_ECC_NIST_P256:
return NID_X9_62_prime256v1;
default:
return NID_undef;
}
}
// Convert TPMT_PUBLIC TPM public area |public_area| of ECC key to a OpenSSL EC
// key.
crypto::ScopedEC_KEY GetEccPublicKeyFromTpmPublicArea(
const trunks::TPMT_PUBLIC& public_area) {
if (public_area.type != trunks::TPM_ALG_ECC) {
LOG(ERROR) << __func__
<< ": Unexpected algorithm type: " << public_area.type;
return nullptr;
}
int nid = TrunksCurveIDToNID(public_area.parameters.ecc_detail.curve_id);
if (nid == NID_undef) {
LOG(ERROR) << __func__ << "Unknown trunks curve_id: " << std::hex
<< std::showbase << public_area.parameters.ecc_detail.curve_id;
return nullptr;
}
crypto::ScopedEC_Key key(EC_KEY_new_by_curve_name(nid));
// Ensure that the curve is recorded in the key by reference to its ASN.1
// object ID rather than explicitly by value.
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
std::string xs = StringFrom_TPM2B_ECC_PARAMETER(public_area.unique.ecc.x);
std::string ys = StringFrom_TPM2B_ECC_PARAMETER(public_area.unique.ecc.y);
crypto::ScopedBIGNUM x(BN_new()), y(BN_new());
if (!x || !y) {
LOG(ERROR) << __func__ << ": Failed to allocate BIGNUMs for ECC parameters";
return nullptr;
}
if (!StringToBignum(xs, x.get()) || !StringToBignum(ys, y.get())) {
LOG(ERROR) << __func__ << ": Failed to parse ECC parameters";
return nullptr;
}
// EC_KEY_set_public_key_affine_coordinates will check the pointers are valid
if (!EC_KEY_set_public_key_affine_coordinates(key.get(), x.get(), y.get())) {
return nullptr;
}
if (!EC_KEY_check_key(key.get())) {
LOG(ERROR) << __func__
<< ": Bad ECC key created from TPM public key object.";
return nullptr;
}
return key;
}
template <typename OpenSSLType>
base::Optional<std::vector<uint8_t>> OpenSSLObjectToBytes(
int (*i2d_convert_function)(OpenSSLType*, unsigned char**),
typename std::remove_const<OpenSSLType>::type* type) {
if (type == nullptr) {
return base::nullopt;
}
unsigned char* openssl_buffer = nullptr;
int size = i2d_convert_function(type, &openssl_buffer);
if (size < 0) {
return base::nullopt;
}
crypto::ScopedOpenSSLBytes scoped_buffer(openssl_buffer);
return std::vector<uint8_t>(openssl_buffer, openssl_buffer + size);
}
// TODO(menghuan): consider use EVP_PKEY and related APIs
// Return RSAPublicKey DER encoded string
std::string RSAPublicKeyToString(const crypto::ScopedRSA& key) {
return BytesToString(OpenSSLObjectToBytes(i2d_RSAPublicKey, key.get()));
}
// Return SubjectPublicKeyInfo DER encoded string for RSA key.
std::string RsaSubjectPublicKeyInfoToString(const crypto::ScopedRSA& key) {
return BytesToString(OpenSSLObjectToBytes(i2d_RSA_PUBKEY, key.get()));
}
// Return SubjectPublicKeyInfo DER encoded string for ECC key.
std::string EccSubjectPublicKeyInfoToString(const crypto::ScopedEC_KEY& key) {
return BytesToString(OpenSSLObjectToBytes(i2d_EC_PUBKEY, key.get()));
}
crypto::ScopedECDSA_SIG CreateEcdsaSigFromRS(std::string r, std::string s) {
crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
crypto::ScopedBIGNUM r_bn(BN_new()), s_bn(BN_new());
if (!sig || !r_bn || !s_bn) {
LOG(ERROR) << __func__ << ": Failed to allocate RSA or BIGNUM";
return nullptr;
}
if (!StringToBignum(r, r_bn.get()) || !StringToBignum(s, s_bn.get())) {
LOG(ERROR) << __func__ << ": Failed to parse ECDSA SIG parameters";
return nullptr;
}
if (!ECDSA_SIG_set0(sig.get(), r_bn.release(), s_bn.release())) {
LOG(ERROR) << __func__ << ": Failed to set ECDSA SIG parameters";
return nullptr;
}
return sig;
}
base::Optional<std::string> SerializeFromTpmSignature(
const trunks::TPMT_SIGNATURE& signature) {
switch (signature.sig_alg) {
case trunks::TPM_ALG_RSASSA:
return StringFrom_TPM2B_PUBLIC_KEY_RSA(signature.signature.rsassa.sig);
case trunks::TPM_ALG_ECDSA: {
crypto::ScopedECDSA_SIG sig = CreateEcdsaSigFromRS(
StringFrom_TPM2B_ECC_PARAMETER(signature.signature.ecdsa.signature_r),
StringFrom_TPM2B_ECC_PARAMETER(
signature.signature.ecdsa.signature_s));
return BytesToString(OpenSSLObjectToBytes(i2d_ECDSA_SIG, sig.get()));
}
default:
LOG(ERROR) << __func__
<< ": unkown TPM 2.0 signature type: " << signature.sig_alg;
return base::nullopt;
}
}
// An authorization delegate to manage multiple authorization sessions for a
// single command.
class MultipleAuthorizations : public AuthorizationDelegate {
public:
MultipleAuthorizations() = default;
~MultipleAuthorizations() override = default;
void AddAuthorizationDelegate(AuthorizationDelegate* delegate) {
delegates_.push_back(delegate);
}
bool GetCommandAuthorization(const std::string& command_hash,
bool is_command_parameter_encryption_possible,
bool is_response_parameter_encryption_possible,
std::string* authorization) override {
std::string combined_authorization;
for (auto delegate : delegates_) {
std::string authorization;
if (!delegate->GetCommandAuthorization(
command_hash, is_command_parameter_encryption_possible,
is_response_parameter_encryption_possible, &authorization)) {
return false;
}
combined_authorization += authorization;
}
*authorization = combined_authorization;
return true;
}
bool CheckResponseAuthorization(const std::string& response_hash,
const std::string& authorization) override {
std::string mutable_authorization = authorization;
for (auto delegate : delegates_) {
if (!delegate->CheckResponseAuthorization(
response_hash,
ExtractSingleAuthorizationResponse(&mutable_authorization))) {
return false;
}
}
return true;
}
bool EncryptCommandParameter(std::string* parameter) override {
for (auto delegate : delegates_) {
if (!delegate->EncryptCommandParameter(parameter)) {
return false;
}
}
return true;
}
bool DecryptResponseParameter(std::string* parameter) override {
for (auto delegate : delegates_) {
if (!delegate->DecryptResponseParameter(parameter)) {
return false;
}
}
return true;
}
bool GetTpmNonce(std::string* nonce) override { return false; }
private:
std::string ExtractSingleAuthorizationResponse(std::string* all_responses) {
std::string response;
trunks::TPMS_AUTH_RESPONSE not_used;
if (TPM_RC_SUCCESS !=
Parse_TPMS_AUTH_RESPONSE(all_responses, &not_used, &response)) {
return std::string();
}
return response;
}
std::vector<AuthorizationDelegate*> delegates_;
};
void FlushObject(trunks::TrunksFactory* factory, TPM_HANDLE object_handle) {
if (object_handle >= trunks::TRANSIENT_FIRST &&
object_handle <= trunks::TRANSIENT_LAST) {
factory->GetTpm()->FlushContextSync(object_handle,
nullptr /* authorization */);
}
}
class TpmObjectScoper {
public:
TpmObjectScoper(trunks::TrunksFactory* factory, TPM_HANDLE object_handle)
: factory_(factory), object_handle_(object_handle) {}
~TpmObjectScoper() { FlushObject(factory_, object_handle_); }
private:
trunks::TrunksFactory* factory_;
TPM_HANDLE object_handle_;
};
} // namespace
namespace attestation {
TpmUtilityV2::TpmUtilityV2(tpm_manager::TpmManagerUtility* tpm_manager_utility,
trunks::TrunksFactory* trunks_factory)
: TpmUtilityCommon(tpm_manager_utility), trunks_factory_(trunks_factory) {}
TpmUtilityV2::~TpmUtilityV2() {
for (auto& i : endorsement_keys_) {
FlushObject(trunks_factory_, i.second);
}
}
bool TpmUtilityV2::Initialize() {
if (!TpmUtilityCommon::Initialize()) {
return false;
}
if (!trunks_factory_) {
default_trunks_factory_ = std::make_unique<trunks::TrunksFactoryImpl>();
if (!default_trunks_factory_->Initialize()) {
LOG(ERROR) << "Failed to initialize trunks.";
return false;
}
trunks_factory_ = default_trunks_factory_.get();
}
trunks_utility_ = trunks_factory_->GetTpmUtility();
return true;
}
bool TpmUtilityV2::ActivateIdentity(const std::string& identity_key_blob,
const std::string& asym_ca_contents,
const std::string& sym_ca_attestation,
std::string* credential) {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
bool TpmUtilityV2::ActivateIdentityForTpm2(
KeyType key_type,
const std::string& identity_key_blob,
const std::string& encrypted_seed,
const std::string& credential_mac,
const std::string& wrapped_credential,
std::string* credential) {
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
TPM_HANDLE identity_key_handle;
TPM_RC result = trunks_utility_->LoadKey(identity_key_blob,
empty_password_authorization.get(),
&identity_key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to load identity key: "
<< trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper(trunks_factory_, identity_key_handle);
std::string identity_key_name;
result = trunks_utility_->GetKeyName(identity_key_handle, &identity_key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get identity key name: "
<< trunks::GetErrorString(result);
return false;
}
TPM_HANDLE endorsement_key_handle;
if (!GetEndorsementKey(key_type, &endorsement_key_handle)) {
LOG(ERROR) << __func__ << ": Endorsement key is not available.";
return false;
}
std::string endorsement_key_name;
result = trunks_utility_->GetKeyName(endorsement_key_handle,
&endorsement_key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get endorsement key name: "
<< trunks::GetErrorString(result);
return false;
}
std::string endorsement_password;
if (!GetEndorsementPassword(&endorsement_password)) {
LOG(ERROR) << __func__ << ": Failed to get endorsement password";
return false;
}
std::unique_ptr<HmacSession> endorsement_session =
trunks_factory_->GetHmacSession();
result = endorsement_session->StartUnboundSession(
true /* salted */, false /* enable_encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to setup endorsement session: "
<< trunks::GetErrorString(result);
return false;
}
endorsement_session->SetEntityAuthorizationValue(endorsement_password);
std::unique_ptr<trunks::PolicySession> session =
trunks_factory_->GetPolicySession();
result = session->StartUnboundSession(true /* salted */,
false /* enable_encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to start session: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMI_DH_ENTITY auth_entity = trunks::TPM_RH_ENDORSEMENT;
std::string auth_entity_name;
trunks::Serialize_TPM_HANDLE(auth_entity, &auth_entity_name);
result = session->PolicySecret(auth_entity, auth_entity_name, std::string(),
std::string(), std::string(), 0,
endorsement_session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to set the secret: "
<< trunks::GetErrorString(result);
return false;
}
MultipleAuthorizations authorization;
authorization.AddAuthorizationDelegate(empty_password_authorization.get());
authorization.AddAuthorizationDelegate(session->GetDelegate());
std::string identity_object_data;
trunks::Serialize_TPM2B_DIGEST(trunks::Make_TPM2B_DIGEST(credential_mac),
&identity_object_data);
identity_object_data += wrapped_credential;
trunks::TPM2B_DIGEST encoded_credential;
result = trunks_factory_->GetTpm()->ActivateCredentialSync(
identity_key_handle, identity_key_name, endorsement_key_handle,
endorsement_key_name, trunks::Make_TPM2B_ID_OBJECT(identity_object_data),
trunks::Make_TPM2B_ENCRYPTED_SECRET(encrypted_seed), &encoded_credential,
&authorization);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to activate: " << trunks::GetErrorString(result);
return false;
}
*credential = trunks::StringFrom_TPM2B_DIGEST(encoded_credential);
return true;
}
bool TpmUtilityV2::CreateCertifiedKey(KeyType key_type,
KeyUsage key_usage,
const std::string& identity_key_blob,
const std::string& external_data,
std::string* key_blob,
std::string* public_key_der,
std::string* public_key_tpm_format,
std::string* key_info,
std::string* proof) {
if (identity_key_blob.empty()) {
LOG(ERROR) << __func__ << ": Unexpected empty identity_key_blob.";
return false;
}
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
trunks::TpmUtility::AsymmetricKeyUsage trunks_key_usage =
(key_usage == KEY_USAGE_SIGN) ? trunks::TpmUtility::kSignKey
: trunks::TpmUtility::kDecryptKey;
TPM_RC result;
switch (key_type) {
case KEY_TYPE_RSA:
result = trunks_utility_->CreateRSAKeyPair(
trunks_key_usage, 2048 /* modulus_bits */,
0 /* Use default public exponent */, std::string() /* password */,
std::string() /* policy_digest */,
false /* use_only_policy_authorization */,
std::vector<uint32_t>() /* creation_pcr_indexes */,
empty_password_authorization.get(), key_blob,
nullptr /* creation_blob */);
break;
case KEY_TYPE_ECC:
result = trunks_utility_->CreateECCKeyPair(
trunks_key_usage, trunks::TPM_ECC_NIST_P256 /* curve_id */,
std::string() /* password */, std::string() /* policy_digest */,
false /* use_only_policy_authorization */,
std::vector<uint32_t>() /* creation_pcr_indexes */,
empty_password_authorization.get(), key_blob,
nullptr /* creation_blob */);
break;
default:
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to create key: " << trunks::GetErrorString(result);
return false;
}
TPM_HANDLE key_handle;
result = trunks_utility_->LoadKey(
*key_blob, empty_password_authorization.get(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper(trunks_factory_, key_handle);
std::string key_name;
result = trunks_utility_->GetKeyName(key_handle, &key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key name: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_PUBLIC public_area;
result = trunks_utility_->GetKeyPublicArea(key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key public area: "
<< trunks::GetErrorString(result);
return false;
}
result = trunks::Serialize_TPMT_PUBLIC(public_area, public_key_tpm_format);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to serialize key public area: "
<< trunks::GetErrorString(result);
return false;
}
switch (key_type) {
case KEY_TYPE_RSA:
*public_key_der =
RSAPublicKeyToString(GetRsaPublicKeyFromTpmPublicArea(public_area));
break;
case KEY_TYPE_ECC:
*public_key_der = EccSubjectPublicKeyInfoToString(
GetEccPublicKeyFromTpmPublicArea(public_area));
break;
}
if (public_key_der->empty()) {
LOG(ERROR) << __func__ << ": Failed to convert public key.";
return false;
}
TPM_HANDLE identity_key_handle;
result = trunks_utility_->LoadKey(identity_key_blob,
empty_password_authorization.get(),
&identity_key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper2(trunks_factory_, identity_key_handle);
result = trunks_utility_->GetKeyPublicArea(identity_key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get identity key public area: "
<< trunks::GetErrorString(result);
return false;
}
std::string identity_key_name;
result = trunks_utility_->GetKeyName(identity_key_handle, &identity_key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get identity key name: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_SIG_SCHEME scheme;
scheme.details.any.hash_alg = trunks::TPM_ALG_SHA256;
switch (public_area.type) {
case trunks::TPM_ALG_RSA:
scheme.scheme = trunks::TPM_ALG_RSASSA;
break;
case trunks::TPM_ALG_ECC:
scheme.scheme = trunks::TPM_ALG_ECDSA;
break;
default:
LOG(ERROR) << __func__ << ": Unknown TPM key type of TPM handle.";
return false;
}
trunks::TPM2B_ATTEST certify_info;
trunks::TPMT_SIGNATURE signature;
MultipleAuthorizations authorization;
authorization.AddAuthorizationDelegate(empty_password_authorization.get());
authorization.AddAuthorizationDelegate(empty_password_authorization.get());
result = trunks_factory_->GetTpm()->CertifySync(
key_handle, key_name, identity_key_handle, identity_key_name,
trunks::Make_TPM2B_DATA(external_data), scheme, &certify_info, &signature,
&authorization);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to certify key: " << trunks::GetErrorString(result);
return false;
}
*key_info = StringFrom_TPM2B_ATTEST(certify_info);
*proof = SerializeFromTpmSignature(signature).value_or("");
return true;
}
bool TpmUtilityV2::SealToPCR0(const std::string& data,
std::string* sealed_data) {
std::string policy_digest;
TPM_RC result = trunks_utility_->GetPolicyDigestForPcrValues(
std::map<uint32_t, std::string>(
{{0, std::string() /* Use current PCR value */}}),
false, /* No authorization session */
&policy_digest);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to compute policy digest: "
<< trunks::GetErrorString(result);
return false;
}
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
result = trunks_utility_->SealData(
data, policy_digest, "", empty_password_authorization.get(), sealed_data);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to seal data: " << trunks::GetErrorString(result);
return false;
}
return true;
}
bool TpmUtilityV2::Unseal(const std::string& sealed_data, std::string* data) {
std::unique_ptr<trunks::PolicySession> session =
trunks_factory_->GetPolicySession();
TPM_RC result = session->StartUnboundSession(true /* salted */,
true /* enable_encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to start encrypted session: "
<< trunks::GetErrorString(result);
return false;
}
result = session->PolicyPCR(std::map<uint32_t, std::string>(
{{0, std::string() /* Use current PCR value */}}));
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to setup policy session: "
<< trunks::GetErrorString(result);
return false;
}
result =
trunks_utility_->UnsealData(sealed_data, session->GetDelegate(), data);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to unseal data: " << trunks::GetErrorString(result);
return false;
}
return true;
}
bool TpmUtilityV2::GetEndorsementPublicKey(KeyType key_type,
std::string* public_key_der) {
TPM_HANDLE key_handle;
if (!GetEndorsementKey(key_type, &key_handle)) {
LOG(ERROR) << __func__ << ": EK not available.";
return false;
}
trunks::TPMT_PUBLIC public_area;
TPM_RC result = trunks_utility_->GetKeyPublicArea(key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get EK public area: "
<< trunks::GetErrorString(result);
return false;
}
switch (key_type) {
case KEY_TYPE_RSA:
*public_key_der = RsaSubjectPublicKeyInfoToString(
GetRsaPublicKeyFromTpmPublicArea(public_area));
break;
case KEY_TYPE_ECC:
*public_key_der = EccSubjectPublicKeyInfoToString(
GetEccPublicKeyFromTpmPublicArea(public_area));
break;
}
if (public_key_der->empty()) {
LOG(ERROR) << __func__
<< ": Failed to convert EK public key to DER format.";
return false;
}
return true;
}
bool TpmUtilityV2::GetEndorsementCertificate(KeyType key_type,
std::string* certificate) {
// TODO(crbug/956855): Use the real index instead of non-real ones.
uint32_t index = (key_type == KEY_TYPE_RSA)
? trunks::kRsaEndorsementCertificateNonRealIndex
: trunks::kEccEndorsementCertificateNonRealIndex;
if (!tpm_manager_utility_->ReadSpace(index, false /*owner auth*/,
certificate)) {
LOG(ERROR) << __func__ << ": Failed to read endorsement certificate";
return false;
}
return true;
}
bool TpmUtilityV2::Unbind(const std::string& key_blob,
const std::string& bound_data,
std::string* data) {
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
TPM_HANDLE key_handle;
TPM_RC result = trunks_utility_->LoadKey(
key_blob, empty_password_authorization.get(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper(trunks_factory_, key_handle);
result = trunks_utility_->AsymmetricDecrypt(
key_handle, trunks::TPM_ALG_OAEP, trunks::TPM_ALG_SHA256, bound_data,
empty_password_authorization.get(), data);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to decrypt: " << trunks::GetErrorString(result);
return false;
}
return true;
}
bool TpmUtilityV2::Sign(const std::string& key_blob,
const std::string& data_to_sign,
std::string* signature) {
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
TPM_HANDLE key_handle;
TPM_RC result = trunks_utility_->LoadKey(
key_blob, empty_password_authorization.get(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
trunks::TPMT_PUBLIC public_area;
result = trunks_utility_->GetKeyPublicArea(key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key public area: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPM_ALG_ID sign_algorithm;
switch (public_area.type) {
case trunks::TPM_ALG_RSA:
sign_algorithm = trunks::TPM_ALG_RSASSA;
break;
case trunks::TPM_ALG_ECC:
sign_algorithm = trunks::TPM_ALG_ECDSA;
break;
default:
LOG(ERROR) << __func__ << ": Unknown TPM key type: " << public_area.type;
return false;
}
TpmObjectScoper scoper(trunks_factory_, key_handle);
result = trunks_utility_->Sign(
key_handle, sign_algorithm, trunks::TPM_ALG_SHA256, data_to_sign,
true /* generate_hash */, empty_password_authorization.get(), signature);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to sign data: " << trunks::GetErrorString(result);
return false;
}
// For ECDSA, trunks_utility_->Sign will return serialized TPM_SIGNATURE
// instead of signal signature data.
if (sign_algorithm == trunks::TPM_ALG_ECDSA) {
trunks::TPMT_SIGNATURE tpm_signature;
trunks::TPM_RC result =
trunks::Parse_TPMT_SIGNATURE(signature, &tpm_signature, nullptr);
if (result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "Error when parse TPM signing result.";
return -1;
}
*signature = SerializeFromTpmSignature(tpm_signature).value_or("");
}
return true;
}
bool TpmUtilityV2::CreateRestrictedKey(KeyType key_type,
KeyUsage key_usage,
std::string* public_key_der,
std::string* public_key_tpm_format,
std::string* private_key_blob) {
if (key_usage != KEY_USAGE_SIGN) {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
trunks::TPM_ALG_ID algorithm =
(key_type == KEY_TYPE_RSA) ? trunks::TPM_ALG_RSA : trunks::TPM_ALG_ECC;
TPM_RC result = trunks_utility_->CreateIdentityKey(
algorithm, empty_password_authorization.get(), private_key_blob);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to create restricted key: "
<< trunks::GetErrorString(result);
return false;
}
std::unique_ptr<trunks::BlobParser> parser = trunks_factory_->GetBlobParser();
trunks::TPM2B_PUBLIC public_info;
trunks::TPM2B_PRIVATE not_used;
if (!parser->ParseKeyBlob(*private_key_blob, &public_info, &not_used)) {
LOG(ERROR) << __func__ << ": Failed to parse key blob.";
return false;
}
result = trunks::Serialize_TPMT_PUBLIC(public_info.public_area,
public_key_tpm_format);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to serialize key public area: "
<< trunks::GetErrorString(result);
return false;
}
switch (key_type) {
case KEY_TYPE_RSA:
*public_key_der = RSAPublicKeyToString(
GetRsaPublicKeyFromTpmPublicArea(public_info.public_area));
break;
case KEY_TYPE_ECC:
*public_key_der = EccSubjectPublicKeyInfoToString(
GetEccPublicKeyFromTpmPublicArea(public_info.public_area));
break;
}
if (public_key_der->empty()) {
LOG(ERROR) << __func__ << ": Failed to convert public key to DER encoded";
return false;
}
return true;
}
std::string TpmUtilityV2::GetPCRValueForMode(const std::string& mode) {
std::string mode_digest = base::SHA1HashString(mode);
mode_digest.resize(crypto::kSHA256Length);
// PCR0 value immediately after power on.
const std::string pcr_initial_value(crypto::kSHA256Length, 0);
return crypto::SHA256HashString(pcr_initial_value + mode_digest);
}
bool TpmUtilityV2::QuotePCR(uint32_t pcr_index,
const std::string& key_blob,
std::string* quoted_pcr_value,
std::string* quoted_data,
std::string* quote) {
TPM_RC result = trunks_utility_->ReadPCR(pcr_index, quoted_pcr_value);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to read PCR " << pcr_index << ": "
<< trunks::GetErrorString(result);
return false;
}
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
TPM_HANDLE key_handle;
result = trunks_utility_->LoadKey(
key_blob, empty_password_authorization.get(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper(trunks_factory_, key_handle);
std::string key_name;
result = trunks_utility_->GetKeyName(key_handle, &key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key name: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_PUBLIC public_area;
result = trunks_utility_->GetKeyPublicArea(key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key public data: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_SIG_SCHEME scheme;
scheme.details.any.hash_alg = trunks::TPM_ALG_SHA256;
switch (public_area.type) {
case trunks::TPM_ALG_RSA:
scheme.scheme = trunks::TPM_ALG_RSASSA;
break;
case trunks::TPM_ALG_ECC:
scheme.scheme = trunks::TPM_ALG_ECDSA;
break;
default:
LOG(ERROR) << __func__ << ": Unknown TPM key type of TPM handle.";
return false;
}
// This process of selecting pcrs is highlighted in TPM 2.0 Library Spec
// Part 2 (Section 10.5 - PCR structures).
trunks::TPML_PCR_SELECTION pcr_selection;
uint8_t pcr_select_index = pcr_index / 8;
uint8_t pcr_select_byte = 1 << (pcr_index % 8);
pcr_selection.count = 1;
pcr_selection.pcr_selections[0].hash = trunks::TPM_ALG_SHA256;
pcr_selection.pcr_selections[0].sizeof_select = PCR_SELECT_MIN;
memset(pcr_selection.pcr_selections[0].pcr_select, 0, PCR_SELECT_MIN);
pcr_selection.pcr_selections[0].pcr_select[pcr_select_index] =
pcr_select_byte;
trunks::TPM2B_ATTEST quoted_struct;
trunks::TPMT_SIGNATURE signature;
result = trunks_factory_->GetTpm()->QuoteSync(
key_handle, key_name,
trunks::Make_TPM2B_DATA("") /* No qualifying data */, scheme,
pcr_selection, &quoted_struct, &signature,
empty_password_authorization.get());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to quote PCR " << pcr_index << ": "
<< trunks::GetErrorString(result);
return false;
}
*quoted_data = StringFrom_TPM2B_ATTEST(quoted_struct);
*quote = SerializeFromTpmSignature(signature).value_or("");
return true;
}
// TODO(b/141520502): Verify the quote against expected output.
bool TpmUtilityV2::IsQuoteForPCR(const std::string& /* quoted_pcr_value */,
const std::string& /* quoted_data */,
const std::string& quote,
uint32_t pcr_index) const {
std::string buffer = quote;
trunks::TPMS_ATTEST parsed_quote;
TPM_RC result = trunks::Parse_TPMS_ATTEST(&buffer, &parsed_quote, nullptr);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to parse the quote: "
<< trunks::GetErrorString(result);
return false;
}
if (parsed_quote.magic != trunks::TPM_GENERATED_VALUE) {
LOG(ERROR) << __func__ << ": Bad magic value";
return false;
}
if (parsed_quote.type != trunks::TPM_ST_ATTEST_QUOTE) {
LOG(ERROR) << __func__ << ": Not a quote";
return false;
}
trunks::TPML_PCR_SELECTION* pcr_select =
&parsed_quote.attested.quote.pcr_select;
if (pcr_select->count != 1) {
LOG(ERROR) << __func__ << ": PCR selection count=" << pcr_select->count;
return false;
}
uint32_t pcr_select_byte = pcr_index / 8;
trunks::BYTE pcr_select_mask = 1 << (pcr_index % 8);
trunks::TPMS_PCR_SELECTION* pcr_selection = pcr_select->pcr_selections;
if (pcr_selection->sizeof_select <= pcr_select_byte) {
LOG(ERROR) << __func__ << ": PCR selection is too short: "
<< pcr_selection->sizeof_select;
return false;
}
int i;
for (i = 0; i < pcr_selection->sizeof_select; ++i) {
if (i == pcr_select_byte) {
if (pcr_selection->pcr_select[i] != pcr_select_mask) {
LOG(ERROR) << __func__ << ": wrong bits in PCR selection mask at " << i;
return false;
}
} else {
if (pcr_selection->pcr_select[i]) {
LOG(ERROR) << __func__ << ": non-zero byte in PCR selection mask at "
<< i;
return false;
}
}
}
return true;
}
bool TpmUtilityV2::ReadPCR(uint32_t pcr_index, std::string* pcr_value) {
TPM_RC result = trunks_utility_->ReadPCR(pcr_index, pcr_value);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to read PCR " << pcr_index << ": "
<< trunks::GetErrorString(result);
return false;
}
return true;
}
bool TpmUtilityV2::GetNVDataSize(uint32_t nv_index, uint16_t* nv_size) const {
trunks::TPMS_NV_PUBLIC public_data;
if (trunks_utility_->GetNVSpacePublicArea(nv_index & ~trunks::HR_NV_INDEX,
&public_data) != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get NV space public area for index "
<< std::hex << nv_index << ".";
return false;
}
*nv_size = public_data.data_size;
return true;
}
bool TpmUtilityV2::CertifyNV(uint32_t nv_index,
int nv_size,
const std::string& key_blob,
std::string* quoted_data,
std::string* quote) {
TPM_RC result;
std::unique_ptr<AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
MultipleAuthorizations authorization;
authorization.AddAuthorizationDelegate(empty_password_authorization.get());
authorization.AddAuthorizationDelegate(empty_password_authorization.get());
TPM_HANDLE key_handle;
result = trunks_utility_->LoadKey(
key_blob, empty_password_authorization.get(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__
<< ": Failed to load key: " << trunks::GetErrorString(result);
return false;
}
TpmObjectScoper scoper(trunks_factory_, key_handle);
std::string key_name;
result = trunks_utility_->GetKeyName(key_handle, &key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key name: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_PUBLIC public_area;
result = trunks_utility_->GetKeyPublicArea(key_handle, &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get key public data: "
<< trunks::GetErrorString(result);
return false;
}
trunks::TPMT_SIG_SCHEME scheme;
scheme.details.any.hash_alg = trunks::TPM_ALG_SHA256;
switch (public_area.type) {
case trunks::TPM_ALG_RSA:
scheme.scheme = trunks::TPM_ALG_RSASSA;
break;
case trunks::TPM_ALG_ECC:
scheme.scheme = trunks::TPM_ALG_ECDSA;
break;
default:
LOG(ERROR) << __func__ << ": Unknown TPM key type of TPM handle.";
return false;
}
trunks::TPM2B_ATTEST quoted_struct;
trunks::TPMT_SIGNATURE signature;
result = trunks_factory_->GetTpm()->NV_CertifySync(
key_handle, // sign_handle
key_name, // sign_handle_name
nv_index, // auth_handle
"", // auth_handle_name
nv_index, // nv_index
"", // nv_index_name
trunks::Make_TPM2B_DATA(""), // qualifying data
scheme, // in_scheme
nv_size, // size to read
0, // offset
&quoted_struct, &signature, &authorization);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to certify the NVs: "
<< trunks::GetErrorString(result);
return false;
}
*quoted_data = StringFrom_TPM2B_ATTEST(quoted_struct);
*quote = SerializeFromTpmSignature(signature).value_or("");
return true;
}
bool TpmUtilityV2::GetEndorsementKey(KeyType key_type, TPM_HANDLE* key_handle) {
if (endorsement_keys_.count(key_type) > 0) {
*key_handle = endorsement_keys_[key_type];
return true;
}
std::string endorsement_password;
if (!GetEndorsementPassword(&endorsement_password)) {
return false;
}
std::unique_ptr<HmacSession> endorsement_session =
trunks_factory_->GetHmacSession();
TPM_RC result = endorsement_session->StartUnboundSession(
true /* salted */, false /* enable_encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to setup endorsement session: "
<< trunks::GetErrorString(result);
return false;
}
endorsement_session->SetEntityAuthorizationValue(endorsement_password);
// Don't fail if the owner password is not available, it may not be needed.
std::string owner_password;
GetOwnerPassword(&owner_password);
std::unique_ptr<HmacSession> owner_session =
trunks_factory_->GetHmacSession();
result = owner_session->StartUnboundSession(true /* salted */,
false /* enable_encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to setup owner session: "
<< trunks::GetErrorString(result);
return false;
}
owner_session->SetEntityAuthorizationValue(owner_password);
trunks::TPM_ALG_ID algorithm =
(key_type == KEY_TYPE_RSA) ? trunks::TPM_ALG_RSA : trunks::TPM_ALG_ECC;
result = trunks_utility_->GetEndorsementKey(
algorithm, endorsement_session->GetDelegate(),
owner_session->GetDelegate(), key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": Failed to get endorsement key: "
<< trunks::GetErrorString(result);
return false;
}
endorsement_keys_[key_type] = *key_handle;
return true;
}
bool TpmUtilityV2::GetEndorsementPublicKeyModulus(KeyType key_type,
std::string* ekm) {
if (key_type == KEY_TYPE_RSA) {
return trunks_utility_->GetPublicRSAEndorsementKeyModulus(ekm) ==
TPM_RC_SUCCESS;
}
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
bool TpmUtilityV2::CreateIdentity(KeyType key_type,
AttestationDatabase::Identity* identity) {
IdentityKey* key_pb = identity->mutable_identity_key();
IdentityBinding* binding_pb = identity->mutable_identity_binding();
if (!CreateRestrictedKey(key_type, KEY_USAGE_SIGN,
key_pb->mutable_identity_public_key_der(),
binding_pb->mutable_identity_public_key_tpm_format(),
key_pb->mutable_identity_key_blob())) {
LOG(ERROR) << __func__ << ": Failed to create restricted key.";
return false;
}
key_pb->set_identity_key_type(key_type);
binding_pb->set_identity_public_key_der(key_pb->identity_public_key_der());
return true;
}
bool TpmUtilityV2::GetRsuDeviceId(std::string* rsu_device_id) {
return trunks_utility_->GetRsuDeviceId(rsu_device_id) ==
trunks::TPM_RC_SUCCESS;
}
} // namespace attestation