blob: f39f218a3d89b301dc729ba19d5992a647b02974 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libhwsec/backend/tpm1/key_management.h"
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <base/functional/callback_helpers.h>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <libhwsec-foundation/crypto/rsa.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/status/status_chain_macros.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "libhwsec/error/tpm1_error.h"
#include "libhwsec/overalls/overalls.h"
#include "libhwsec/status.h"
#include "libhwsec/tss_utils/scoped_tss_type.h"
using brillo::BlobFromString;
using brillo::BlobToString;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::kWellKnownExponent;
using hwsec_foundation::Sha1;
using hwsec_foundation::Sha256;
using hwsec_foundation::status::MakeStatus;
namespace hwsec {
namespace {
constexpr uint8_t kDefaultSrkAuth[] = {};
constexpr uint32_t kDefaultDiscardableWrapPasswordLength = 32;
constexpr uint32_t kDefaultTpmRsaKeyModulusBit = TSS_KEY_SIZEVAL_2048BIT;
constexpr uint8_t kDefaultTpmPublicExponentArray[] = {0x01, 0x00, 0x01};
constexpr uint32_t kSha1ModeAuthValueLength = 20;
// Min and max supported RSA modulus sizes (in bytes).
constexpr uint32_t kMinModulusSize = 64;
constexpr uint32_t kMaxModulusSize = 256;
struct RsaParameters {
uint32_t key_exponent;
brillo::Blob key_modulus;
};
StatusOr<RsaParameters> ParseSpkiDer(const brillo::Blob& public_key_spki_der) {
// Parse the SPKI.
const unsigned char* asn1_ptr = public_key_spki_der.data();
const crypto::ScopedEVP_PKEY pkey(
d2i_PUBKEY(nullptr, &asn1_ptr, public_key_spki_der.size()));
if (!pkey) {
return MakeStatus<TPMError>("Failed to parse Subject Public Key Info DER",
TPMRetryAction::kNoRetry);
}
const crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey.get()));
if (!rsa) {
return MakeStatus<TPMError>("non-RSA key was supplied",
TPMRetryAction::kNoRetry);
}
brillo::Blob key_modulus(RSA_size(rsa.get()));
const BIGNUM* n;
const BIGNUM* e;
RSA_get0_key(rsa.get(), &n, &e, nullptr);
if (BN_bn2bin(n, key_modulus.data()) != key_modulus.size()) {
return MakeStatus<TPMError>("Failed to extract public key modulus",
TPMRetryAction::kNoRetry);
}
constexpr BN_ULONG kInvalidBnWord = ~static_cast<BN_ULONG>(0);
const BN_ULONG exponent_word = BN_get_word(e);
if (exponent_word == kInvalidBnWord ||
!base::IsValueInRangeForNumericType<uint32_t>(exponent_word)) {
return MakeStatus<TPMError>("Failed to extract public key exponent",
TPMRetryAction::kNoRetry);
}
const uint32_t key_exponent = static_cast<uint32_t>(exponent_word);
return RsaParameters{
.key_exponent = key_exponent,
.key_modulus = std::move(key_modulus),
};
}
TSS_FLAG GetKeySize(int modulus_bits) {
switch (modulus_bits) {
case TSS_KEY_SIZEVAL_512BIT:
return TSS_KEY_SIZE_512;
case TSS_KEY_SIZEVAL_1024BIT:
return TSS_KEY_SIZE_1024;
case TSS_KEY_SIZEVAL_2048BIT:
return TSS_KEY_SIZE_2048;
case TSS_KEY_SIZEVAL_4096BIT:
return TSS_KEY_SIZE_4096;
case TSS_KEY_SIZEVAL_8192BIT:
return TSS_KEY_SIZE_8192;
case TSS_KEY_SIZEVAL_16384BIT:
return TSS_KEY_SIZE_16384;
default:
return TSS_KEY_SIZE_DEFAULT;
}
}
StatusOr<ScopedTssPolicy> AddAuthPolicy(overalls::Overalls& overalls,
TSS_HCONTEXT context,
TSS_HKEY key,
brillo::SecureBlob auth_value) {
ScopedTssPolicy auth_policy(overalls, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE,
auth_policy.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
if (auth_value.empty()) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Ospi_Policy_SetSecret(
auth_policy, TSS_SECRET_MODE_NONE, 0, nullptr)))
.WithStatus<TPMError>("Failed to call Ospi_Policy_SetSecret");
} else if (auth_value.size() == kSha1ModeAuthValueLength) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Ospi_Policy_SetSecret(
auth_policy, TSS_SECRET_MODE_SHA1, auth_value.size(),
auth_value.data())))
.WithStatus<TPMError>("Failed to call Ospi_Policy_SetSecret");
} else {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Ospi_Policy_SetSecret(
auth_policy, TSS_SECRET_MODE_PLAIN, auth_value.size(),
auth_value.data())))
.WithStatus<TPMError>("Failed to call Ospi_Policy_SetSecret");
}
RETURN_IF_ERROR(MakeStatus<TPM1Error>(
overalls.Ospi_Policy_AssignToObject(auth_policy, key)))
.WithStatus<TPMError>("Failed to call Ospi_Policy_AssignToObject");
return auth_policy;
}
} // namespace
KeyManagementTpm1::~KeyManagementTpm1() {
std::vector<Key> key_list;
for (auto& [token, data] : key_map_) {
key_list.push_back(Key{.token = token});
}
for (Key key : key_list) {
if (Status status = Flush(key); !status.ok()) {
LOG(WARNING) << "Failed to flush key: " << status;
}
}
}
StatusOr<absl::flat_hash_set<KeyAlgoType>>
KeyManagementTpm1::GetSupportedAlgo() {
return absl::flat_hash_set<KeyAlgoType>({
KeyAlgoType::kRsa,
});
}
Status KeyManagementTpm1::IsSupported(KeyAlgoType key_algo,
const CreateKeyOptions& options) {
switch (key_algo) {
case KeyAlgoType::kRsa:
if (options.rsa_modulus_bits.has_value()) {
uint32_t bits = options.rsa_modulus_bits.value();
if (bits < kMinModulusSize * 8) {
return MakeStatus<TPMError>("Modulus bits too small",
TPMRetryAction::kNoRetry);
}
if (bits > kMaxModulusSize * 8) {
return MakeStatus<TPMError>("Modulus bits too big",
TPMRetryAction::kNoRetry);
}
}
return OkStatus();
default:
return MakeStatus<TPMError>("Unsupported key creation algorithm",
TPMRetryAction::kNoRetry);
}
}
StatusOr<KeyManagementTpm1::CreateKeyResult> KeyManagementTpm1::CreateKey(
const OperationPolicySetting& policy,
KeyAlgoType key_algo,
const LoadKeyOptions& load_key_options,
const CreateKeyOptions& options) {
switch (key_algo) {
case KeyAlgoType::kRsa:
return CreateRsaKey(policy, options, load_key_options);
default:
return MakeStatus<TPMError>("Unsupported key creation algorithm",
TPMRetryAction::kNoRetry);
}
}
StatusOr<KeyManagementTpm1::CreateKeyResult> KeyManagementTpm1::CreateRsaKey(
const OperationPolicySetting& policy,
const CreateKeyOptions& options,
const LoadKeyOptions& load_key_options) {
ASSIGN_OR_RETURN(
const ConfigTpm1::PcrMap& setting,
config_.ToSettingsPcrMap(policy.device_config_settings),
_.WithStatus<TPMError>("Failed to convert setting to PCR map"));
if (options.allow_software_gen && setting.empty()) {
return CreateSoftwareGenRsaKey(policy, options, load_key_options);
}
ASSIGN_OR_RETURN(ScopedKey srk,
GetPersistentKey(PersistentKeyType::kStorageRootKey));
ASSIGN_OR_RETURN(const KeyTpm1& srk_data, GetKeyData(srk.GetKey()));
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
// Create a PCRS object to hold pcr_index and pcr_value.
ScopedTssPcrs pcrs(overalls_, context);
if (!setting.empty()) {
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_PCRS, TSS_PCRS_STRUCT_INFO, pcrs.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
for (const auto& map_pair : setting) {
uint32_t pcr_index = map_pair.first;
brillo::Blob pcr_value = map_pair.second;
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_PcrComposite_SetPcrValue(
pcrs, pcr_index, pcr_value.size(), pcr_value.data())))
.WithStatus<TPMError>("Failed to call Ospi_PcrComposite_SetPcrValue");
}
}
// Create a non-migratable key restricted to |pcrs|.
ScopedTssKey pcr_bound_key(overalls_, context);
TSS_FLAG init_flags = TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE |
GetKeySize(options.rsa_modulus_bits.value_or(
kDefaultTpmRsaKeyModulusBit));
if (policy.permission.auth_value.has_value()) {
init_flags |= TSS_KEY_AUTHORIZATION;
}
// In this case, the key is not decrypt only. It can be used to sign the
// data too. No easy way to make a decrypt only key here.
if (options.allow_sign && !options.allow_decrypt) {
init_flags |= TSS_KEY_TYPE_SIGNING;
} else {
init_flags |= TSS_KEY_TYPE_LEGACY;
}
brillo::Blob exponent = options.rsa_exponent.value_or(
brillo::Blob(std::begin(kDefaultTpmPublicExponentArray),
std::end(kDefaultTpmPublicExponentArray)));
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_RSAKEY, init_flags, pcr_bound_key.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
if (options.allow_sign) {
uint32_t sig_scheme = TSS_SS_RSASSAPKCS1V15_DER;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
pcr_bound_key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, sig_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
}
if (options.allow_decrypt) {
uint32_t enc_scheme = TSS_ES_RSAESPKCSV15;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
pcr_bound_key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME, enc_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
}
if (exponent != brillo::Blob(std::begin(kDefaultTpmPublicExponentArray),
std::end(kDefaultTpmPublicExponentArray))) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribData(
pcr_bound_key, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, exponent.size(),
exponent.data())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribData");
}
ScopedTssPolicy auth_policy(overalls_, context);
if (policy.permission.auth_value.has_value()) {
ASSIGN_OR_RETURN(auth_policy,
AddAuthPolicy(overalls_, context, pcr_bound_key,
policy.permission.auth_value.value()),
_.WithStatus<TPMError>("Failed to add auth policy"));
}
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Key_CreateKey(
pcr_bound_key, srk_data.key_handle, pcrs)))
.WithStatus<TPMError>("Failed to call Ospi_Key_CreateKey");
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Key_LoadKey(
pcr_bound_key, srk_data.key_handle)))
.WithStatus<TPMError>("Failed to call Ospi_Key_LoadKey");
// Get the key blob so we can load it later.
uint32_t length = 0;
ScopedTssMemory buf(overalls_, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_GetAttribData(
pcr_bound_key, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB, &length, buf.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_GetAttribData");
brillo::Blob key_blob(buf.value(), buf.value() + length);
ASSIGN_OR_RETURN(
const OperationPolicy& op_policy, config_.ToOperationPolicy(policy),
_.WithStatus<TPMError>("Failed to convert setting to policy"));
uint32_t key_handle = pcr_bound_key.value();
KeyTpm1::Type key_type = KeyTpm1::Type::kTransientKey;
std::optional<KeyReloadDataTpm1> reload_data;
if (load_key_options.auto_reload == true) {
key_type = KeyTpm1::Type::kReloadableTransientKey;
reload_data = KeyReloadDataTpm1{
.policy = op_policy,
.key_blob = key_blob,
};
}
ASSIGN_OR_RETURN(ScopedKey key,
LoadKeyInternal(key_type, key_handle,
KeyPolicyPair{
.tss_key = std::move(pcr_bound_key),
.tss_policy = std::move(auth_policy),
},
/*reload_data=*/std::nullopt),
_.WithStatus<TPMError>("Failed to load created RSA key"));
return CreateKeyResult{
.key = std::move(key),
.key_blob = std::move(key_blob),
};
}
StatusOr<KeyManagementTpm1::CreateKeyResult>
KeyManagementTpm1::CreateSoftwareGenRsaKey(
const OperationPolicySetting& policy,
const CreateKeyOptions& options,
const LoadKeyOptions& load_key_options) {
brillo::SecureBlob public_modulus;
brillo::SecureBlob prime_factor;
if (!hwsec_foundation::CreateRsaKey(
options.rsa_modulus_bits.value_or(kDefaultTpmRsaKeyModulusBit),
&public_modulus, &prime_factor)) {
return MakeStatus<TPMError>("Failed to creating software RSA key",
TPMRetryAction::kNoRetry);
}
return WrapRSAKey(
policy,
brillo::Blob(std::begin(public_modulus), std::end(public_modulus)),
prime_factor, load_key_options, options);
}
StatusOr<KeyManagementTpm1::CreateKeyResult> KeyManagementTpm1::WrapRSAKey(
const OperationPolicySetting& policy,
const brillo::Blob& public_modulus,
const brillo::SecureBlob& private_prime_factor,
const LoadKeyOptions& load_key_options,
const CreateKeyOptions& options) {
brillo::Blob exponent = options.rsa_exponent.value_or(
brillo::Blob(std::begin(kDefaultTpmPublicExponentArray),
std::end(kDefaultTpmPublicExponentArray)));
brillo::Blob modulus = public_modulus;
brillo::SecureBlob prime_factor = private_prime_factor;
ASSIGN_OR_RETURN(ScopedKey srk,
GetPersistentKey(PersistentKeyType::kStorageRootKey));
ASSIGN_OR_RETURN(const KeyTpm1& srk_data, GetKeyData(srk.GetKey()));
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
// Create the key object
TSS_FLAG init_flags = TSS_KEY_VOLATILE | TSS_KEY_MIGRATABLE |
GetKeySize(options.rsa_modulus_bits.value_or(
kDefaultTpmRsaKeyModulusBit));
if (policy.permission.auth_value.has_value()) {
init_flags |= TSS_KEY_AUTHORIZATION;
}
// In this case, the key is not decrypt only. It can be used to sign the
// data too. No easy way to make a decrypt only key here.
if (options.allow_sign && !options.allow_decrypt) {
init_flags |= TSS_KEY_TYPE_SIGNING;
} else {
init_flags |= TSS_KEY_TYPE_LEGACY;
}
ScopedTssKey local_key_handle(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_RSAKEY, init_flags, local_key_handle.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
// Set the attributes
if (options.allow_sign) {
uint32_t sig_scheme = TSS_SS_RSASSAPKCS1V15_DER;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, sig_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
}
if (options.allow_decrypt) {
uint32_t enc_scheme = TSS_ES_RSAESPKCSV15;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME, enc_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
}
// Set a random migration policy password, and discard it. The key will not
// be migrated, but to create the key outside of the TPM, we have to do it
// this way.
ScopedTssPolicy policy_handle(overalls_, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION,
policy_handle.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
brillo::SecureBlob migration_password =
CreateSecureRandomBlob(kDefaultDiscardableWrapPasswordLength);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Policy_SetSecret(
policy_handle, TSS_SECRET_MODE_PLAIN,
migration_password.size(), migration_password.data())))
.WithStatus<TPMError>("Failed to call Ospi_Policy_SetSecret");
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Policy_AssignToObject(
policy_handle, local_key_handle)))
.WithStatus<TPMError>("Failed to call Ospi_Policy_AssignToObject");
ScopedTssPolicy auth_policy(overalls_, context);
if (policy.permission.auth_value.has_value()) {
ASSIGN_OR_RETURN(auth_policy,
AddAuthPolicy(overalls_, context, local_key_handle,
policy.permission.auth_value.value()),
_.WithStatus<TPMError>("Failed to add auth policy"));
}
if (exponent != brillo::Blob(std::begin(kDefaultTpmPublicExponentArray),
std::end(kDefaultTpmPublicExponentArray))) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribData(
local_key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, exponent.size(),
exponent.data())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribData");
}
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribData(
local_key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, modulus.size(), modulus.data())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribData");
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribData(
local_key_handle, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PRIVATE_KEY, prime_factor.size(),
prime_factor.data())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribData");
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Key_WrapKey(
local_key_handle, srk_data.key_handle, 0)))
.WithStatus<TPMError>("Failed to call Ospi_Key_WrapKey");
uint32_t length = 0;
ScopedTssMemory buf(overalls_, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_GetAttribData(
local_key_handle, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB, &length, buf.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_GetAttribData");
brillo::Blob key_blob(buf.value(), buf.value() + length);
ASSIGN_OR_RETURN(
const OperationPolicy& op_policy, config_.ToOperationPolicy(policy),
_.WithStatus<TPMError>("Failed to convert setting to policy"));
// TODO(b/257208272): We should reuse the auth_policy, so we don't need to
// load it again.
ASSIGN_OR_RETURN(
ScopedKey key, LoadKey(op_policy, key_blob, load_key_options),
_.WithStatus<TPMError>("Failed to load created software RSA key"));
return CreateKeyResult{
.key = std::move(key),
.key_blob = std::move(key_blob),
};
}
StatusOr<KeyManagementTpm1::CreateKeyResult> KeyManagementTpm1::WrapECCKey(
const OperationPolicySetting& policy,
const brillo::Blob& public_point_x,
const brillo::Blob& public_point_y,
const brillo::SecureBlob& private_value,
const LoadKeyOptions& load_key_options,
const CreateKeyOptions& options) {
return MakeStatus<TPMError>("Unsupported", TPMRetryAction::kNoRetry);
}
StatusOr<ScopedKey> KeyManagementTpm1::LoadKey(
const OperationPolicy& policy,
const brillo::Blob& key_blob,
const LoadKeyOptions& load_key_options) {
ASSIGN_OR_RETURN(KeyPolicyPair result, LoadKeyBlob(policy, key_blob),
_.WithStatus<TPMError>("Failed to load key blob"));
uint32_t key_handle = result.tss_key.value();
KeyTpm1::Type key_type = KeyTpm1::Type::kTransientKey;
std::optional<KeyReloadDataTpm1> reload_data;
if (load_key_options.auto_reload == true) {
key_type = KeyTpm1::Type::kReloadableTransientKey;
reload_data = KeyReloadDataTpm1{
.policy = policy,
.key_blob = key_blob,
};
}
return LoadKeyInternal(key_type, key_handle, std::move(result), reload_data);
}
StatusOr<KeyManagementTpm1::KeyPolicyPair> KeyManagementTpm1::LoadKeyBlob(
const OperationPolicy& policy, const brillo::Blob& key_blob) {
ASSIGN_OR_RETURN(ScopedKey srk,
GetPersistentKey(PersistentKeyType::kStorageRootKey));
ASSIGN_OR_RETURN(const KeyTpm1& srk_data, GetKeyData(srk.GetKey()));
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ScopedTssKey local_key_handle(overalls_, context);
brillo::Blob mutable_key_blob = key_blob;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Context_LoadKeyByBlob(
context, srk_data.key_handle, mutable_key_blob.size(),
mutable_key_blob.data(), local_key_handle.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_LoadKeyByBlob");
ScopedTssPolicy auth_policy(overalls_, context);
if (policy.permission.auth_value.has_value()) {
ASSIGN_OR_RETURN(
auth_policy,
AddAuthPolicy(overalls_, context, local_key_handle,
policy.permission.auth_value.value()),
_.WithStatus<TPMError>("Failed to add auth policy for load key"));
}
return KeyPolicyPair{
.tss_key = std::move(local_key_handle),
.tss_policy = std::move(auth_policy),
};
}
StatusOr<ScopedKey> KeyManagementTpm1::GetPolicyEndorsementKey(
const OperationPolicySetting& policy, KeyAlgoType key_algo) {
return MakeStatus<TPMError>("Unsupported policy endorsement key",
TPMRetryAction::kNoRetry);
}
StatusOr<ScopedKey> KeyManagementTpm1::GetPersistentKey(
PersistentKeyType key_type) {
auto it = persistent_key_map_.find(key_type);
if (it != persistent_key_map_.end()) {
return ScopedKey(Key{.token = it->second}, middleware_derivative_);
}
uint32_t key_handle = 0;
switch (key_type) {
case PersistentKeyType::kStorageRootKey: {
ASSIGN_OR_RETURN(key_handle, GetSrk(),
_.WithStatus<TPMError>("Failed to get SRK"));
} break;
default:
return MakeStatus<TPMError>("Unknown persistent key type",
TPMRetryAction::kNoRetry);
}
ASSIGN_OR_RETURN(
ScopedKey key,
LoadKeyInternal(KeyTpm1::Type::kPersistentKey, key_handle,
/*key_policy_pair=*/std::nullopt,
/*reload_data=*/std::nullopt),
_.WithStatus<TPMError>("Failed to side load persistent key"));
persistent_key_map_[key_type] = key.GetKey().token;
return key;
}
StatusOr<brillo::Blob> KeyManagementTpm1::GetPubkeyHash(Key key) {
ASSIGN_OR_RETURN(const KeyTpm1& key_data, GetKeyData(key));
return Sha1(key_data.cache.pubkey_blob);
}
StatusOr<RSAPublicInfo> KeyManagementTpm1::GetRSAPublicInfo(Key key) {
ASSIGN_OR_RETURN(const KeyTpm1& key_data, GetKeyData(key));
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
uint32_t exponent_len = 0;
ScopedTssMemory exponent(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_GetAttribData(
key_data.key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, &exponent_len, exponent.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_GetAttribData");
uint32_t modulus_len = 0;
ScopedTssMemory modulus(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_GetAttribData(
key_data.key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modulus_len, modulus.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_GetAttribData");
return RSAPublicInfo{
.exponent =
brillo::Blob(exponent.value(), exponent.value() + exponent_len),
.modulus = brillo::Blob(modulus.value(), modulus.value() + modulus_len),
};
}
StatusOr<ECCPublicInfo> KeyManagementTpm1::GetECCPublicInfo(Key key) {
return MakeStatus<TPMError>("Unsupported", TPMRetryAction::kNoRetry);
}
StatusOr<ScopedKey> KeyManagementTpm1::SideLoadKey(uint32_t key_handle) {
return LoadKeyInternal(KeyTpm1::Type::kPersistentKey, key_handle,
/*key_policy_pair=*/std::nullopt,
/*reload_data=*/std::nullopt);
}
StatusOr<uint32_t> KeyManagementTpm1::GetKeyHandle(Key key) {
ASSIGN_OR_RETURN(const KeyTpm1& key_data, GetKeyData(key));
return key_data.key_handle;
}
StatusOr<brillo::Blob> KeyManagementTpm1::GetPubkeyBlob(uint32_t key_handle) {
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
uint32_t size;
ScopedTssMemory public_blob(overalls_, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Key_GetPubKey(
key_handle, &size, public_blob.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Key_GetPubKey");
return brillo::Blob(public_blob.value(), public_blob.value() + size);
}
StatusOr<ScopedKey> KeyManagementTpm1::LoadKeyInternal(
KeyTpm1::Type key_type,
uint32_t key_handle,
std::optional<KeyPolicyPair> key_policy_pair,
std::optional<KeyReloadDataTpm1> reload_data) {
ASSIGN_OR_RETURN(brillo::Blob && pubkey_blob, GetPubkeyBlob(key_handle),
_.WithStatus<TPMError>("Failed to get pubkey blob"));
std::optional<ScopedTssKey> scoped_key;
std::optional<ScopedTssPolicy> scoped_policy;
if (key_policy_pair.has_value()) {
scoped_key = std::move(key_policy_pair->tss_key);
scoped_policy = std::move(key_policy_pair->tss_policy);
}
KeyToken token = current_token_++;
key_map_.emplace(token, KeyTpm1{
.type = key_type,
.key_handle = key_handle,
.cache =
KeyTpm1::Cache{
.pubkey_blob = std::move(pubkey_blob),
},
.scoped_key = std::move(scoped_key),
.scoped_policy = std::move(scoped_policy),
.reload_data = std::move(reload_data),
});
return ScopedKey(Key{.token = token}, middleware_derivative_);
}
Status KeyManagementTpm1::Flush(Key key) {
ASSIGN_OR_RETURN(const KeyTpm1& key_data, GetKeyData(key));
switch (key_data.type) {
case KeyTpm1::Type::kPersistentKey:
// We don't need to unload these kinds of key.
return OkStatus();
case KeyTpm1::Type::kTransientKey:
case KeyTpm1::Type::kReloadableTransientKey:
key_map_.erase(key.token);
return OkStatus();
default:
return MakeStatus<TPMError>("Unknown key type", TPMRetryAction::kNoRetry);
}
}
StatusOr<std::reference_wrapper<KeyTpm1>> KeyManagementTpm1::GetKeyData(
Key key) {
auto it = key_map_.find(key.token);
if (it == key_map_.end()) {
return MakeStatus<TPMError>("Unknown key", TPMRetryAction::kNoRetry);
}
return it->second;
}
Status KeyManagementTpm1::ReloadIfPossible(Key key) {
ASSIGN_OR_RETURN(KeyTpm1 & key_data, GetKeyData(key));
if (key_data.type != KeyTpm1::Type::kReloadableTransientKey) {
// We don't need to reload un-reloadable key.
return OkStatus();
}
if (!key_data.reload_data.has_value()) {
return MakeStatus<TPMError>("Empty reload data", TPMRetryAction::kNoRetry);
}
ASSIGN_OR_RETURN(
KeyPolicyPair result,
LoadKeyBlob(key_data.reload_data->policy, key_data.reload_data->key_blob),
_.WithStatus<TPMError>("Failed to load key blob"));
key_data.key_handle = result.tss_key.value();
key_data.scoped_key = std::move(result.tss_key);
key_data.scoped_policy = std::move(result.tss_policy);
return OkStatus();
}
StatusOr<uint32_t> KeyManagementTpm1::GetSrk() {
if (srk_cache_.has_value()) {
return srk_cache_->value();
}
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
// Load the Storage Root Key
TSS_UUID SRK_UUID = TSS_UUID_SRK;
ScopedTssKey local_srk_handle(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_LoadKeyByUUID(
context, TSS_PS_TYPE_SYSTEM, SRK_UUID, local_srk_handle.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_LoadKeyByUUID");
// Check if the SRK wants a password
uint32_t srk_authusage;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_GetAttribUint32(
local_srk_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_AUTHUSAGE, &srk_authusage)))
.WithStatus<TPMError>("Failed to call Ospi_GetAttribUint32");
// Give it the password if needed
if (srk_authusage) {
TSS_HPOLICY srk_usage_policy;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_GetPolicyObject(
local_srk_handle, TSS_POLICY_USAGE, &srk_usage_policy)))
.WithStatus<TPMError>("Failed to call Ospi_GetPolicyObject");
brillo::Blob srk_auth(kDefaultSrkAuth,
kDefaultSrkAuth + sizeof(kDefaultSrkAuth));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Policy_SetSecret(
srk_usage_policy, TSS_SECRET_MODE_PLAIN,
srk_auth.size(), srk_auth.data())))
.WithStatus<TPMError>("Failed to call Ospi_Policy_SetSecret");
}
srk_cache_ = std::move(local_srk_handle);
return srk_cache_->value();
}
StatusOr<ScopedKey> KeyManagementTpm1::CreateRsaPublicKeyObject(
brillo::Blob key_modulus,
uint32_t key_flags,
uint32_t signature_scheme,
uint32_t encryption_scheme) {
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ScopedTssKey local_key_handle(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_RSAKEY, key_flags, local_key_handle.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribData(
local_key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, key_modulus.size(),
key_modulus.data())))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribData");
if (signature_scheme != TSS_SS_NONE) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, signature_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
}
if (encryption_scheme != TSS_ES_NONE) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_SetAttribUint32(
local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME, encryption_scheme)))
.WithStatus<TPMError>("Failed to call Ospi_SetAttribUint32");
}
uint32_t key_handle = local_key_handle.value();
KeyToken token = current_token_++;
key_map_.emplace(
token,
KeyTpm1{
.type = KeyTpm1::Type::kTransientKey,
.key_handle = key_handle,
.cache =
KeyTpm1::Cache{
// RSA public key object would not have valid pubkey blob.
.pubkey_blob = brillo::Blob(),
},
.scoped_key = std::move(local_key_handle),
.reload_data = std::nullopt,
});
return ScopedKey(Key{.token = token}, middleware_derivative_);
}
StatusOr<ScopedKey> KeyManagementTpm1::LoadPublicKeyFromSpki(
const brillo::Blob& public_key_spki_der,
uint32_t signature_scheme,
uint32_t encryption_scheme) {
ASSIGN_OR_RETURN(RsaParameters public_key, ParseSpkiDer(public_key_spki_der));
if (public_key.key_exponent != kWellKnownExponent) {
// Trousers only supports the well-known exponent, failing internally on
// incorrect data serialization when other exponents are used.
return MakeStatus<TPMError>("Unsupported key exponent",
TPMRetryAction::kNoRetry);
}
uint32_t key_size_flag = 0;
const size_t key_size_bits =
public_key.key_modulus.size() * /*bits per byte*/ 8;
switch (key_size_bits) {
case 1024:
key_size_flag = TSS_KEY_SIZE_1024;
break;
case 2048:
key_size_flag = TSS_KEY_SIZE_2048;
break;
default:
return MakeStatus<TPMError>("Unsupported key size",
TPMRetryAction::kNoRetry);
}
return CreateRsaPublicKeyObject(
public_key.key_modulus,
TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING | key_size_flag, signature_scheme,
encryption_scheme);
}
} // namespace hwsec