| // Copyright 2023 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/attestation.h" |
| |
| #include <arpa/inet.h> |
| #include <base/hash/sha1.h> |
| #include <base/memory/free_deleter.h> |
| #include <base/sys_byteorder.h> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <attestation/proto_bindings/attestation_ca.pb.h> |
| #include <attestation/proto_bindings/database.pb.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <libhwsec-foundation/status/status_chain_macros.h> |
| #include <libhwsec-foundation/crypto/rsa.h> |
| #include <tpm_manager/proto_bindings/tpm_manager.pb.h> |
| #include <tpm_manager-client/tpm_manager/dbus-constants.h> |
| #include <tpm_manager-client/tpm_manager/dbus-proxies.h> |
| |
| #include "libhwsec/backend/tpm1/static_utils.h" |
| #include "libhwsec/error/tpm1_error.h" |
| #include "libhwsec/error/tpm_manager_error.h" |
| #include "libhwsec/overalls/overalls.h" |
| #include "libhwsec/status.h" |
| #include "libhwsec/structures/key.h" |
| #include "libhwsec/structures/operation_policy.h" |
| |
| using brillo::BlobFromString; |
| using brillo::BlobToString; |
| using hwsec_foundation::status::MakeStatus; |
| using Mode = hwsec::DeviceConfigSettings::BootModeSetting::Mode; |
| |
| namespace hwsec { |
| |
| namespace { |
| |
| using ScopedByteArray = std::unique_ptr<BYTE, base::FreeDeleter>; |
| |
| constexpr unsigned int kDefaultTpmRsaKeyBits = 2048; |
| constexpr unsigned int kDefaultTpmRsaKeyFlag = TSS_KEY_SIZE_2048; |
| constexpr unsigned int kDigestSize = sizeof(TPM_DIGEST); |
| constexpr size_t kSelectBitmapSize = 2; |
| |
| std::string TSSBufferAsString(const BYTE* buffer, size_t length) { |
| return std::string(reinterpret_cast<const char*>(buffer), length); |
| } |
| |
| // Builds the serialized TPM_PCR_COMPOSITE stream, where |pcr_index| is the PCR |
| // index, and |quoted_pcr_value| is the value of the register. |
| StatusOr<std::string> buildPcrComposite(uint32_t pcr_index, |
| const std::string& quoted_pcr_value) { |
| if (pcr_index >= kSelectBitmapSize * 8) { |
| return MakeStatus<TPMError>("PCR index is out of range", |
| TPMRetryAction::kNoRetry); |
| } |
| // Builds the PCR composite header. |
| struct __attribute__((packed)) { |
| // Corresponding to TPM_PCR_SELECTION.sizeOfSelect. |
| uint16_t select_size; |
| // Corresponding to TPM_PCR_SELECTION.pcrSelect. |
| uint8_t select_bitmap[kSelectBitmapSize]; |
| // Corresponding to TPM_PCR_COMPOSITE.valueSize. |
| uint32_t value_size; |
| } composite_header = {0}; |
| static_assert(sizeof(composite_header) == |
| sizeof(uint16_t) + kSelectBitmapSize + sizeof(uint32_t), |
| "Expect no padding between composite struct."); |
| // Sets to 2 bytes. |
| composite_header.select_size = base::HostToNet16(2u); |
| composite_header.select_bitmap[pcr_index / 8] = 1 << (pcr_index % 8); |
| composite_header.value_size = base::HostToNet32(quoted_pcr_value.size()); |
| const char* buffer = reinterpret_cast<const char*>(&composite_header); |
| return std::string(buffer, sizeof(composite_header)) + quoted_pcr_value; |
| } |
| |
| StatusOr<std::string> DecryptIdentityRequest(overalls::Overalls& overalls, |
| RSA* pca_key, |
| const std::string& request) { |
| // Parse the serialized TPM_IDENTITY_REQ structure. |
| UINT64 offset = 0; |
| BYTE* buffer = reinterpret_cast<BYTE*>( |
| const_cast<typename std::string::value_type*>(request.data())); |
| TPM_IDENTITY_REQ request_parsed; |
| RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_IDENTITY_REQ( |
| &offset, buffer, &request_parsed))) |
| .WithStatus<TPMError>("Failed to call Ospi_UnloadBlob_IDENTITY_REQ"); |
| ScopedByteArray scoped_asym_blob(request_parsed.asymBlob); |
| ScopedByteArray scoped_sym_blob(request_parsed.symBlob); |
| |
| // Decrypt the symmetric key. |
| unsigned char key_buffer[kDefaultTpmRsaKeyBits / 8]; |
| int key_length = |
| RSA_private_decrypt(request_parsed.asymSize, request_parsed.asymBlob, |
| key_buffer, pca_key, RSA_PKCS1_PADDING); |
| if (key_length == -1) { |
| return MakeStatus<TPMError>("Failed to decrypt identity request key", |
| TPMRetryAction::kNoRetry); |
| } |
| TPM_SYMMETRIC_KEY symmetric_key; |
| offset = 0; |
| RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_SYMMETRIC_KEY( |
| &offset, key_buffer, &symmetric_key))) |
| .WithStatus<TPMError>("Failed to call Ospi_UnloadBlob_SYMMETRIC_KEY"); |
| ScopedByteArray scoped_sym_key(symmetric_key.data); |
| |
| // Decrypt the request with the symmetric key. |
| brillo::SecureBlob proof_serial; |
| proof_serial.resize(request_parsed.symSize); |
| UINT32 proof_serial_length = proof_serial.size(); |
| RETURN_IF_ERROR( |
| MakeStatus<TPM1Error>(overalls.Orspi_SymDecrypt( |
| symmetric_key.algId, TPM_ES_SYM_CBC_PKCS5PAD, symmetric_key.data, |
| NULL, request_parsed.symBlob, request_parsed.symSize, |
| proof_serial.data(), &proof_serial_length))) |
| .WithStatus<TPMError>("Failed to call Orspi_SymDecrypt"); |
| |
| // Parse the serialized TPM_IDENTITY_PROOF structure. |
| TPM_IDENTITY_PROOF proof; |
| offset = 0; |
| RETURN_IF_ERROR( |
| MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_IDENTITY_PROOF( |
| &offset, proof_serial.data(), &proof))) |
| .WithStatus<TPMError>("Failed to call Orspi_UnloadBlob_IDENTITY_PROOF"); |
| ScopedByteArray scoped_label(proof.labelArea); |
| ScopedByteArray scoped_binding(proof.identityBinding); |
| ScopedByteArray scoped_endorsement(proof.endorsementCredential); |
| ScopedByteArray scoped_platform(proof.platformCredential); |
| ScopedByteArray scoped_conformance(proof.conformanceCredential); |
| ScopedByteArray scoped_key(proof.identityKey.pubKey.key); |
| ScopedByteArray scoped_parms(proof.identityKey.algorithmParms.parms); |
| |
| std::string identity_binding( |
| proof.identityBinding, proof.identityBinding + proof.identityBindingSize); |
| brillo::SecureClearBytes(proof.identityBinding, proof.identityBindingSize); |
| return identity_binding; |
| } |
| |
| std::string GetDescriptionForMode(const Mode& mode) { |
| return base::StringPrintf( |
| "(Developer Mode: %s, Recovery Mode: %s, Firmware Type: %s)", |
| mode.developer_mode ? "On" : "Off", mode.recovery_mode ? "On" : "Off", |
| mode.verified_firmware ? "Verified" : "Developer"); |
| } |
| |
| Status RemoveOwnerDependencyForAttestation( |
| org::chromium::TpmManagerProxyInterface& tpm_manager) { |
| tpm_manager::RemoveOwnerDependencyRequest request; |
| request.set_owner_dependency(tpm_manager::kTpmOwnerDependency_Attestation); |
| tpm_manager::RemoveOwnerDependencyReply reply; |
| |
| if (brillo::ErrorPtr err; !tpm_manager.RemoveOwnerDependency( |
| request, &reply, &err, Proxy::kDefaultDBusTimeoutMs)) { |
| return MakeStatus<TPMError>(TPMRetryAction::kCommunication) |
| .Wrap(std::move(err)); |
| } |
| |
| RETURN_IF_ERROR(MakeStatus<TPMManagerError>(reply.status())); |
| return OkStatus(); |
| } |
| |
| } // namespace |
| |
| StatusOr<attestation::TpmVersion> AttestationTpm1::GetTpmVersion() { |
| return attestation::TpmVersion::TPM_1_2; |
| } |
| |
| StatusOr<attestation::Quote> AttestationTpm1::Quote( |
| DeviceConfigs device_configs, Key key) { |
| if (device_configs.none()) { |
| return MakeStatus<TPMError>("Quote with no device config specified", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| attestation::Quote quote; |
| ASSIGN_OR_RETURN(const ConfigTpm1::PcrMap& pcr_map, |
| config_.ToPcrMap(device_configs), |
| _.WithStatus<TPMError>("Failed to get PCR map")); |
| if (pcr_map.size() == 1) { |
| int pcr = pcr_map.begin()->first; |
| ASSIGN_OR_RETURN(const brillo::Blob& value, config_.ReadPcr(pcr), |
| _.WithStatus<TPMError>("Failed to read PCR")); |
| quote.set_quoted_pcr_value(BlobToString(value)); |
| } |
| |
| ASSIGN_OR_RETURN(ScopedTssPcrs && pcr_select, |
| config_.ToPcrSelection(device_configs), |
| _.WithStatus<TPMError>( |
| "Failed to convert device configs to PCR selection")); |
| |
| ASSIGN_OR_RETURN(const KeyTpm1& key_data, key_management_.GetKeyData(key)); |
| ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetTpmHandle()); |
| ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext()); |
| // Generate the quote. |
| TSS_VALIDATION validation = {}; |
| // Here we use well-known string value for consistency with AttestationTpm2, |
| // which doesn't supply any qualifying data from caller while in TPM 1.2 |
| // it's required to have non-empty external data. |
| BYTE well_known_external_data[kDigestSize] = {}; |
| validation.ulExternalDataLength = kDigestSize; |
| validation.rgbExternalData = well_known_external_data; |
| RETURN_IF_ERROR( |
| MakeStatus<TPM1Error>(overalls_.Ospi_TPM_Quote( |
| tpm_handle, key_data.key_handle, pcr_select, &validation))) |
| .WithStatus<TPMError>("Failed to call Ospi_TPM_Quote"); |
| ScopedTssMemory scoped_signed_data(overalls_, context, validation.rgbData); |
| ScopedTssMemory scoped_signature(overalls_, context, |
| validation.rgbValidationData); |
| |
| if (device_configs[DeviceConfig::kDeviceModel]) { |
| if (StatusOr<std::string> hwid = config_.GetHardwareID(); !hwid.ok()) { |
| LOG(WARNING) << "Failed to get Hardware ID: " << hwid.status(); |
| } else { |
| quote.set_pcr_source_hint(hwid.value()); |
| } |
| } |
| quote.set_quoted_data(std::string( |
| validation.rgbData, validation.rgbData + validation.ulDataLength)); |
| quote.set_quote(std::string( |
| validation.rgbValidationData, |
| validation.rgbValidationData + validation.ulValidationDataLength)); |
| |
| return quote; |
| } |
| |
| StatusOr<bool> AttestationTpm1::IsQuoted(DeviceConfigs device_configs, |
| const attestation::Quote& quote) { |
| if (device_configs.none()) { |
| return MakeStatus<TPMError>("No device config specified", |
| TPMRetryAction::kNoRetry); |
| } |
| if (device_configs.count() > 1) { |
| return MakeStatus<TPMError>( |
| "Verifying quote for Multiple device configs is unsupported", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!quote.has_quoted_pcr_value() || !quote.has_quoted_data()) { |
| return MakeStatus<TPMError>("Invalid attestation::Quote", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| if (device_configs[DeviceConfig::kBootMode]) { |
| ASSIGN_OR_RETURN(Mode mode, config_.GetCurrentBootMode(), |
| _.WithStatus<TPMError>("Failed to get current boot mode")); |
| ASSIGN_OR_RETURN( |
| Mode quoted_mode, |
| config_.ToBootMode(BlobFromString(quote.quoted_pcr_value())), |
| _.WithStatus<TPMError>("Failed to get quoted boot mode")); |
| if (mode != quoted_mode) { |
| std::string err_msg = base::StringPrintf( |
| "Quoted boot mode mismatched: current %s vs quoted %s", |
| GetDescriptionForMode(mode).c_str(), |
| GetDescriptionForMode(quoted_mode).c_str()); |
| return MakeStatus<TPMError>(err_msg, TPMRetryAction::kNoRetry); |
| } |
| } |
| |
| ASSIGN_OR_RETURN(const ConfigTpm1::PcrMap& pcr_map, |
| config_.ToPcrMap(device_configs), |
| _.WithStatus<TPMError>("Failed to get PCR map")); |
| if (pcr_map.size() != 1) { |
| return MakeStatus<TPMError>("Wrong number of PCR specified", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| ASSIGN_OR_RETURN( |
| const std::string& pcr_composite, |
| buildPcrComposite(pcr_map.begin()->first, quote.quoted_pcr_value())); |
| // Checks that the quoted value matches the given PCR value by reconstructing |
| // the TPM_PCR_COMPOSITE structure the TPM would create. |
| const std::string pcr_digest = base::SHA1HashString(pcr_composite); |
| |
| const std::string signed_data = quote.quoted_data(); |
| // The PCR digest should appear starting at 8th byte of the quoted data. See |
| // the TPM_QUOTE_INFO structure. |
| if (signed_data.length() < pcr_digest.length() + 8) { |
| return MakeStatus<TPMError>("Quoted data is too short", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!std::equal(pcr_digest.begin(), pcr_digest.end(), |
| signed_data.begin() + 8)) { |
| return false; |
| } |
| return true; |
| } |
| |
| StatusOr<AttestationTpm1::CertifyKeyResult> AttestationTpm1::CertifyKey( |
| Key key, Key identity_key, const std::string& external_data) { |
| ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext()); |
| ASSIGN_OR_RETURN(const KeyTpm1& key_data, key_management_.GetKeyData(key), |
| _.WithStatus<TPMError>("Failed to get the key data")); |
| ASSIGN_OR_RETURN( |
| const KeyTpm1& identity_key_data, |
| key_management_.GetKeyData(identity_key), |
| _.WithStatus<TPMError>("Failed to get the identity key data")); |
| TSS_HKEY key_handle = key_data.key_handle; |
| TSS_HKEY identity_key_handle = identity_key_data.key_handle; |
| |
| TSS_VALIDATION validation; |
| memset(&validation, 0, sizeof(validation)); |
| validation.ulExternalDataLength = external_data.size(); |
| brillo::Blob mutable_external_data = BlobFromString(external_data); |
| validation.rgbExternalData = mutable_external_data.data(); |
| |
| RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Key_CertifyKey( |
| key_handle, identity_key_handle, &validation))) |
| .WithStatus<TPMError>("Failed to call Ospi_Key_CertifyKey"); |
| ScopedTssMemory scoped_certified_data(overalls_, context, validation.rgbData); |
| ScopedTssMemory scoped_proof(overalls_, context, |
| validation.rgbValidationData); |
| |
| return CertifyKeyResult{ |
| .certify_info = |
| TSSBufferAsString(validation.rgbData, validation.ulDataLength), |
| .signature = TSSBufferAsString(validation.rgbValidationData, |
| validation.ulValidationDataLength)}; |
| } |
| |
| StatusOr<attestation::CertifiedKey> AttestationTpm1::CreateCertifiedKey( |
| Key identity_key, |
| attestation::KeyType key_type, |
| attestation::KeyUsage key_usage, |
| KeyRestriction restriction, |
| EndorsementAuth endorsement_auth, |
| const std::string& external_data) { |
| KeyAlgoType key_algo; |
| switch (key_type) { |
| case attestation::KeyType::KEY_TYPE_RSA: |
| key_algo = hwsec::KeyAlgoType::kRsa; |
| break; |
| default: |
| return MakeStatus<TPMError>("Unsupported key algorithm type", |
| TPMRetryAction::kNoRetry); |
| } |
| if (restriction == KeyRestriction::kRestricted) { |
| return MakeStatus<TPMError>("Unsupported restricted key", |
| TPMRetryAction::kNoRetry); |
| } |
| if (endorsement_auth == EndorsementAuth::kEndorsement) { |
| return MakeStatus<TPMError>("Unsupported using endorsement hierarchy", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| ASSIGN_OR_RETURN( |
| const KeyManagement::CreateKeyResult& create_key_result, |
| key_management_.CreateKey( |
| OperationPolicySetting{}, key_algo, |
| KeyManagement::LoadKeyOptions{.auto_reload = true}, |
| KeyManagement::CreateKeyOptions{ |
| .allow_software_gen = false, |
| .allow_decrypt = |
| key_usage == attestation::KeyUsage::KEY_USAGE_DECRYPT, |
| .allow_sign = key_usage == attestation::KeyUsage::KEY_USAGE_SIGN, |
| }), |
| _.WithStatus<TPMError>("Failed to create key")); |
| const ScopedKey& key = create_key_result.key; |
| const brillo::Blob& key_blob = create_key_result.key_blob; |
| |
| ASSIGN_OR_RETURN(const CertifyKeyResult& certify_key_result, |
| CertifyKey(key.GetKey(), identity_key, external_data), |
| _.WithStatus<TPMError>("Failed to certify key")); |
| |
| ASSIGN_OR_RETURN(const KeyTpm1& key_data, |
| key_management_.GetKeyData(key.GetKey()), |
| _.WithStatus<TPMError>("Failed to get key data")); |
| std::string serialized_public_key = BlobToString(key_data.cache.pubkey_blob); |
| |
| ASSIGN_OR_RETURN( |
| const brillo::Blob& public_key_der, |
| key_management_.GetPublicKeyDer(key.GetKey()), |
| _.WithStatus<TPMError>("Failed to get public key in DER format")); |
| |
| attestation::CertifiedKey certified_key; |
| certified_key.set_key_blob(BlobToString(key_blob)); |
| certified_key.set_public_key(BlobToString(public_key_der)); |
| certified_key.set_public_key_tpm_format(serialized_public_key); |
| certified_key.set_certified_key_info(certify_key_result.certify_info); |
| certified_key.set_certified_key_proof(certify_key_result.signature); |
| certified_key.set_key_type(key_type); |
| certified_key.set_key_usage(key_usage); |
| |
| return certified_key; |
| } |
| |
| StatusOr<Attestation::CreateIdentityResult> AttestationTpm1::CreateIdentity( |
| attestation::KeyType key_type) { |
| if (key_type != attestation::KEY_TYPE_RSA) { |
| return MakeStatus<TPMError>("non-RSA identity key is unsupported", |
| TPMRetryAction::kNoRetry); |
| } |
| ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext()); |
| ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetTpmHandle()); |
| ASSIGN_OR_RETURN(base::ScopedClosureRunner owner_handle_cleanup, |
| tss_helper_.SetTpmHandleAsOwner()); |
| |
| // Create fake PCA key. |
| crypto::ScopedRSA fake_pca_key = |
| hwsec_foundation::GenerateRsa(kDefaultTpmRsaKeyBits); |
| if (!fake_pca_key) { |
| return MakeStatus<TPMError>("Failed to generate fake pca key", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| brillo::Blob modulus(RSA_size(fake_pca_key.get())); |
| const BIGNUM* n; |
| RSA_get0_key(fake_pca_key.get(), &n, nullptr, nullptr); |
| if (BN_bn2bin(n, modulus.data()) != modulus.size()) { |
| return MakeStatus<TPMError>("RSA modulus size mismatch", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| // Create a TSS object for the fake PCA public key. |
| TSS_FLAG pca_key_flags = |
| kDefaultTpmRsaKeyFlag | TSS_KEY_TYPE_LEGACY | TSS_KEY_MIGRATABLE; |
| ASSIGN_OR_RETURN( |
| ScopedKey pca_public_key_object, |
| key_management_.CreateRsaPublicKeyObject( |
| modulus, pca_key_flags, TSS_SS_NONE, TSS_ES_RSAESPKCSV15), |
| _.WithStatus<TPMError>("Failed to create PCA public key info")); |
| ASSIGN_OR_RETURN(const KeyTpm1& pca_public_key_data, |
| key_management_.GetKeyData(pca_public_key_object.GetKey()), |
| _.WithStatus<TPMError>("Failed to get PCA public key data")); |
| if (!pca_public_key_data.scoped_key.has_value()) { |
| return MakeStatus<TPMError>("Missing scoped key in PCA public key data", |
| TPMRetryAction::kNoRetry); |
| } |
| TSS_HKEY pca_public_key = pca_public_key_data.scoped_key.value().value(); |
| |
| // Get the fake PCA public key in serialized TPM_PUBKEY form. |
| ASSIGN_OR_RETURN( |
| const brillo::Blob& pca_public_key_blob, |
| GetAttribData(overalls_, context, pca_public_key, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY), |
| _.WithStatus<TPMError>("Failed to get serilized PCA public key")); |
| |
| // Construct an arbitrary unicode label. |
| const char* label_text = "ChromeOS_AIK_1BJNAMQDR4RH44F4ET2KPAOMJMO043K1"; |
| BYTE* label_ascii = |
| const_cast<BYTE*>(reinterpret_cast<const BYTE*>(label_text)); |
| unsigned int label_size = strlen(label_text); |
| ScopedByteArray scoped_label( |
| overalls_.Orspi_Native_To_UNICODE(label_ascii, &label_size)); |
| if (!scoped_label.get()) { |
| return MakeStatus<TPMError>("Failed to create AIK label", |
| TPMRetryAction::kNoRetry); |
| } |
| BYTE* label = scoped_label.get(); |
| std::string identity_label(label, label + label_size); |
| |
| // Initialize a key object to hold the new identity key. |
| ScopedTssKey identity_key(overalls_, context); |
| TSS_FLAG identity_key_flags = kDefaultTpmRsaKeyFlag | TSS_KEY_TYPE_IDENTITY | |
| TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE; |
| RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject( |
| context, TSS_OBJECT_TYPE_RSAKEY, identity_key_flags, |
| identity_key.ptr()))) |
| .WithStatus<TPMError>("Failed to create identity key object"); |
| |
| // Get Storage Root Key (SRK). |
| ASSIGN_OR_RETURN(ScopedKey srk, |
| key_management_.GetPersistentKey( |
| KeyManagement::PersistentKeyType::kStorageRootKey)); |
| ASSIGN_OR_RETURN(const KeyTpm1& srk_data, |
| key_management_.GetKeyData(srk.GetKey())); |
| |
| // Create the identity and receive the request intended for the PCA. |
| uint32_t request_length = 0; |
| ScopedTssMemory request(overalls_, context); |
| RETURN_IF_ERROR( |
| MakeStatus<TPM1Error>(overalls_.Ospi_TPM_CollateIdentityRequest( |
| tpm_handle, srk_data.key_handle, pca_public_key, label_size, label, |
| identity_key, TSS_ALG_3DES, &request_length, request.ptr()))) |
| .WithStatus<TPMError>("Failed to make identity"); |
| |
| // Decrypt and parse the identity request. |
| std::string request_blob(request.value(), request.value() + request_length); |
| ASSIGN_OR_RETURN( |
| const std::string& identity_binding, |
| DecryptIdentityRequest(overalls_, fake_pca_key.get(), request_blob), |
| _.WithStatus<TPMError>("Failed to decrypt the identity request")); |
| brillo::SecureClearBytes(request.value(), request_length); |
| |
| // Get the AIK public key. |
| ASSIGN_OR_RETURN( |
| const brillo::Blob& identity_public_key, |
| GetAttribData(overalls_, context, identity_key, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY), |
| _.WithStatus<TPMError>("Failed to get identity key blob")); |
| ASSIGN_OR_RETURN( |
| const brillo::Blob& identity_public_key_der, |
| key_management_.GetPublicKeyDerFromBlob(identity_public_key), |
| _.WithStatus<TPMError>("Failed to get DER-encoded identity public key")); |
| |
| // Get the AIK blob so we can load it later. |
| ASSIGN_OR_RETURN( |
| const brillo::Blob& identity_key_blob, |
| GetAttribData(overalls_, context, identity_key, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_BLOB), |
| _.WithStatus<TPMError>("Failed to get identity key blob")); |
| |
| // Fill the fields in CreateIdentityResult |
| attestation::IdentityKey identity_key_info; |
| identity_key_info.set_identity_key_type(key_type); |
| identity_key_info.set_identity_public_key_der( |
| BlobToString(identity_public_key_der)); |
| identity_key_info.set_identity_key_blob(BlobToString(identity_key_blob)); |
| |
| attestation::IdentityBinding identity_binding_info; |
| identity_binding_info.set_identity_public_key_tpm_format( |
| BlobToString(identity_public_key)); |
| identity_binding_info.set_identity_binding(identity_binding); |
| identity_binding_info.set_pca_public_key(BlobToString(pca_public_key_blob)); |
| identity_binding_info.set_identity_label(identity_label); |
| identity_binding_info.set_identity_public_key_der( |
| BlobToString(identity_public_key_der)); |
| |
| return Attestation::CreateIdentityResult{ |
| .identity_key = identity_key_info, |
| .identity_binding = identity_binding_info, |
| }; |
| } |
| |
| StatusOr<brillo::SecureBlob> AttestationTpm1::ActivateIdentity( |
| attestation::KeyType key_type, |
| Key identity_key, |
| const attestation::EncryptedIdentityCredential& encrypted_certificate) { |
| ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext()); |
| ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetTpmHandle()); |
| ASSIGN_OR_RETURN(base::ScopedClosureRunner delegate_handle_cleanup, |
| tss_helper_.SetTpmHandleAsDelegate()); |
| ASSIGN_OR_RETURN( |
| const KeyTpm1& identity_key_data, |
| key_management_.GetKeyData(identity_key), |
| _.WithStatus<TPMError>("Failed to get the identity key data")); |
| brillo::Blob asym_ca_contents = |
| BlobFromString(encrypted_certificate.asym_ca_contents()); |
| brillo::Blob sym_ca_attestation = |
| BlobFromString(encrypted_certificate.sym_ca_attestation()); |
| UINT32 credential_length = 0; |
| ScopedTssMemory credential_buffer(overalls_, context); |
| RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_TPM_ActivateIdentity( |
| tpm_handle, identity_key_data.key_handle, |
| asym_ca_contents.size(), asym_ca_contents.data(), |
| sym_ca_attestation.size(), sym_ca_attestation.data(), |
| &credential_length, credential_buffer.ptr()))) |
| .WithStatus<TPMError>("Failed to activate identity"); |
| return brillo::SecureBlob(credential_buffer.value(), |
| credential_buffer.value() + credential_length); |
| } |
| |
| Status AttestationTpm1::FinalizeEnrollmentPreparation() { |
| return RemoveOwnerDependencyForAttestation(tpm_manager_); |
| } |
| |
| } // namespace hwsec |