| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "trunks/tpm_pinweaver.h" |
| |
| #include <endian.h> |
| |
| #include <algorithm> |
| |
| #include <base/logging.h> |
| |
| #include <trunks/cr50_headers/pinweaver_types.h> |
| |
| namespace trunks { |
| namespace { |
| |
| void Serialize(const void* value, size_t length, std::string* buffer) { |
| const char* value_bytes = reinterpret_cast<const char*>(value); |
| buffer->append(value_bytes, length); |
| } |
| |
| void Serialize_pw_request_header_t(uint8_t protocol_version, |
| uint8_t message_type, |
| uint16_t data_length, |
| std::string* buffer) { |
| struct pw_request_header_t header = { |
| protocol_version, {message_type}, htole16(data_length)}; |
| Serialize(&header, sizeof(header), buffer); |
| } |
| |
| TPM_RC Parse_unimported_leaf_data_t(std::string::const_iterator begin, |
| std::string::const_iterator end, |
| std::string* cred_metadata, |
| std::string* mac) { |
| auto size = end - begin; |
| if (size < sizeof(unimported_leaf_data_t)) |
| return SAPI_RC_BAD_SIZE; |
| |
| const struct unimported_leaf_data_t* unimported_leaf_data = |
| reinterpret_cast<const struct unimported_leaf_data_t*>(&*begin); |
| if (size != sizeof(unimported_leaf_data_t) + |
| unimported_leaf_data->head.pub_len + |
| unimported_leaf_data->head.sec_len) { |
| return SAPI_RC_BAD_SIZE; |
| } |
| |
| if (cred_metadata) |
| cred_metadata->assign(begin, end); |
| if (mac) { |
| mac->assign( |
| unimported_leaf_data->hmac, |
| unimported_leaf_data->hmac + sizeof(unimported_leaf_data->hmac)); |
| } |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Validate_cred_metadata(const std::string& cred_metadata) { |
| return Parse_unimported_leaf_data_t(cred_metadata.begin(), |
| cred_metadata.end(), nullptr, nullptr); |
| } |
| |
| } // namespace |
| |
| TPM_RC Serialize_pw_ping_t(uint8_t request_version, std::string* buffer) { |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t)); |
| |
| Serialize_pw_request_header_t(request_version, PW_MT_INVALID, 0, buffer); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_reset_tree_t(uint8_t protocol_version, |
| uint8_t bits_per_level, |
| uint8_t height, |
| std::string* buffer) { |
| struct pw_request_reset_tree_t data = {{bits_per_level}, {height}}; |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + sizeof(data)); |
| |
| Serialize_pw_request_header_t(protocol_version, PW_RESET_TREE, sizeof(data), |
| buffer); |
| Serialize(&data, sizeof(data), buffer); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_insert_leaf_t( |
| uint8_t protocol_version, |
| uint64_t label, |
| const std::string& h_aux, |
| const brillo::SecureBlob& le_secret, |
| const brillo::SecureBlob& he_secret, |
| const brillo::SecureBlob& reset_secret, |
| const std::map<uint32_t, uint32_t>& delay_schedule, |
| const ValidPcrCriteria& valid_pcr_criteria, |
| std::string* buffer) { |
| if (h_aux.length() > PW_MAX_PATH_SIZE || le_secret.size() != PW_SECRET_SIZE || |
| he_secret.size() != PW_SECRET_SIZE || |
| reset_secret.size() != PW_SECRET_SIZE || |
| delay_schedule.size() > PW_SCHED_COUNT || |
| valid_pcr_criteria.valid_pcr_values_size() > PW_MAX_PCR_CRITERIA_COUNT) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| struct pw_request_insert_leaf_t data = {}; |
| int pcr_criteria_size = |
| sizeof(struct valid_pcr_value_t) * PW_MAX_PCR_CRITERIA_COUNT; |
| int data_size = sizeof(data); |
| if (protocol_version == 0) { |
| data_size -= pcr_criteria_size; |
| } |
| |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + data_size + |
| h_aux.size()); |
| |
| data.label = {htole64(label)}; |
| size_t x = 0; |
| for (const auto& itr : delay_schedule) { |
| data.delay_schedule[x].attempt_count = {htole32(itr.first)}; |
| data.delay_schedule[x].time_diff = {htole32(itr.second)}; |
| ++x; |
| } |
| |
| if (protocol_version > 0) { |
| x = 0; |
| for (const ValidPcrValue& value : valid_pcr_criteria.valid_pcr_values()) { |
| data.valid_pcr_criteria[x].bitmask[0] = value.bitmask()[0]; |
| data.valid_pcr_criteria[x].bitmask[1] = value.bitmask()[1]; |
| if (value.digest().size() > sizeof(data.valid_pcr_criteria[0].digest)) |
| return SAPI_RC_BAD_PARAMETER; |
| std::copy(value.digest().begin(), value.digest().end(), |
| data.valid_pcr_criteria[x].digest); |
| ++x; |
| } |
| for (; x < PW_MAX_PCR_CRITERIA_COUNT; ++x) { |
| memset(data.valid_pcr_criteria[x].bitmask, 0, |
| sizeof(data.valid_pcr_criteria[x].bitmask)); |
| } |
| } else if (valid_pcr_criteria.valid_pcr_values_size() > 0) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| std::copy(le_secret.begin(), le_secret.end(), data.low_entropy_secret); |
| std::copy(he_secret.begin(), he_secret.end(), data.high_entropy_secret); |
| std::copy(reset_secret.begin(), reset_secret.end(), data.reset_secret); |
| |
| Serialize_pw_request_header_t(protocol_version, PW_INSERT_LEAF, |
| data_size + h_aux.size(), buffer); |
| Serialize(&data, data_size, buffer); |
| |
| buffer->append(h_aux); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_remove_leaf_t(uint8_t protocol_version, |
| uint64_t label, |
| const std::string& h_aux, |
| const std::string& mac, |
| std::string* buffer) { |
| if (h_aux.length() > PW_MAX_PATH_SIZE || mac.size() != PW_HASH_SIZE) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| struct pw_request_remove_leaf_t data = {}; |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + sizeof(data) + |
| h_aux.size()); |
| |
| data.leaf_location = {htole64(label)}; |
| std::copy(mac.begin(), mac.end(), data.leaf_hmac); |
| |
| Serialize_pw_request_header_t(protocol_version, PW_REMOVE_LEAF, |
| sizeof(data) + h_aux.size(), buffer); |
| Serialize(&data, sizeof(data), buffer); |
| buffer->append(h_aux); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_try_auth_t(uint8_t protocol_version, |
| const brillo::SecureBlob& le_secret, |
| const std::string& h_aux, |
| const std::string& cred_metadata, |
| std::string* buffer) { |
| if (le_secret.size() != PW_SECRET_SIZE || h_aux.length() > PW_MAX_PATH_SIZE || |
| Validate_cred_metadata(cred_metadata) != TPM_RC_SUCCESS) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + |
| sizeof(pw_request_try_auth_t) + |
| (cred_metadata.size() - sizeof(unimported_leaf_data_t)) + |
| h_aux.size()); |
| |
| Serialize_pw_request_header_t( |
| protocol_version, PW_TRY_AUTH, |
| le_secret.size() + cred_metadata.size() + h_aux.size(), buffer); |
| |
| buffer->append(le_secret.to_string()); |
| buffer->append(cred_metadata); |
| buffer->append(h_aux); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_reset_auth_t(uint8_t protocol_version, |
| const brillo::SecureBlob& reset_secret, |
| const std::string& h_aux, |
| const std::string& cred_metadata, |
| std::string* buffer) { |
| if (reset_secret.size() != PW_SECRET_SIZE || |
| h_aux.length() > PW_MAX_PATH_SIZE || |
| Validate_cred_metadata(cred_metadata) != TPM_RC_SUCCESS) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + |
| sizeof(pw_request_reset_auth_t) + |
| (cred_metadata.size() - sizeof(unimported_leaf_data_t)) + |
| h_aux.size()); |
| |
| Serialize_pw_request_header_t( |
| protocol_version, PW_RESET_AUTH, |
| reset_secret.size() + cred_metadata.size() + h_aux.size(), buffer); |
| |
| buffer->append(reset_secret.to_string()); |
| buffer->append(cred_metadata); |
| buffer->append(h_aux); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_get_log_t(uint8_t protocol_version, |
| const std::string& root, |
| std::string* buffer) { |
| if (root.size() != PW_HASH_SIZE) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| Serialize_pw_request_header_t(protocol_version, PW_GET_LOG, |
| sizeof(struct pw_request_get_log_t), buffer); |
| buffer->append(root); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Serialize_pw_log_replay_t(uint8_t protocol_version, |
| const std::string& log_root, |
| const std::string& h_aux, |
| const std::string& cred_metadata, |
| std::string* buffer) { |
| if (log_root.size() != PW_HASH_SIZE || h_aux.length() > PW_MAX_PATH_SIZE || |
| Validate_cred_metadata(cred_metadata) != TPM_RC_SUCCESS) { |
| return SAPI_RC_BAD_PARAMETER; |
| } |
| |
| buffer->reserve(buffer->size() + sizeof(pw_request_header_t) + |
| sizeof(pw_request_log_replay_t) + |
| (cred_metadata.size() - sizeof(unimported_leaf_data_t)) + |
| h_aux.size()); |
| |
| Serialize_pw_request_header_t(protocol_version, PW_LOG_REPLAY, |
| sizeof(pw_request_log_replay_t) - |
| sizeof(struct unimported_leaf_data_t) + |
| cred_metadata.size() + h_aux.size(), |
| buffer); |
| buffer->append(log_root); |
| buffer->append(cred_metadata); |
| buffer->append(h_aux); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Parse_pw_response_header_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| uint16_t* data_length) { |
| *result_code = 0; |
| if (root_hash) |
| root_hash->clear(); |
| *data_length = 0; |
| |
| if (buffer.empty()) { |
| return SAPI_RC_INSUFFICIENT_BUFFER; |
| } |
| |
| uint8_t version = (uint8_t)buffer[0]; |
| if (version > PW_PROTOCOL_VERSION) { |
| LOG(ERROR) << "Pinweaver protocol version mismatch: got " |
| << static_cast<uint32_t>(version) << " expected " |
| << PW_PROTOCOL_VERSION << " or lower."; |
| return SAPI_RC_ABI_MISMATCH; |
| } |
| |
| if (buffer.size() < sizeof(struct pw_response_header_t)) { |
| LOG(ERROR) << "Pinweaver response contained an unexpected number of bytes."; |
| return SAPI_RC_INSUFFICIENT_BUFFER; |
| } |
| |
| const struct pw_response_header_t* header = |
| reinterpret_cast<const struct pw_response_header_t*>(buffer.data()); |
| *result_code = le32toh(header->result_code); |
| if (root_hash) |
| root_hash->assign(header->root, header->root + sizeof(header->root)); |
| *data_length = le16toh(header->data_length); |
| |
| if (buffer.size() != sizeof(struct pw_response_header_t) + *data_length) { |
| LOG(ERROR) << "Pinweaver response contained " << buffer.size() |
| << " instead of " |
| << sizeof(struct pw_response_header_t) + *data_length |
| << "bytes."; |
| return SAPI_RC_BAD_SIZE; |
| } |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Parse_pw_short_message(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash) { |
| uint16_t data_length; |
| TPM_RC rc = |
| Parse_pw_response_header_t(buffer, result_code, root_hash, &data_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| if (data_length != 0) { |
| LOG(ERROR) << "Pinweaver error contained an unexpected number of bytes."; |
| return SAPI_RC_BAD_SIZE; |
| } |
| |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Parse_pw_pong_t(const std::string& buffer, uint8_t* protocol_version) { |
| uint32_t result_code; |
| TPM_RC rc = Parse_pw_short_message(buffer, &result_code, nullptr); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| if (result_code != PW_ERR_TYPE_INVALID) |
| return SAPI_RC_ABI_MISMATCH; |
| *protocol_version = (uint8_t)buffer[0]; |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC Parse_pw_insert_leaf_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| std::string* cred_metadata, |
| std::string* mac) { |
| cred_metadata->clear(); |
| mac->clear(); |
| |
| uint16_t response_length; |
| TPM_RC rc = Parse_pw_response_header_t(buffer, result_code, root_hash, |
| &response_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| if (*result_code != 0) { |
| return response_length == 0 ? TPM_RC_SUCCESS : SAPI_RC_BAD_SIZE; |
| } |
| |
| return Parse_unimported_leaf_data_t( |
| buffer.begin() + sizeof(pw_response_header_t), buffer.end(), |
| cred_metadata, mac); |
| } |
| |
| TPM_RC Parse_pw_try_auth_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| uint32_t* seconds_to_wait, |
| brillo::SecureBlob* he_secret, |
| brillo::SecureBlob* reset_secret, |
| std::string* cred_metadata_out, |
| std::string* mac_out) { |
| *seconds_to_wait = 0; |
| he_secret->clear(); |
| reset_secret->clear(); |
| cred_metadata_out->clear(); |
| mac_out->clear(); |
| |
| uint16_t response_length; |
| TPM_RC rc = Parse_pw_response_header_t(buffer, result_code, root_hash, |
| &response_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| // For EC_SUCCESS, PW_ERR_RATE_LIMIT_REACHED, and PW_ERR_LOWENT_AUTH_FAILED |
| // a full size response is sent. However, only particular fields are valid. |
| if (*result_code != 0 && *result_code != PW_ERR_RATE_LIMIT_REACHED && |
| *result_code != PW_ERR_LOWENT_AUTH_FAILED) { |
| return response_length == 0 ? TPM_RC_SUCCESS : SAPI_RC_BAD_SIZE; |
| } |
| |
| if (response_length < sizeof(pw_response_try_auth_t)) |
| return SAPI_RC_BAD_SIZE; |
| |
| auto itr = buffer.begin() + sizeof(pw_response_header_t); |
| // This field may not be aligned so it is retrieved in a way that will work |
| // regardless of platform. |
| *seconds_to_wait = (static_cast<uint32_t>(itr[0]) << 24) | |
| (static_cast<uint32_t>(itr[1]) << 16) | |
| (static_cast<uint32_t>(itr[2]) << 8) | |
| static_cast<uint32_t>(itr[3]); |
| itr += 4; |
| |
| // he_secret is only valid for EC_SUCCESS. |
| if (*result_code == 0) { |
| he_secret->assign(itr, itr + PW_SECRET_SIZE); |
| // reset_secret is present only starting from protocol_version = 1. |
| if ((uint8_t)buffer[0] > 0) { |
| reset_secret->assign(itr + PW_SECRET_SIZE, itr + 2 * PW_SECRET_SIZE); |
| } |
| } |
| if ((uint8_t)buffer[0] > 0) { |
| itr += 2 * PW_SECRET_SIZE; |
| } else { |
| itr += PW_SECRET_SIZE; |
| } |
| |
| // For PW_ERR_RATE_LIMIT_REACHED the only valid result field is |
| // seconds_to_wait. |
| if (*result_code == PW_ERR_RATE_LIMIT_REACHED) |
| return TPM_RC_SUCCESS; |
| |
| return Parse_unimported_leaf_data_t(itr, buffer.end(), cred_metadata_out, |
| mac_out); |
| } |
| |
| TPM_RC Parse_pw_reset_auth_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| brillo::SecureBlob* he_secret, |
| std::string* cred_metadata_out, |
| std::string* mac_out) { |
| he_secret->clear(); |
| cred_metadata_out->clear(); |
| mac_out->clear(); |
| |
| uint16_t response_length; |
| TPM_RC rc = Parse_pw_response_header_t(buffer, result_code, root_hash, |
| &response_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| if (*result_code != 0) { |
| return response_length == 0 ? TPM_RC_SUCCESS : SAPI_RC_BAD_SIZE; |
| } |
| |
| if (response_length < sizeof(pw_response_reset_auth_t)) { |
| LOG(ERROR) << "Pinweaver pw_response_reset_auth contained an unexpected " |
| "number of bytes."; |
| return SAPI_RC_BAD_SIZE; |
| } |
| |
| auto itr = buffer.begin() + sizeof(pw_response_header_t); |
| he_secret->assign(itr, itr + PW_SECRET_SIZE); |
| itr += PW_SECRET_SIZE; |
| |
| return Parse_unimported_leaf_data_t(itr, buffer.end(), cred_metadata_out, |
| mac_out); |
| } |
| |
| TPM_RC Parse_pw_get_log_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| std::vector<trunks::PinWeaverLogEntry>* log) { |
| log->clear(); |
| |
| uint16_t response_length; |
| TPM_RC rc = Parse_pw_response_header_t(buffer, result_code, root_hash, |
| &response_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| if (*result_code != 0) { |
| return response_length == 0 ? TPM_RC_SUCCESS : SAPI_RC_BAD_SIZE; |
| } |
| |
| if (response_length % sizeof(struct pw_get_log_entry_t) != 0) |
| return SAPI_RC_BAD_SIZE; |
| |
| log->resize(response_length / sizeof(struct pw_get_log_entry_t)); |
| TPM_RC ret = TPM_RC_SUCCESS; |
| size_t x = 0; |
| for (auto itr = buffer.begin() + sizeof(struct pw_response_header_t); |
| itr < buffer.end(); itr += sizeof(struct pw_get_log_entry_t)) { |
| const struct pw_get_log_entry_t* entry = |
| reinterpret_cast<const struct pw_get_log_entry_t*>(&*itr); |
| trunks::PinWeaverLogEntry* proto_entry = &(*log)[x]; |
| proto_entry->set_label(entry->label.v); |
| proto_entry->set_root(entry->root, PW_HASH_SIZE); |
| switch (entry->type.v) { |
| case PW_INSERT_LEAF: |
| proto_entry->mutable_insert_leaf()->set_hmac(entry->leaf_hmac, |
| PW_HASH_SIZE); |
| break; |
| case PW_REMOVE_LEAF: |
| proto_entry->mutable_remove_leaf(); |
| break; |
| case PW_TRY_AUTH: { |
| auto* ptr = proto_entry->mutable_auth(); |
| ptr->mutable_timestamp()->set_boot_count(entry->timestamp.boot_count); |
| ptr->mutable_timestamp()->set_timer_value(entry->timestamp.timer_value); |
| ptr->set_return_code(entry->return_code); |
| } break; |
| case PW_RESET_TREE: |
| proto_entry->mutable_reset_tree(); |
| break; |
| default: |
| ret = SAPI_RC_BAD_SEQUENCE; |
| } |
| ++x; |
| } |
| return ret; |
| } |
| |
| TPM_RC Parse_pw_log_replay_t(const std::string& buffer, |
| uint32_t* result_code, |
| std::string* root_hash, |
| std::string* cred_metadata_out, |
| std::string* mac_out) { |
| cred_metadata_out->clear(); |
| mac_out->clear(); |
| |
| uint16_t response_length; |
| TPM_RC rc = Parse_pw_response_header_t(buffer, result_code, root_hash, |
| &response_length); |
| if (rc != TPM_RC_SUCCESS) |
| return rc; |
| |
| if (*result_code != 0) { |
| return response_length == 0 ? TPM_RC_SUCCESS : SAPI_RC_BAD_SIZE; |
| } |
| |
| if (response_length < sizeof(struct pw_response_reset_auth_t)) |
| return SAPI_RC_BAD_SIZE; |
| |
| auto itr = buffer.begin() + sizeof(struct pw_response_header_t); |
| |
| return Parse_unimported_leaf_data_t(itr, buffer.end(), cred_metadata_out, |
| mac_out); |
| } |
| |
| } // namespace trunks |