| // 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. |
| // |
| // FuzzedCommanTransceiver is a test transceiver for fuzzing TPM commands and |
| // responses. |
| |
| #include <arpa/inet.h> |
| #include <cstring> |
| |
| #include <base/callback.h> |
| |
| #include "trunks/fuzzed_command_transceiver.h" |
| #include "trunks/tpm_generated.h" |
| |
| namespace { |
| |
| // TPM message header size. |
| constexpr size_t kHeaderSize = 10; |
| // Probability in % of generating a pure random value or byte stream. |
| constexpr uint32_t kPureRandomProb = 5; |
| // Probability in % of generating an error response. |
| constexpr uint32_t kErrorResponseProb = 20; |
| // Probability in % of generating a response of minimal size. |
| constexpr uint32_t kMinimalResponseProb = 80; |
| |
| std::string BuildHeader(uint16_t tag, uint32_t size, uint32_t code) { |
| std::string header; |
| trunks::Serialize_uint16_t(tag, &header); |
| trunks::Serialize_uint32_t(size, &header); |
| trunks::Serialize_uint32_t(code, &header); |
| return header; |
| } |
| |
| bool ParseHeader(const std::string& command, |
| uint16_t* tag, |
| uint32_t* size, |
| uint32_t* code) { |
| std::string header(command, 0, kHeaderSize); |
| if (trunks::Parse_uint16_t(&header, tag, nullptr)) { |
| return false; |
| } |
| if (trunks::Parse_uint32_t(&header, size, nullptr)) { |
| return false; |
| } |
| if (trunks::Parse_uint32_t(&header, code, nullptr)) { |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| namespace trunks { |
| |
| FuzzedCommandTransceiver::FuzzedCommandTransceiver( |
| FuzzedDataProvider* const data_provider, size_t max_message_size) |
| : data_provider_(data_provider), max_message_size_(max_message_size) { |
| CHECK(data_provider); |
| } |
| |
| void FuzzedCommandTransceiver::SendCommand(const std::string& command, |
| const ResponseCallback& callback) { |
| callback.Run(SendCommandAndWait(command)); |
| } |
| |
| std::string FuzzedCommandTransceiver::SendCommandAndWait( |
| const std::string& command) { |
| return ConsumeResponseForCommand(command); |
| } |
| |
| std::string FuzzedCommandTransceiver::ConsumeCommand() { |
| // With low probability return a completely random message. |
| if (ConsumeBoolWithProbability(kPureRandomProb)) { |
| return ConsumeRandomMessage(); |
| } |
| |
| // Build a valid message. |
| uint16_t tag = ConsumeCommandTag(); |
| uint32_t code = ConsumeCommandCode(); |
| std::string handles = ConsumeHandles(GetNumberOfRequestHandles(code)); |
| std::string payload = ConsumePayload(kHeaderSize + handles.size()); |
| |
| return BuildHeader(tag, kHeaderSize + handles.size() + payload.size(), code) + |
| handles + payload; |
| } |
| |
| std::string FuzzedCommandTransceiver::ConsumeResponseForCommand( |
| const std::string& command) { |
| // With low probability return a completely random message. |
| if (ConsumeBoolWithProbability(kPureRandomProb)) { |
| return ConsumeRandomMessage(); |
| } |
| |
| // Parse command, use defaults in case of parsing errors. |
| uint16_t cmd_tag = TPM_ST_NO_SESSIONS; |
| uint32_t cmd_code = TPM_CC_FIRST; |
| uint32_t cmd_size = 0; |
| ParseHeader(command, &cmd_tag, &cmd_size, &cmd_code); |
| |
| // Decide if we want to return an error or success. |
| uint32_t resp_code; |
| std::string handles; |
| if (ConsumeBoolWithProbability(kErrorResponseProb)) { |
| resp_code = ConsumeResponseCode(); |
| } else { |
| resp_code = TPM_RC_SUCCESS; |
| handles = ConsumeHandles(GetNumberOfResponseHandles(cmd_code)); |
| } |
| |
| // Error or success, with high probability return response of minimal size. |
| std::string payload; |
| if (!ConsumeBoolWithProbability(kMinimalResponseProb)) { |
| payload = ConsumePayload(kHeaderSize + handles.size()); |
| } |
| |
| return BuildHeader(cmd_tag, kHeaderSize + handles.size() + payload.size(), |
| resp_code) + |
| handles + payload; |
| } |
| |
| uint32_t FuzzedCommandTransceiver::ConsumeCommandCode() { |
| // Decide between realistic and purely random value. |
| if (ConsumeBoolWithProbability(kPureRandomProb)) { |
| return ConsumeUint32(); |
| } |
| return data_provider_->ConsumeIntegralInRange<uint32_t>(TPM_CC_FIRST, |
| TPM_CC_LAST); |
| } |
| |
| uint32_t FuzzedCommandTransceiver::ConsumeResponseCode() { |
| // Decide between realistic and purely random value. |
| if (ConsumeBoolWithProbability(kPureRandomProb)) { |
| return ConsumeUint32(); |
| } |
| uint32_t rc = data_provider_->ConsumeIntegralInRange<uint32_t>(0, 0xFFF); |
| if (data_provider_->ConsumeBool()) { |
| // Generate WARN or FMT0 error RC. |
| rc &= 0x97F; |
| } else { |
| // Generate FMT1 error RC. |
| rc |= RC_FMT1; |
| } |
| return rc; |
| } |
| |
| uint16_t FuzzedCommandTransceiver::ConsumeCommandTag() { |
| // Decide between realistic and purely random value. |
| if (ConsumeBoolWithProbability(kPureRandomProb)) { |
| return data_provider_->ConsumeIntegral<uint16_t>(); |
| } |
| if (data_provider_->ConsumeBool()) { |
| return TPM_ST_SESSIONS; |
| } |
| return TPM_ST_NO_SESSIONS; |
| } |
| |
| std::string FuzzedCommandTransceiver::ConsumeHandles(size_t qnt_handles) { |
| std::string handles; |
| for (; qnt_handles > 0; --qnt_handles) { |
| Serialize_uint32_t(ConsumeHandle(), &handles); |
| } |
| return handles; |
| } |
| |
| uint32_t FuzzedCommandTransceiver::ConsumeHandle() { |
| uint32_t handle = ConsumeUint32(); |
| // Decide between realistic and purely random value. |
| if (!ConsumeBoolWithProbability(kPureRandomProb)) { |
| handle &= 0xC3000003u; |
| } |
| return handle; |
| } |
| |
| std::string FuzzedCommandTransceiver::ConsumePayload(size_t pre_payload_size) { |
| if (max_message_size_ <= pre_payload_size) { |
| return std::string(); |
| } |
| return data_provider_->ConsumeRandomLengthString(max_message_size_ - |
| pre_payload_size); |
| } |
| |
| bool FuzzedCommandTransceiver::ConsumeBoolWithProbability( |
| uint32_t probability) { |
| return data_provider_->ConsumeIntegralInRange<uint32_t>(0, 99) < probability; |
| } |
| |
| std::string FuzzedCommandTransceiver::ConsumeRandomMessage() { |
| return data_provider_->ConsumeRandomLengthString(max_message_size_); |
| } |
| |
| uint32_t FuzzedCommandTransceiver::ConsumeUint32() { |
| return data_provider_->ConsumeIntegralInRange<uint32_t>(0, 0xFFFFFFFFu); |
| } |
| |
| } // namespace trunks |