| // Copyright 2014 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // trunks_client is a command line tool that supports various TPM operations. It |
| // does not provide direct access to the trunksd D-Bus interface. |
| |
| #include <inttypes.h> |
| #include <stdio.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include <base/check.h> |
| #include <base/command_line.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/timer/elapsed_timer.h> |
| #include <brillo/file_utils.h> |
| #include <brillo/syslog_logging.h> |
| #include <crypto/libcrypto-compat.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <openssl/sha.h> |
| |
| #include "trunks/error_codes.h" |
| #include "trunks/hmac_session.h" |
| #include "trunks/multiple_authorization_delegate.h" |
| #include "trunks/password_authorization_delegate.h" |
| #include "trunks/policy_session.h" |
| #include "trunks/scoped_key_handle.h" |
| #include "trunks/session_manager.h" |
| #include "trunks/tpm_state.h" |
| #include "trunks/tpm_utility.h" |
| #include "trunks/trunks_client_test.h" |
| #include "trunks/trunks_dbus_proxy.h" |
| #include "trunks/trunks_factory_impl.h" |
| |
| namespace { |
| |
| using trunks::AuthorizationDelegate; |
| using trunks::CommandTransceiver; |
| using trunks::MultipleAuthorizations; |
| using trunks::TrunksDBusProxy; |
| using trunks::TrunksFactory; |
| using trunks::TrunksFactoryImpl; |
| |
| // Initial PCR0 value at boot (all zeroes). |
| constexpr unsigned char kPcr0ValueZero[SHA256_DIGEST_SIZE] = { |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }; |
| |
| // PCR0 value for Recovery mode. |
| // Equals to SHA256(initial_value | extended_value), where |
| // - initial_value = 0..0 (32 bytes), |
| // - extended_value = SHA1(0x00|0x01|0x00) + 00s to 32 bytes. |
| constexpr unsigned char kPcr0ValueRec[SHA256_DIGEST_SIZE] = { |
| 0x9F, 0x9E, 0xA8, 0x66, 0xD3, 0xF3, 0x4F, 0xE3, 0xA3, 0x11, 0x2A, |
| 0xE9, 0xCB, 0x1F, 0xBA, 0xBC, 0x6F, 0xFE, 0x8C, 0xD2, 0x61, 0xD4, |
| 0x24, 0x93, 0xBC, 0x68, 0x42, 0xA9, 0xE4, 0xF9, 0x3B, 0x3D, |
| }; |
| |
| // PCR0 value for Recovery+Developer mode. |
| // Equals to SHA256(initial_value | extended_value), where |
| // - initial_value = 0..0 (32 bytes), |
| // - extended_value = SHA1(0x01|0x01|0x00) + 00s to 32 bytes. |
| constexpr unsigned char kPcr0ValueRecDev[SHA256_DIGEST_SIZE] = { |
| 0x2A, 0x75, 0x80, 0xE5, 0xDA, 0x28, 0x95, 0x46, 0xF4, 0xD2, 0xE0, |
| 0x50, 0x9C, 0xC6, 0xDE, 0x15, 0x5E, 0xA1, 0x31, 0x81, 0x89, 0x54, |
| 0xD3, 0x6D, 0x49, 0xE0, 0x27, 0xFD, 0x42, 0xB8, 0xC8, 0xF8, |
| }; |
| |
| void PrintUsage() { |
| puts("TPM command options:"); |
| puts(" --allocate_pcr - Configures PCR 0-15 under the SHA256 bank."); |
| puts(" --clear - Clears the TPM. Use before initializing the TPM."); |
| puts(" --help - Prints this message."); |
| puts(" --init_tpm - Initializes a TPM as CrOS firmware does."); |
| puts(" --own - Takes ownership of the TPM with the provided password."); |
| puts(" --owner_password - used to provide an owner password"); |
| puts(" --endorsement_password - used to provide an endorsement password"); |
| puts(" --regression_test - Runs some basic regression tests. If"); |
| puts(" *_password is supplied, it runs tests that"); |
| puts(" require the permissions."); |
| puts(" --startup - Performs startup and self-tests."); |
| puts(" --status - Prints TPM status information."); |
| puts(" --stress_test - Runs some basic stress tests."); |
| puts(" --read_pcr --index=<N> - Reads a PCR and prints the value."); |
| puts(" --extend_pcr --index=<N> --value=<value> - Extends a PCR."); |
| puts(" --tpm_version - Prints TPM versions and IDs similar to tpm_version."); |
| puts(" --rw_version - Prints RW Firmware version."); |
| puts(" --endorsement_public_key - Prints the public endorsement key."); |
| puts(" --key_create (--rsa=<bits>|--ecc) --usage=sign|decrypt|all"); |
| puts(" --key_blob=<file> [--print_time] [--sess_*]"); |
| puts(" - Creates a key and saves the blob to file."); |
| puts(" --key_load --key_blob=<file> [--print_time] [--sess_*]"); |
| puts(" - Loads key from blob, returns handle."); |
| puts(" --key_unload --handle=<H>"); |
| puts(" - Unloads a loaded key."); |
| puts(" --key_sign --handle=<H> --data=<in_file> --signature=<out_file>"); |
| puts(" [--ecc] [--print_time] [--sess_*]"); |
| puts(" - Signs the hash of data using the loaded key."); |
| puts(" --key_info --handle=<H> - Prints information about the loaded key."); |
| puts(" --persistent_keys - Prints all persistent key handles (Up to 128)."); |
| puts(" --transient_keys - Prints all transient key handles (Up to 128)."); |
| puts(" --key_test_short_ecc --handle=<H>."); |
| puts(" --sess_* - group of options providing parameters for auth session:"); |
| puts(" --sess_salted"); |
| puts(" --sess_encrypted"); |
| puts(" --sess_empty_auth (supports --key_create and --key_load)"); |
| puts(" --index_name --index=<N> - print the name of NV index N in hex"); |
| puts(" format."); |
| puts(" --index_data --index=<N> - print the data of NV index N in hex"); |
| puts(" format."); |
| puts(" --ext_command_test - Runs regression tests on extended commands."); |
| puts(" --uds_calc [(--zero|--rec|--recdev)]"); |
| puts(" - Calculate UnDefineSpecial(UDS) digest for the PCR0 value"); |
| puts(" (use current value, if none of the perdefined is specified)"); |
| puts(" --policy_or=<val1>,<val2>,<val3>"); |
| puts(" - Calculate PolicyOR digest for the specified digests"); |
| puts(" --uds_create --index=<index> --size=<size> --digest=<digest>"); |
| puts(" - Create test nvmem space with UDS digest."); |
| puts(" --uds_delete --index=<index> [(--zero|--rec|--recdev)]"); |
| puts(" [--or=<val1>,<val2>,<val3>]"); |
| puts(" - Delete test nvmem space with UDS digest and"); |
| puts(" optional PolicyOR using current PCR0 value."); |
| puts(" --ecc_ek_handle --password=<endorsement passwword"); |
| puts(" - Get the ECC EK handle."); |
| puts(" --test_credential_command --password=<hex endorsement password>"); |
| puts(" -handle=<endorsement key handle>"); |
| puts(" - Perform a closed-loop testing: Create AIK-> Make credential"); |
| puts(" -> Activate credential with EK."); |
| puts(" --test_sign_verify - Perform a closed-loop sign and verify test"); |
| puts(" --test_certify_simple"); |
| puts(" - Perform certifying key and partially verify the output-"); |
| } |
| |
| std::string HexEncode(const std::string& bytes) { |
| return base::HexEncode(bytes.data(), bytes.size()); |
| } |
| |
| std::string HexEncode(const trunks::TPM2B_DIGEST& tpm2b) { |
| return base::HexEncode(tpm2b.buffer, tpm2b.size); |
| } |
| std::string HexEncode(const trunks::TPM2B_ECC_PARAMETER& tpm2b) { |
| return base::HexEncode(tpm2b.buffer, tpm2b.size); |
| } |
| std::string HexEncode(const trunks::TPM2B_PUBLIC_KEY_RSA& tpm2b) { |
| return base::HexEncode(tpm2b.buffer, tpm2b.size); |
| } |
| |
| int OutputToFile(const std::string& file_name, const std::string& data) { |
| if (!brillo::WriteStringToFile(base::FilePath(file_name), data)) { |
| LOG(ERROR) << "Failed to write to " << file_name; |
| return -1; |
| } |
| return 0; |
| } |
| |
| int InputFromFile(const std::string& file_name, std::string* data) { |
| if (!base::ReadFileToString(base::FilePath(file_name), data)) { |
| LOG(ERROR) << "Failed to write to " << file_name; |
| return -1; |
| } |
| return 0; |
| } |
| |
| template <typename OP, typename... ARGS> |
| trunks::TPM_RC CallTimed(bool print_time, |
| const char* op_name, |
| OP op_func, |
| ARGS&&... args) { |
| base::ElapsedTimer timer; |
| trunks::TPM_RC rc = op_func(args...); |
| if (print_time) { |
| printf("%s took %" PRId64 " ms\n", op_name, |
| timer.Elapsed().InMilliseconds()); |
| } |
| return rc; |
| } |
| |
| template <typename OP, typename... ARGS> |
| trunks::TPM_RC CallTpmUtility(bool print_time, |
| const TrunksFactory& factory, |
| const char* op_name, |
| OP op_func, |
| ARGS&&... args) { |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| trunks::TPM_RC rc = CallTimed(print_time, op_name, std::mem_fn(op_func), |
| tpm_utility.get(), args...); |
| LOG_IF(ERROR, rc) << "Error during " << op_name << ": " |
| << trunks::GetErrorString(rc); |
| return rc; |
| } |
| |
| int Startup(const TrunksFactory& factory) { |
| factory.GetTpmUtility()->Shutdown(); |
| return factory.GetTpmUtility()->Startup(); |
| } |
| |
| int Clear(const TrunksFactory& factory) { |
| return factory.GetTpmUtility()->Clear(); |
| } |
| |
| int InitializeTpm(const TrunksFactory& factory) { |
| return factory.GetTpmUtility()->InitializeTpm(); |
| } |
| |
| int AllocatePCR(const TrunksFactory& factory) { |
| trunks::TPM_RC result; |
| result = factory.GetTpmUtility()->AllocatePCR(""); |
| if (result != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error allocating PCR:" << trunks::GetErrorString(result); |
| return result; |
| } |
| factory.GetTpmUtility()->Shutdown(); |
| return factory.GetTpmUtility()->Startup(); |
| } |
| |
| int TakeOwnership(const std::string& owner_password, |
| const TrunksFactory& factory) { |
| trunks::TPM_RC rc; |
| rc = factory.GetTpmUtility()->TakeOwnership(owner_password, owner_password, |
| owner_password); |
| if (rc) { |
| LOG(ERROR) << "Error taking ownership: " << trunks::GetErrorString(rc); |
| return rc; |
| } |
| return 0; |
| } |
| |
| int DumpStatus(const TrunksFactory& factory) { |
| trunks::TpmState* state = factory.GetTpmState(); |
| trunks::TPM_RC result = state->Refresh(); |
| if (result != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Failed to read TPM state: " |
| << trunks::GetErrorString(result); |
| return result; |
| } |
| printf("Owner password set: %s\n", |
| state->IsOwnerPasswordSet() ? "true" : "false"); |
| printf("Endorsement password set: %s\n", |
| state->IsEndorsementPasswordSet() ? "true" : "false"); |
| printf("Lockout password set: %s\n", |
| state->IsLockoutPasswordSet() ? "true" : "false"); |
| printf("Ownership status: %s\n", state->IsOwned() ? "true" : "false"); |
| printf("In lockout: %s\n", state->IsInLockout() ? "true" : "false"); |
| printf("Platform hierarchy enabled: %s\n", |
| state->IsPlatformHierarchyEnabled() ? "true" : "false"); |
| printf("Storage hierarchy enabled: %s\n", |
| state->IsStorageHierarchyEnabled() ? "true" : "false"); |
| printf("Endorsement hierarchy enabled: %s\n", |
| state->IsEndorsementHierarchyEnabled() ? "true" : "false"); |
| printf("Is Tpm enabled: %s\n", state->IsEnabled() ? "true" : "false"); |
| printf("Was shutdown orderly: %s\n", |
| state->WasShutdownOrderly() ? "true" : "false"); |
| printf("Is RSA supported: %s\n", state->IsRSASupported() ? "true" : "false"); |
| printf("Is ECC supported: %s\n", state->IsECCSupported() ? "true" : "false"); |
| printf("Lockout Counter: %u\n", state->GetLockoutCounter()); |
| printf("Lockout Threshold: %u\n", state->GetLockoutThreshold()); |
| printf("Lockout Interval: %u\n", state->GetLockoutInterval()); |
| printf("Lockout Recovery: %u\n", state->GetLockoutRecovery()); |
| return 0; |
| } |
| |
| int ReadPCR(const TrunksFactory& factory, int index) { |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| std::string value; |
| trunks::TPM_RC result = tpm_utility->ReadPCR(index, &value); |
| if (result) { |
| LOG(ERROR) << "ReadPCR: " << trunks::GetErrorString(result); |
| return result; |
| } |
| printf("PCR Value: %s\n", HexEncode(value).c_str()); |
| return 0; |
| } |
| |
| int ExtendPCR(const TrunksFactory& factory, |
| int index, |
| const std::string& value) { |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| trunks::TPM_RC result = tpm_utility->ExtendPCR(index, value, nullptr); |
| if (result) { |
| LOG(ERROR) << "ExtendPCR: " << trunks::GetErrorString(result); |
| return result; |
| } |
| return 0; |
| } |
| |
| char* TpmPropertyToStr(uint32_t value) { |
| static char str[5]; |
| char c; |
| int i = 0; |
| int shift = 24; |
| for (; i < 4; i++, shift -= 8) { |
| c = static_cast<char>((value >> shift) & 0xFF); |
| if (c == 0) |
| break; |
| str[i] = (c >= 32 && c < 127) ? c : ' '; |
| } |
| str[i] = 0; |
| return str; |
| } |
| |
| int TpmVersion(const TrunksFactory& factory) { |
| trunks::TpmState* state = factory.GetTpmState(); |
| trunks::TPM_RC result = state->Initialize(); |
| if (result != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Failed to read TPM state: " |
| << trunks::GetErrorString(result); |
| return result; |
| } |
| printf(" TPM 2.0 Version Info:\n"); |
| // Print Chip Version for compatibility with tpm_version, hardcoded as |
| // there's no 2.0 equivalent (TPM_PT_FAMILY_INDICATOR is const). |
| printf(" Chip Version: 2.0.0.0\n"); |
| uint32_t family = state->GetTpmFamily(); |
| printf(" Spec Family: %08" PRIx32 "\n", family); |
| printf(" Spec Family String: %s\n", TpmPropertyToStr(family)); |
| printf(" Spec Level: %" PRIu32 "\n", |
| state->GetSpecificationLevel()); |
| printf(" Spec Revision: %" PRIu32 "\n", |
| state->GetSpecificationRevision()); |
| uint32_t manufacturer = state->GetManufacturer(); |
| printf(" Manufacturer Info: %08" PRIx32 "\n", manufacturer); |
| printf(" Manufacturer String: %s\n", TpmPropertyToStr(manufacturer)); |
| printf(" Vendor ID: %s\n", state->GetVendorIDString().c_str()); |
| printf(" TPM Model: %08" PRIx32 "\n", state->GetTpmModel()); |
| printf(" Firmware Version: %016" PRIx64 "\n", |
| state->GetFirmwareVersion()); |
| |
| return 0; |
| } |
| |
| int GetRwVersion(const TrunksFactory& factory) { |
| trunks::TPM_RC rc; |
| uint32_t epoch; |
| uint32_t major; |
| uint32_t minor; |
| rc = factory.GetTpmUtility()->GetRwVersion(&epoch, &major, &minor); |
| if (rc) { |
| LOG(ERROR) << "Error getting RW version: " << trunks::GetErrorString(rc); |
| return rc; |
| } |
| printf(" RW Version Info:\n"); |
| printf(" Epoch: %08" PRIx32 "\n", epoch); |
| printf(" Major: %08" PRIx32 "\n", major); |
| printf(" Minor: %08" PRIx32 "\n", minor); |
| return 0; |
| } |
| |
| int EndorsementPublicKey(const TrunksFactory& factory) { |
| std::string ekm; |
| factory.GetTpmUtility()->GetPublicRSAEndorsementKeyModulus(&ekm); |
| std::string ekm_hex = HexEncode(ekm); |
| printf(" Public Endorsement Key Modulus: %s\n", ekm_hex.c_str()); |
| return 0; |
| } |
| |
| int KeyStartSession(trunks::SessionManager* session_manager, |
| base::CommandLine* cl, |
| trunks::HmacAuthorizationDelegate* delegate) { |
| bool salted = cl->HasSwitch("sess_salted"); |
| bool encrypted = cl->HasSwitch("sess_encrypted"); |
| bool print_time = cl->HasSwitch("print_time"); |
| |
| trunks::TPM_RC rc = |
| CallTimed(print_time, "StartSession", |
| std::mem_fn(&trunks::SessionManager::StartSession), |
| session_manager, trunks::TPM_SE_HMAC, trunks::TPM_RH_NULL, "", |
| salted, encrypted, delegate); |
| LOG_IF(ERROR, rc) << "Failed to start session: " |
| << trunks::GetErrorString(rc); |
| return rc; |
| } |
| |
| int GetKeyUsage(const std::string& option_value, |
| trunks::TpmUtility::AsymmetricKeyUsage* key_usage) { |
| const std::map<std::string, trunks::TpmUtility::AsymmetricKeyUsage> mapping = |
| // NOLINTNEXTLINE(whitespace/braces) |
| { |
| {"decrypt", trunks::TpmUtility::kDecryptKey}, |
| {"sign", trunks::TpmUtility::kSignKey}, |
| {"all", trunks::TpmUtility::kDecryptAndSignKey}, |
| }; |
| auto entry = mapping.find(option_value); |
| if (entry == mapping.end()) { |
| LOG(ERROR) << "Unrecognized key usage: " << option_value; |
| return -1; |
| } |
| *key_usage = entry->second; |
| return 0; |
| } |
| |
| int KeyInfo(bool print_time, const TrunksFactory& factory, uint32_t handle) { |
| trunks::TPMT_PUBLIC public_area; |
| if (CallTpmUtility(print_time, factory, "GetKeyPublicArea", |
| &trunks::TpmUtility::GetKeyPublicArea, handle, |
| &public_area)) { |
| return -1; |
| } |
| puts("Key public area:"); |
| printf(" type: %#x\n", public_area.type); |
| printf(" name_alg: %#x\n", public_area.name_alg); |
| printf(" attributes: %#x\n", public_area.object_attributes); |
| printf(" auth_policy: %s\n", HexEncode(public_area.auth_policy).c_str()); |
| if (public_area.type == trunks::TPM_ALG_RSA) { |
| printf(" RSA modulus: %s\n", HexEncode(public_area.unique.rsa).c_str()); |
| } else if (public_area.type == trunks::TPM_ALG_ECC) { |
| printf(" ECC X: %s\n", HexEncode(public_area.unique.ecc.x).c_str()); |
| printf(" ECC Y: %s\n", HexEncode(public_area.unique.ecc.y).c_str()); |
| } |
| |
| std::string key_name; |
| if (CallTpmUtility(print_time, factory, "GetKeyName", |
| &trunks::TpmUtility::GetKeyName, handle, &key_name)) { |
| return -1; |
| } |
| printf("Key name: %s\n", HexEncode(key_name).c_str()); |
| |
| return 0; |
| } |
| |
| int PersistentKeys(const TrunksFactory& factory) { |
| trunks::TPMI_YES_NO more_data = YES; |
| trunks::TPMS_CAPABILITY_DATA capability_data; |
| trunks::TPM_RC rc = factory.GetTpm()->GetCapabilitySync( |
| trunks::TPM_CAP_HANDLES, trunks::PERSISTENT_FIRST, 128 /*property_count*/, |
| &more_data, &capability_data, nullptr /*authorization_delegate*/); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << ": Error querying handles: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| const trunks::TPML_HANDLE& handles = capability_data.data.handles; |
| if (handles.count == 0) { |
| puts("No persistent key found."); |
| return 0; |
| } |
| puts("Persistent keys:"); |
| for (int i = 0; i < handles.count; ++i) { |
| printf(" %#x\n", handles.handle[i]); |
| } |
| return 0; |
| } |
| |
| int TransientKeys(const TrunksFactory& factory) { |
| trunks::TPMI_YES_NO more_data = YES; |
| trunks::TPMS_CAPABILITY_DATA capability_data; |
| trunks::TPM_RC rc = factory.GetTpm()->GetCapabilitySync( |
| trunks::TPM_CAP_HANDLES, trunks::TRANSIENT_FIRST, 128 /*property_count*/, |
| &more_data, &capability_data, nullptr /*authorization_delegate*/); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << ": Error querying handles: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| const trunks::TPML_HANDLE& handles = capability_data.data.handles; |
| if (handles.count == 0) { |
| puts("No transient key found."); |
| return 0; |
| } |
| puts("Transient keys:"); |
| for (int i = 0; i < handles.count; ++i) { |
| printf(" %#x\n", handles.handle[i]); |
| } |
| return 0; |
| } |
| |
| void PrintEccPoint(const char* name, const trunks::TPM2B_ECC_POINT& point) { |
| printf("%s point: [%u]", name, point.size); |
| printf(" X=[%u] %s, ", point.point.x.size, HexEncode(point.point.x).c_str()); |
| printf(" Y=[%u] %s\n", point.point.y.size, HexEncode(point.point.y).c_str()); |
| } |
| |
| int KeyTestShortEcc(const TrunksFactory& factory, uint32_t handle) { |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| std::string name; |
| trunks::TPM_RC rc = tpm_utility->GetKeyName(handle, &name); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error during GetKeyName: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| trunks::HmacAuthorizationDelegate delegate; |
| std::unique_ptr<trunks::SessionManager> session_manager = |
| factory.GetSessionManager(); |
| rc = session_manager->StartSession(trunks::TPM_SE_HMAC, trunks::TPM_RH_NULL, |
| "", false, false, &delegate); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error during StartSession: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| trunks::TPM2B_ECC_POINT z_point; |
| trunks::TPM2B_ECC_POINT pub_point; |
| |
| do { |
| rc = factory.GetTpm()->ECDH_KeyGenSync(handle, name, &z_point, &pub_point, |
| nullptr); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error during ECDH_KeyGen: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| } while (pub_point.point.x.buffer[0] && pub_point.point.y.buffer[0]); |
| PrintEccPoint("z", z_point); |
| PrintEccPoint("pub", pub_point); |
| |
| trunks::TPM2B_ECC_POINT out1_point; |
| rc = factory.GetTpm()->ECDH_ZGenSync(handle, name, pub_point, &out1_point, |
| &delegate); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error during ECDH_ZGen (pass 1): " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| PrintEccPoint("out1", out1_point); |
| |
| if (pub_point.point.x.buffer[0] == 0) { |
| pub_point.point.x.size--; |
| memmove(pub_point.point.x.buffer, pub_point.point.x.buffer + 1, |
| pub_point.point.x.size); |
| } |
| if (pub_point.point.y.buffer[0] == 0) { |
| pub_point.point.y.size--; |
| memmove(pub_point.point.y.buffer, pub_point.point.y.buffer + 1, |
| pub_point.point.y.size); |
| } |
| PrintEccPoint("shortened pub", pub_point); |
| |
| trunks::TPM2B_ECC_POINT out2_point; |
| rc = factory.GetTpm()->ECDH_ZGenSync(handle, name, pub_point, &out2_point, |
| &delegate); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error during ECDH_ZGen (pass 2): " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| PrintEccPoint("out2", out2_point); |
| |
| if (out1_point.point.x.size != out2_point.point.x.size || |
| out1_point.point.y.size != out2_point.point.y.size || |
| memcmp(out1_point.point.x.buffer, out2_point.point.x.buffer, |
| out1_point.point.x.size) || |
| memcmp(out1_point.point.y.buffer, out2_point.point.y.buffer, |
| out1_point.point.y.size)) { |
| LOG(ERROR) << "Different out points produced by pass 1 and pass 2"; |
| return -1; |
| } |
| |
| printf("SUCCESS\n"); |
| return 0; |
| } |
| |
| int PrintIndexNameInHex(const TrunksFactory& factory, int index) { |
| // Mask out the nv index handle so the user can either add or not add it |
| // themselves. |
| index &= trunks::HR_HANDLE_MASK; |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| std::string name; |
| trunks::TPM_RC rc = tpm_utility->GetNVSpaceName(index, &name); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error getting NV index name: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| // Also, print the name returned by TPM directly.. |
| trunks::TPM2B_NAME nvram_name; |
| trunks::TPM2B_NV_PUBLIC public_area; |
| public_area.nv_public.nv_index = 0; |
| const trunks::UINT32 nv_index = trunks::NV_INDEX_FIRST + index; |
| rc = factory.GetTpm()->NV_ReadPublicSync(nv_index, "", &public_area, |
| &nvram_name, nullptr); |
| std::string name_from_tpm(nvram_name.name, nvram_name.name + nvram_name.size); |
| printf("NV Index name: %s\n", HexEncode(name).c_str()); |
| printf("NV Index name from tpm: %s\n", HexEncode(name_from_tpm).c_str()); |
| return 0; |
| } |
| |
| int PrintIndexDataInHex(const TrunksFactory& factory, int index) { |
| // Mask out the nv index handle so the user can either add or not add it |
| // themselves. |
| index &= trunks::HR_HANDLE_MASK; |
| std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility(); |
| trunks::TPMS_NV_PUBLIC nvram_public; |
| trunks::TPM_RC rc = tpm_utility->GetNVSpacePublicArea(index, &nvram_public); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error reading NV space public area: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| std::unique_ptr<AuthorizationDelegate> empty_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| std::string nvram_data; |
| rc = |
| tpm_utility->ReadNVSpace(index, /*offset=*/0, nvram_public.data_size, |
| /*using_owner_authorization=*/false, &nvram_data, |
| empty_password_authorization.get()); |
| if (rc != trunks::TPM_RC_SUCCESS) { |
| LOG(ERROR) << "Error reading NV space: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| printf("NV Index data: %s\n", HexEncode(nvram_data).c_str()); |
| return 0; |
| } |
| |
| std::string DigestString(const unsigned char* digest_value) { |
| auto ptr = reinterpret_cast<const char*>(digest_value); |
| return std::string(ptr, SHA256_DIGEST_SIZE); |
| } |
| |
| std::string GetPcr0Digest(const TrunksFactory& factory, base::CommandLine* cl) { |
| if (cl->HasSwitch("zero")) { |
| return DigestString(kPcr0ValueZero); |
| } else if (cl->HasSwitch("rec")) { |
| return DigestString(kPcr0ValueRec); |
| } else if (cl->HasSwitch("recdev")) { |
| return DigestString(kPcr0ValueRecDev); |
| } |
| std::string pcr_digest; |
| if (CallTpmUtility(false, factory, "ReadPCR", &trunks::TpmUtility::ReadPCR, 0, |
| &pcr_digest)) { |
| return std::string(); |
| } |
| return pcr_digest; |
| } |
| |
| std::unique_ptr<trunks::PolicySession> GetUDSSession( |
| const TrunksFactory& factory, base::CommandLine* cl, bool trial) { |
| auto session = trial ? factory.GetTrialSession() : factory.GetPolicySession(); |
| if (!session) { |
| LOG(ERROR) << "Error during Get" << (trial ? "Trial" : "Policy") |
| << "Session"; |
| return nullptr; |
| } |
| |
| trunks::TPM_RC rc = session->StartUnboundSession(false, false); |
| if (rc) { |
| LOG(ERROR) << "Error during StartUnboundSession: " |
| << trunks::GetErrorString(rc); |
| return nullptr; |
| } |
| |
| rc = session->PolicyCommandCode(trunks::TPM_CC_NV_UndefineSpaceSpecial); |
| if (rc) { |
| LOG(ERROR) << "Error during PolicyCommandCode: " |
| << trunks::GetErrorString(rc); |
| return nullptr; |
| } |
| |
| const std::string pcr_digest = GetPcr0Digest(factory, cl); |
| if (pcr_digest.empty()) { |
| return nullptr; |
| } |
| |
| const std::map<uint32_t, std::string> pcrs{{0, pcr_digest}}; |
| rc = session->PolicyPCR(pcrs); |
| if (rc) { |
| LOG(ERROR) << "Error during PolicyPCR: " << trunks::GetErrorString(rc); |
| return nullptr; |
| } |
| |
| return session; |
| } |
| |
| int PrintEccEndorsementKeyHandle(const trunks::TrunksFactory& factory, |
| const std::string& endorsement_password) { |
| std::unique_ptr<AuthorizationDelegate> endorsement_password_authorization = |
| factory.GetPasswordAuthorization(endorsement_password); |
| // Won't be used because we don't persist ECC EK. |
| std::unique_ptr<AuthorizationDelegate> owner_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| trunks::TPM_HANDLE endorsement_key_handle = 0; |
| trunks::TPM_RC rc = factory.GetTpmUtility()->GetEndorsementKey( |
| trunks::TPM_ALG_ECC, endorsement_password_authorization.get(), |
| owner_password_authorization.get(), &endorsement_key_handle); |
| if (rc) { |
| LOG(ERROR) << "Error getting ECC EK handle: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| printf("Loaded key handle: %#x\n", endorsement_key_handle); |
| return 0; |
| } |
| |
| bool TestCredentialCommand(const trunks::TrunksFactory& factory, |
| const std::string& endorsement_password, |
| trunks::TPM_HANDLE endorsement_key_handle) { |
| std::unique_ptr<AuthorizationDelegate> empty_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| std::string aik_blob; |
| trunks::TPM_RC rc = factory.GetTpmUtility()->CreateIdentityKey( |
| trunks::TPM_ALG_ECC, empty_password_authorization.get(), &aik_blob); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::CreateIdentityKey()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| // load aik. |
| trunks::TPM_HANDLE aik_handle = 0; |
| rc = factory.GetTpmUtility()->LoadKey( |
| aik_blob, empty_password_authorization.get(), &aik_handle); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::LoadKey()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Make sure the object is flushed. |
| trunks::ScopedKeyHandle scoped_aik(factory, aik_handle); |
| scoped_aik.set_synchronized(true); |
| |
| const std::string fake_credential = "fake credential"; |
| std::string endorsement_key_name; |
| rc = factory.GetTpmUtility()->GetKeyName(endorsement_key_handle, |
| &endorsement_key_name); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::GetKeyName()` for ek: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| std::string aik_name; |
| rc = factory.GetTpmUtility()->GetKeyName(aik_handle, &aik_name); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::GetKeyName()` for aik: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| trunks::TPM2B_ID_OBJECT credential_blob = {}; |
| trunks::TPM2B_ENCRYPTED_SECRET secret = {}; |
| rc = factory.GetTpm()->MakeCredentialSync( |
| endorsement_key_handle, endorsement_key_name, |
| trunks::Make_TPM2B_DIGEST(fake_credential), |
| trunks::Make_TPM2B_NAME(aik_name), &credential_blob, &secret, nullptr); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `Tpm::MakeCredentialSync()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Prepare the auth session. |
| std::unique_ptr<AuthorizationDelegate> endorsement_password_authorization = |
| factory.GetPasswordAuthorization(endorsement_password); |
| |
| std::unique_ptr<trunks::PolicySession> session = factory.GetPolicySession(); |
| rc = session->StartUnboundSession(false /* salted */, |
| false /* enable_encryption */); |
| if (rc) { |
| LOG(ERROR) << "Failed to start policy session: " |
| << trunks::GetErrorString(rc); |
| 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); |
| |
| rc = session->PolicySecret(auth_entity, auth_entity_name, std::string(), |
| std::string(), std::string(), 0, |
| endorsement_password_authorization.get()); |
| if (rc) { |
| LOG(ERROR) << __func__ |
| << ": Failed to set the secret: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Activate the credential. |
| MultipleAuthorizations authorization; |
| authorization.AddAuthorizationDelegate(empty_password_authorization.get()); |
| authorization.AddAuthorizationDelegate(session->GetDelegate()); |
| |
| trunks::TPM2B_DIGEST blob_out; |
| LOG(WARNING) << secret.size; |
| |
| rc = factory.GetTpm()->ActivateCredentialSync( |
| aik_handle, aik_name, endorsement_key_handle, endorsement_key_name, |
| credential_blob, secret, &blob_out, &authorization); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `Tpm::ActivateCredentialSync()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| std::string activated_credential = trunks::StringFrom_TPM2B_DIGEST(blob_out); |
| printf("fake credential in: %s\n", fake_credential.c_str()); |
| printf("fake credential out: %s\n", activated_credential.c_str()); |
| return fake_credential == activated_credential; |
| } |
| |
| bool TestSignVerify(const trunks::TrunksFactory& factory) { |
| std::unique_ptr<AuthorizationDelegate> empty_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| std::string blob, creation_blob; |
| trunks::TPM_RC rc = factory.GetTpmUtility()->CreateECCKeyPair( |
| trunks::TpmUtility::AsymmetricKeyUsage::kSignKey, |
| trunks::TPM_ECC_NIST_P256, |
| /*password=*/"", |
| /*policy_digest=*/"", |
| /*use_only_policy_authorization=*/false, |
| /*creation_pcr_indexes=*/{}, empty_password_authorization.get(), &blob, |
| &creation_blob); |
| if (rc) { |
| LOG(ERROR) << "Failed to create key: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| trunks::TPM_HANDLE handle; |
| rc = factory.GetTpmUtility()->LoadKey( |
| blob, empty_password_authorization.get(), &handle); |
| if (rc) { |
| LOG(ERROR) << "Failed to load key: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| trunks::ScopedKeyHandle scoped_key(factory, handle); |
| scoped_key.set_synchronized(true); |
| |
| const std::string data = "At the end it doesn't even matter."; |
| trunks::TPM2B_DIGEST digest = {}; |
| trunks::TPMT_TK_HASHCHECK validation = {}; |
| rc = factory.GetTpm()->HashSync(trunks::Make_TPM2B_MAX_BUFFER(data), |
| trunks::TPM_ALG_SHA256, trunks::TPM_RH_OWNER, |
| &digest, &validation, |
| /*authorization_delegate=*/nullptr); |
| if (rc) { |
| LOG(ERROR) << "Failed to hash: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| trunks::TPMT_SIG_SCHEME scheme = { |
| .scheme = trunks::TPM_ALG_ECDSA, |
| .details.any.hash_alg = trunks::TPM_ALG_SHA256, |
| }; |
| trunks::TPMT_SIGNATURE signature = {}; |
| rc = factory.GetTpm()->SignSync( |
| handle, /*key_handle_name=*/"not used w/o auth session", digest, scheme, |
| validation, &signature, empty_password_authorization.get()); |
| if (rc) { |
| LOG(ERROR) << "Failed to sign: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| trunks::TPMT_TK_VERIFIED verified = {}; |
| rc = factory.GetTpm()->VerifySignatureSync( |
| handle, /*key_handle_name=*/"not used w/o auth session", digest, |
| signature, &verified, /*authorization_delegate=*/nullptr); |
| if (rc) { |
| LOG(ERROR) << "Failed to verify signature: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TestCertify(const trunks::TrunksFactory& factory) { |
| std::unique_ptr<AuthorizationDelegate> empty_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| std::string aik_blob; |
| trunks::TPM_RC rc = factory.GetTpmUtility()->CreateIdentityKey( |
| trunks::TPM_ALG_ECC, empty_password_authorization.get(), &aik_blob); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::CreateIdentityKey()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| std::string blob, creation_blob; |
| rc = factory.GetTpmUtility()->CreateECCKeyPair( |
| trunks::TpmUtility::AsymmetricKeyUsage::kSignKey, |
| trunks::TPM_ECC_NIST_P256, |
| /*password=*/"", |
| /*policy_digest=*/"", |
| /*use_only_policy_authorization=*/false, |
| /*creation_pcr_indexes=*/{}, empty_password_authorization.get(), &blob, |
| &creation_blob); |
| if (rc) { |
| LOG(ERROR) << "Failed to create key: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Load aik. |
| trunks::TPM_HANDLE aik_handle = 0; |
| rc = factory.GetTpmUtility()->LoadKey( |
| aik_blob, empty_password_authorization.get(), &aik_handle); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::LoadKey()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Load key to be certified. |
| trunks::TPM_HANDLE handle = 0; |
| rc = factory.GetTpmUtility()->LoadKey( |
| blob, empty_password_authorization.get(), &handle); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `TpmUtility::LoadKey()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Make sure the object is flushed. |
| trunks::ScopedKeyHandle scoped_aik(factory, aik_handle); |
| scoped_aik.set_synchronized(true); |
| trunks::ScopedKeyHandle scoped_key(factory, handle); |
| scoped_key.set_synchronized(true); |
| |
| // Certify. |
| MultipleAuthorizations authorization; |
| authorization.AddAuthorizationDelegate(empty_password_authorization.get()); |
| authorization.AddAuthorizationDelegate(empty_password_authorization.get()); |
| |
| trunks::TPMT_SIG_SCHEME scheme = { |
| .scheme = trunks::TPM_ALG_ECDSA, |
| .details.any.hash_alg = trunks::TPM_ALG_SHA256, |
| }; |
| |
| trunks::TPM2B_ATTEST certify_info = {}; |
| trunks::TPMT_SIGNATURE signature = {}; |
| |
| rc = factory.GetTpm()->CertifySync( |
| handle, |
| /*object_handle_name=*/"not used w/o auth session", aik_handle, |
| /*sign_handle_name=*/"not used w/o auth session", |
| trunks::Make_TPM2B_DATA("qualifying data"), scheme, &certify_info, |
| &signature, &authorization); |
| |
| if (rc) { |
| LOG(ERROR) << "Failed to certify: " << trunks::GetErrorString(rc); |
| return false; |
| } |
| |
| // Only perform a simple verification against the output `TPMS_ATTEST`. |
| trunks::TPMS_ATTEST attest = {}; |
| std::string buffer = StringFrom_TPM2B_ATTEST(certify_info); |
| rc = trunks::Parse_TPMS_ATTEST(&buffer, &attest, nullptr); |
| if (rc) { |
| LOG(ERROR) << "Failed to call `Parse_TPMS_ATTEST()`: " |
| << trunks::GetErrorString(rc); |
| return false; |
| } |
| if (!buffer.empty()) { |
| LOG(ERROR) << "wrong size of `TPMS_ATTEST` buffer."; |
| return false; |
| } |
| if (attest.magic != trunks::TPM_GENERATED_VALUE) { |
| LOG(ERROR) << "Unexpected magic number: " << attest.magic; |
| return false; |
| } |
| if (attest.type != trunks::TPM_ST_ATTEST_CERTIFY) { |
| LOG(ERROR) << "Unexpected attest type: " << attest.type; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::vector<std::string> BreakByDelim(const std::string& value, |
| const std::string& delim) { |
| std::vector<std::string> result; |
| size_t beg = 0; |
| size_t end = value.find(delim); |
| while (end != std::string::npos) { |
| result.emplace_back(value, beg, end - beg); |
| beg = end + delim.size(); |
| end = value.find(delim, beg); |
| } |
| result.emplace_back(value, beg); |
| return result; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| base::CommandLine::Init(argc, argv); |
| brillo::InitLog(brillo::kLogToStderr); |
| base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); |
| if (cl->HasSwitch("help")) { |
| puts("Trunks Client: A command line tool to access the TPM."); |
| PrintUsage(); |
| return 0; |
| } |
| |
| std::unique_ptr<TrunksDBusProxy> dbus_proxy = |
| std::make_unique<TrunksDBusProxy>(); |
| |
| CHECK(dbus_proxy->Init()) << "Failed to initialize D-Bus proxy."; |
| |
| TrunksFactoryImpl factory(dbus_proxy.get()); |
| CHECK(factory.Initialize()) << "Failed to initialize trunks factory."; |
| |
| std::unique_ptr<AuthorizationDelegate> empty_password_authorization = |
| factory.GetPasswordAuthorization(""); |
| |
| bool print_time = cl->HasSwitch("print_time"); |
| if (cl->HasSwitch("status")) { |
| return DumpStatus(factory); |
| } |
| if (cl->HasSwitch("startup")) { |
| return Startup(factory); |
| } |
| if (cl->HasSwitch("clear")) { |
| return Clear(factory); |
| } |
| if (cl->HasSwitch("init_tpm")) { |
| return InitializeTpm(factory); |
| } |
| if (cl->HasSwitch("allocate_pcr")) { |
| return AllocatePCR(factory); |
| } |
| |
| if (cl->HasSwitch("own")) { |
| return TakeOwnership(cl->GetSwitchValueASCII("owner_password"), factory); |
| } |
| if (cl->HasSwitch("regression_test")) { |
| trunks::TrunksClientTest test(factory); |
| LOG(INFO) << "Running RNG test."; |
| if (!test.RNGTest()) { |
| LOG(ERROR) << "Error running RNGtest."; |
| return -1; |
| } |
| LOG(INFO) << "Running RSA key tests."; |
| if (!test.SignTest()) { |
| LOG(ERROR) << "Error running SignTest."; |
| return -1; |
| } |
| if (!test.DecryptTest()) { |
| LOG(ERROR) << "Error running DecryptTest."; |
| return -1; |
| } |
| if (!test.ImportTest()) { |
| LOG(ERROR) << "Error running ImportTest."; |
| return -1; |
| } |
| if (!test.AuthChangeTest()) { |
| LOG(ERROR) << "Error running AuthChangeTest."; |
| return -1; |
| } |
| if (!test.VerifyKeyCreationTest()) { |
| LOG(ERROR) << "Error running VerifyKeyCreationTest."; |
| return -1; |
| } |
| LOG(INFO) << "Running Sealed Data test."; |
| if (!test.SealedDataTest()) { |
| LOG(ERROR) << "Error running SealedDataTest."; |
| return -1; |
| } |
| LOG(INFO) << "Running Sealed to Multiple PCR Data test."; |
| if (!test.SealedToMultiplePCRDataTest()) { |
| LOG(ERROR) << "Error running SealedToMultiplePCRDataTest."; |
| return -1; |
| } |
| LOG(INFO) << "Running PCR test."; |
| if (!test.PCRTest()) { |
| LOG(ERROR) << "Error running PCRTest."; |
| return -1; |
| } |
| LOG(INFO) << "Running policy tests."; |
| if (!test.PolicyAuthValueTest()) { |
| LOG(ERROR) << "Error running PolicyAuthValueTest."; |
| return -1; |
| } |
| if (!test.PolicyAndTest()) { |
| LOG(ERROR) << "Error running PolicyAndTest."; |
| return -1; |
| } |
| if (!test.PolicyOrTest()) { |
| LOG(ERROR) << "Error running PolicyOrTest."; |
| return -1; |
| } |
| LOG(INFO) << "Running identity key test."; |
| if (!test.IdentityKeyTest()) { |
| LOG(ERROR) << "Error running IdentityKeyTest."; |
| return -1; |
| } |
| if (cl->HasSwitch("owner_password")) { |
| std::string owner_password = cl->GetSwitchValueASCII("owner_password"); |
| LOG(INFO) << "Running NVRAM test."; |
| if (!test.NvramTest(owner_password)) { |
| LOG(ERROR) << "Error running NvramTest."; |
| return -1; |
| } |
| if (cl->HasSwitch("endorsement_password")) { |
| std::string endorsement_password = |
| cl->GetSwitchValueASCII("endorsement_password"); |
| LOG(INFO) << "Running endorsement test."; |
| if (!test.EndorsementTest(endorsement_password, owner_password)) { |
| LOG(ERROR) << "Error running EndorsementTest."; |
| return -1; |
| } |
| } |
| } |
| LOG(INFO) << "All tests were run successfully."; |
| return 0; |
| } |
| if (cl->HasSwitch("ext_command_test")) { |
| trunks::TrunksClientTest test(factory); |
| LOG(INFO) << "Running PolicyFidoSigned test."; |
| if (!test.PolicyFidoSignedTest(trunks::TPM_ALG_RSASSA)) { |
| LOG(ERROR) << "Error running PolicyFidoSigned with RSASSA scheme."; |
| return -1; |
| } |
| if (!test.PolicyFidoSignedTest(trunks::TPM_ALG_ECDSA)) { |
| LOG(ERROR) << "Error running PolicyFidoSigned with ECDSA scheme."; |
| return -1; |
| } |
| LOG(INFO) << "All tests were run successfully."; |
| return 0; |
| } |
| if (cl->HasSwitch("stress_test")) { |
| LOG(INFO) << "Running stress tests."; |
| trunks::TrunksClientTest test(factory); |
| if (!test.ManyKeysTest()) { |
| LOG(ERROR) << "Error running ManyKeysTest."; |
| return -1; |
| } |
| if (!test.ManySessionsTest()) { |
| LOG(ERROR) << "Error running ManySessionsTest."; |
| return -1; |
| } |
| return 0; |
| } |
| if (cl->HasSwitch("read_pcr") && cl->HasSwitch("index")) { |
| return ReadPCR(factory, atoi(cl->GetSwitchValueASCII("index").c_str())); |
| } |
| if (cl->HasSwitch("extend_pcr") && cl->HasSwitch("index") && |
| cl->HasSwitch("value")) { |
| return ExtendPCR(factory, atoi(cl->GetSwitchValueASCII("index").c_str()), |
| cl->GetSwitchValueASCII("value")); |
| } |
| if (cl->HasSwitch("tpm_version")) { |
| return TpmVersion(factory); |
| } |
| if (cl->HasSwitch("rw_version")) { |
| return GetRwVersion(factory); |
| } |
| if (cl->HasSwitch("endorsement_public_key")) { |
| return EndorsementPublicKey(factory); |
| } |
| |
| if (cl->HasSwitch("key_create") && |
| (cl->HasSwitch("rsa") || cl->HasSwitch("ecc")) && |
| cl->HasSwitch("usage") && cl->HasSwitch("key_blob")) { |
| trunks::TpmUtility::AsymmetricKeyUsage key_usage; |
| if (GetKeyUsage(cl->GetSwitchValueASCII("usage"), &key_usage)) { |
| return -1; |
| } |
| trunks::HmacAuthorizationDelegate hmac_delegate; |
| trunks::AuthorizationDelegate* delegate = nullptr; |
| std::unique_ptr<trunks::SessionManager> session_manager = |
| factory.GetSessionManager(); |
| if (cl->HasSwitch("sess_empty_auth")) { |
| delegate = empty_password_authorization.get(); |
| } else if (KeyStartSession(session_manager.get(), cl, &hmac_delegate)) { |
| return -1; |
| } else { |
| delegate = &hmac_delegate; |
| } |
| |
| std::string key_blob; |
| |
| if (cl->HasSwitch("rsa")) { |
| uint32_t modulus_bits; |
| if (!base::StringToUint(cl->GetSwitchValueASCII("rsa"), &modulus_bits)) { |
| LOG(ERROR) << "Incorrect RSA key length"; |
| return -1; |
| } |
| if (CallTpmUtility(print_time, factory, "CreateRSAKeyPair", |
| &trunks::TpmUtility::CreateRSAKeyPair, key_usage, |
| modulus_bits, 0x10001 /* exponent */, |
| "" /* password */, "" /* policy_digest */, |
| false /* use_only_policy_digest */, |
| std::vector<uint32_t>() /* pcrs */, delegate, |
| &key_blob, nullptr /* creation_blob */)) { |
| return -1; |
| } |
| } else { |
| if (CallTpmUtility(print_time, factory, "CreateECCKeyPair", |
| &trunks::TpmUtility::CreateECCKeyPair, key_usage, |
| trunks::TPM_ECC_NIST_P256 /* curve_id */, |
| "" /* password */, "" /* policy_digest */, |
| false /* use_only_policy_digest */, |
| std::vector<uint32_t>() /* pcrs */, delegate, |
| &key_blob, nullptr /* creation_blob */)) { |
| return -1; |
| } |
| } |
| return OutputToFile(cl->GetSwitchValueASCII("key_blob"), key_blob); |
| } |
| if (cl->HasSwitch("key_load") && cl->HasSwitch("key_blob")) { |
| std::string key_blob; |
| if (InputFromFile(cl->GetSwitchValueASCII("key_blob"), &key_blob)) { |
| return -1; |
| } |
| trunks::HmacAuthorizationDelegate hmac_delegate; |
| trunks::AuthorizationDelegate* delegate = nullptr; |
| std::unique_ptr<trunks::SessionManager> session_manager = |
| factory.GetSessionManager(); |
| if (cl->HasSwitch("sess_empty_auth")) { |
| delegate = empty_password_authorization.get(); |
| } else if (KeyStartSession(session_manager.get(), cl, &hmac_delegate)) { |
| return -1; |
| } else { |
| delegate = &hmac_delegate; |
| } |
| |
| trunks::TPM_HANDLE handle; |
| if (CallTpmUtility(print_time, factory, "Load", |
| &trunks::TpmUtility::LoadKey, key_blob, delegate, |
| &handle)) { |
| return -1; |
| } |
| printf("Loaded key handle: %#x\n", handle); |
| return 0; |
| } |
| if (cl->HasSwitch("key_unload") && cl->HasSwitch("handle")) { |
| uint32_t handle; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("handle"), &handle)) { |
| LOG(ERROR) << "Incorrect hex handle number"; |
| return -1; |
| } |
| |
| trunks::TPM_RC result = factory.GetTpm()->FlushContextSync( |
| static_cast<trunks::TPM_HANDLE>(handle), nullptr); |
| if (result) { |
| LOG(ERROR) << "Error closing handle: " << handle << " : " |
| << trunks::GetErrorString(result); |
| return -1; |
| } |
| return 0; |
| } |
| if (cl->HasSwitch("key_sign") && cl->HasSwitch("handle") && |
| cl->HasSwitch("data") && cl->HasSwitch("signature")) { |
| uint32_t handle; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("handle"), &handle)) { |
| LOG(ERROR) << "Incorrect hex handle number"; |
| return -1; |
| } |
| std::string data; |
| if (InputFromFile(cl->GetSwitchValueASCII("data"), &data)) { |
| return -1; |
| } |
| trunks::HmacAuthorizationDelegate delegate; |
| std::unique_ptr<trunks::SessionManager> session_manager = |
| factory.GetSessionManager(); |
| if (KeyStartSession(session_manager.get(), cl, &delegate)) { |
| return -1; |
| } |
| trunks::TPM_ALG_ID signature_algorithm = |
| cl->HasSwitch("ecc") ? trunks::TPM_ALG_ECDSA : trunks::TPM_ALG_RSASSA; |
| std::string signature; |
| if (CallTpmUtility(print_time, factory, "Sign", &trunks::TpmUtility::Sign, |
| handle, signature_algorithm, trunks::TPM_ALG_SHA256, |
| data, true /* generate_hash */, &delegate, &signature)) { |
| return -1; |
| } |
| |
| if (signature_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 parsing TPM signing result."; |
| return -1; |
| } |
| |
| // Pack TPM structure to OpenSSL ECDSA_SIG structure. |
| crypto::ScopedECDSA_SIG openssl_ecdsa(ECDSA_SIG_new()); |
| crypto::ScopedBIGNUM r(BN_new()), s(BN_new()); |
| if (!openssl_ecdsa || !r || !s) { |
| LOG(ERROR) << "Failed to allocate ECDSA_SIG or its BIGNUMs."; |
| return -1; |
| } |
| if (!BN_bin2bn(tpm_signature.signature.ecdsa.signature_r.buffer, |
| tpm_signature.signature.ecdsa.signature_r.size, r.get()) || |
| !BN_bin2bn(tpm_signature.signature.ecdsa.signature_s.buffer, |
| tpm_signature.signature.ecdsa.signature_s.size, s.get()) || |
| !ECDSA_SIG_set0(openssl_ecdsa.get(), r.release(), s.release())) { |
| LOG(ERROR) << "Error when parse TPM signing result."; |
| return -1; |
| } |
| |
| // Dump ECDSA_SIG to DER format |
| unsigned char* openssl_buffer = nullptr; |
| int length = i2d_ECDSA_SIG(openssl_ecdsa.get(), &openssl_buffer); |
| crypto::ScopedOpenSSLBytes scoped_buffer(openssl_buffer); |
| |
| signature = std::string(reinterpret_cast<char*>(openssl_buffer), length); |
| } |
| |
| return OutputToFile(cl->GetSwitchValueASCII("signature"), signature); |
| } |
| if (cl->HasSwitch("key_info") && cl->HasSwitch("handle")) { |
| uint32_t handle; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("handle"), &handle)) { |
| LOG(ERROR) << "Incorrect hex handle number"; |
| return -1; |
| } |
| return KeyInfo(print_time, factory, handle); |
| } |
| if (cl->HasSwitch("persistent_keys")) { |
| return PersistentKeys(factory); |
| } |
| if (cl->HasSwitch("transient_keys")) { |
| return TransientKeys(factory); |
| } |
| if (cl->HasSwitch("key_test_short_ecc") && cl->HasSwitch("handle")) { |
| uint32_t handle; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("handle"), &handle)) { |
| LOG(ERROR) << "Incorrect hex handle number"; |
| return -1; |
| } |
| return KeyTestShortEcc(factory, handle); |
| } |
| if (cl->HasSwitch("index_name") && cl->HasSwitch("index")) { |
| uint32_t nv_index; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("index"), &nv_index)) { |
| LOG(ERROR) << "Incorrect hex nv_index format"; |
| return -1; |
| } |
| return PrintIndexNameInHex(factory, nv_index); |
| } |
| if (cl->HasSwitch("index_data") && cl->HasSwitch("index")) { |
| uint32_t nv_index; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("index"), &nv_index)) { |
| LOG(ERROR) << "Incorrect hex nv_index format"; |
| return -1; |
| } |
| return PrintIndexDataInHex(factory, nv_index); |
| } |
| |
| if (cl->HasSwitch("uds_calc")) { |
| auto session = GetUDSSession(factory, cl, true); |
| |
| std::string digest; |
| trunks::TPM_RC rc = session->GetDigest(&digest); |
| if (rc) { |
| LOG(ERROR) << "Error during GetDigest: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| printf("Digest: %s\n", HexEncode(digest).c_str()); |
| return 0; |
| } |
| |
| if (cl->HasSwitch("policy_or")) { |
| auto hexdigests = BreakByDelim(cl->GetSwitchValueASCII("policy_or"), ","); |
| |
| std::vector<std::string> digests; |
| for (const auto& hexdigest : hexdigests) { |
| std::string digest; |
| if (!base::HexStringToString(hexdigest, &digest) || |
| digest.size() != SHA256_DIGEST_SIZE) { |
| LOG(ERROR) << "Syntax error: policy_or takes " |
| << "a list of comma-separated SHA256 digests"; |
| return -1; |
| } |
| digests.push_back(digest); |
| } |
| if (digests.size() > 8) { |
| LOG(ERROR) << "Syntax error: policy_or supports up to 8 digests"; |
| return -1; |
| } |
| for (const auto& digest : digests) { |
| printf("Input Digest: %s\n", HexEncode(digest).c_str()); |
| } |
| printf("\n"); |
| |
| auto session = factory.GetTrialSession(); |
| if (!session) { |
| LOG(ERROR) << "Error during GetTrialSession"; |
| return -1; |
| } |
| |
| trunks::TPM_RC rc = session->StartUnboundSession(false, false); |
| if (rc) { |
| LOG(ERROR) << "Error during StartUnboundSession: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| rc = session->PolicyOR(digests); |
| if (rc) { |
| LOG(ERROR) << "Error during PolicyOR: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| std::string digest; |
| rc = session->GetDigest(&digest); |
| if (rc) { |
| LOG(ERROR) << "Error during GetDigest: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| printf("Digest: %s\n", HexEncode(digest).c_str()); |
| return 0; |
| } |
| |
| if (cl->HasSwitch("uds_create") && cl->HasSwitch("index") && |
| cl->HasSwitch("size") && cl->HasSwitch("digest")) { |
| uint32_t index; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("index"), &index)) { |
| LOG(ERROR) << "Incorrect hex index number"; |
| return -1; |
| } |
| uint32_t size; |
| if (!base::StringToUint(cl->GetSwitchValueASCII("size"), &size)) { |
| LOG(ERROR) << "Incorrect buffer size"; |
| return -1; |
| } |
| if (size > MAX_NV_BUFFER_SIZE) { |
| LOG(ERROR) << "Size too big"; |
| return -1; |
| } |
| auto hexdigest = cl->GetSwitchValueASCII("digest"); |
| std::string digest; |
| if (!base::HexStringToString(hexdigest, &digest) || |
| digest.size() != SHA256_DIGEST_SIZE) { |
| PrintUsage(); |
| return -1; |
| } |
| |
| const uint32_t nv_index = trunks::NV_INDEX_FIRST + index; |
| trunks::TPMS_NV_PUBLIC public_data; |
| public_data.nv_index = nv_index; |
| public_data.name_alg = trunks::TPM_ALG_SHA256; |
| public_data.attributes = |
| trunks::TPMA_NV_PPWRITE + trunks::TPMA_NV_AUTHREAD + |
| trunks::TPMA_NV_PPREAD + trunks::TPMA_NV_PLATFORMCREATE + |
| trunks::TPMA_NV_WRITE_STCLEAR + trunks::TPMA_NV_POLICY_DELETE; |
| public_data.auth_policy = trunks::Make_TPM2B_DIGEST(digest); |
| public_data.data_size = size; |
| |
| const trunks::TPM2B_AUTH authorization = trunks::Make_TPM2B_DIGEST(""); |
| const trunks::TPM2B_NV_PUBLIC public_area = |
| trunks::Make_TPM2B_NV_PUBLIC(public_data); |
| std::string rh_name; |
| trunks::Serialize_TPM_HANDLE(trunks::TPM_RH_PLATFORM, &rh_name); |
| trunks::PasswordAuthorizationDelegate delegate(""); |
| |
| trunks::TPM_RC rc = factory.GetTpm()->NV_DefineSpaceSync( |
| trunks::TPM_RH_PLATFORM, rh_name, authorization, public_area, |
| &delegate); |
| if (rc) { |
| LOG(ERROR) << "Error during NV_DefineSpace: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| std::string nv_name; |
| rc = factory.GetTpmUtility()->GetNVSpaceName(index, &nv_name); |
| if (rc) { |
| LOG(ERROR) << "Error during GetNVSpaceName: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| trunks::TPM2B_MAX_NV_BUFFER data; |
| data.size = size; |
| memset(data.buffer, 0xA5, size); |
| rc = factory.GetTpm()->NV_WriteSync(trunks::TPM_RH_PLATFORM, rh_name, |
| nv_index, nv_name, data, 0, &delegate); |
| if (rc) { |
| LOG(ERROR) << "Error during NV_Write: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| rc = factory.GetTpm()->NV_WriteLockSync(trunks::TPM_RH_PLATFORM, rh_name, |
| nv_index, nv_name, &delegate); |
| if (rc) { |
| LOG(ERROR) << "Error during NV_WriteLock: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| if (cl->HasSwitch("uds_delete") && cl->HasSwitch("index")) { |
| uint32_t index; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("index"), &index)) { |
| LOG(ERROR) << "Incorrect hex index number"; |
| return -1; |
| } |
| auto hexdigests = BreakByDelim(cl->GetSwitchValueASCII("or"), ","); |
| auto empty_password_authorization = |
| factory.GetPasswordAuthorization(std::string()); |
| |
| std::vector<std::string> digests; |
| for (const auto& hexdigest : hexdigests) { |
| std::string digest; |
| if (!base::HexStringToString(hexdigest, &digest) || |
| digest.size() != SHA256_DIGEST_SIZE) { |
| LOG(ERROR) << "Syntax error: or takes " |
| << "a list of comma-separated SHA256 digests"; |
| return -1; |
| } |
| digests.push_back(digest); |
| } |
| if (digests.size() > 8) { |
| LOG(ERROR) << "Syntax error: or supports up to 8 digests"; |
| return -1; |
| } |
| for (const auto& digest : digests) { |
| printf("Or Digest: %s\n", HexEncode(digest).c_str()); |
| } |
| printf("\n"); |
| |
| auto session = GetUDSSession(factory, cl, false); |
| trunks::TPM_RC rc = session->PolicyOR(digests); |
| if (rc) { |
| LOG(ERROR) << "Error during PolicyOR: " << trunks::GetErrorString(rc); |
| return -1; |
| } |
| |
| const uint32_t nv_index = trunks::NV_INDEX_FIRST + index; |
| std::string nv_name; |
| rc = factory.GetTpmUtility()->GetNVSpaceName(index, &nv_name); |
| if (rc) { |
| LOG(ERROR) << "Error during GetNVSpaceName: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| std::string rh_name; |
| trunks::Serialize_TPM_HANDLE(trunks::TPM_RH_PLATFORM, &rh_name); |
| |
| MultipleAuthorizations authorization; |
| authorization.AddAuthorizationDelegate(session->GetDelegate()); |
| authorization.AddAuthorizationDelegate(empty_password_authorization.get()); |
| rc = factory.GetTpm()->NV_UndefineSpaceSpecialSync( |
| nv_index, nv_name, trunks::TPM_RH_PLATFORM, rh_name, &authorization); |
| if (rc) { |
| LOG(ERROR) << "Error during NV_UndefineSpaceSpecial: " |
| << trunks::GetErrorString(rc); |
| return -1; |
| } |
| return 0; |
| } |
| if (cl->HasSwitch("ecc_ek_handle") && cl->HasSwitch("password")) { |
| const std::string hex_password = cl->GetSwitchValueASCII("password"); |
| std::string endorsement_password; |
| if (!hex_password.empty() && |
| !base::HexStringToString(hex_password, &endorsement_password)) { |
| puts("Error hex-decoding endorsement password."); |
| return -1; |
| } |
| return PrintEccEndorsementKeyHandle(factory, endorsement_password); |
| } |
| if (cl->HasSwitch("test_credential_command") && cl->HasSwitch("password") && |
| cl->HasSwitch("handle")) { |
| uint32_t endorsement_key_handle; |
| if (!base::HexStringToUInt(cl->GetSwitchValueASCII("handle"), |
| &endorsement_key_handle)) { |
| LOG(ERROR) << "Incorrect hex handle number"; |
| return -1; |
| } |
| const std::string hex_password = cl->GetSwitchValueASCII("password"); |
| std::string endorsement_password; |
| if (!hex_password.empty() && |
| !base::HexStringToString(hex_password, &endorsement_password)) { |
| puts("Error hex-decoding endorsement password."); |
| return -1; |
| } |
| if (TestCredentialCommand(factory, endorsement_password, |
| endorsement_key_handle)) { |
| puts("pass"); |
| return 0; |
| } |
| puts("fail"); |
| return -1; |
| } |
| if (cl->HasSwitch("test_sign_verify")) { |
| if (TestSignVerify(factory)) { |
| puts("pass"); |
| return 0; |
| } |
| puts("fail"); |
| return -1; |
| } |
| if (cl->HasSwitch("test_certify_simple")) { |
| if (TestCertify(factory)) { |
| puts("pass"); |
| return 0; |
| } |
| puts("fail"); |
| return -1; |
| } |
| |
| puts("Invalid options!"); |
| PrintUsage(); |
| return -1; |
| } |