blob: dabb993d9e76bab4e6041a75d2bc8b6ba5bd52cf [file] [log] [blame]
// 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 <base/check.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