450 lines
16 KiB
C++
450 lines
16 KiB
C++
//
|
|
// Copyright (C) 2014 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
// Note: These tests are not generated. They test generated code.
|
|
|
|
#include <base/bind.h>
|
|
#include <base/callback.h>
|
|
#include <base/message_loop/message_loop.h>
|
|
#include <base/run_loop.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "trunks/mock_authorization_delegate.h"
|
|
#include "trunks/mock_command_transceiver.h"
|
|
#include "trunks/tpm_generated.h"
|
|
|
|
using testing::_;
|
|
using testing::DoAll;
|
|
using testing::Invoke;
|
|
using testing::Return;
|
|
using testing::SetArgPointee;
|
|
using testing::StrictMock;
|
|
using testing::WithArg;
|
|
|
|
namespace trunks {
|
|
|
|
// This test is designed to get good coverage of the different types of code
|
|
// generated for serializing and parsing structures / unions / typedefs.
|
|
TEST(GeneratorTest, SerializeParseStruct) {
|
|
TPM2B_CREATION_DATA data;
|
|
memset(&data, 0, sizeof(TPM2B_CREATION_DATA));
|
|
data.creation_data.pcr_select.count = 1;
|
|
data.creation_data.pcr_select.pcr_selections[0].hash = TPM_ALG_SHA256;
|
|
data.creation_data.pcr_select.pcr_selections[0].sizeof_select = 1;
|
|
data.creation_data.pcr_select.pcr_selections[0].pcr_select[0] = 0;
|
|
data.creation_data.pcr_digest.size = 2;
|
|
data.creation_data.locality = 0;
|
|
data.creation_data.parent_name_alg = TPM_ALG_SHA256;
|
|
data.creation_data.parent_name.size = 3;
|
|
data.creation_data.parent_qualified_name.size = 4;
|
|
data.creation_data.outside_info.size = 5;
|
|
std::string buffer;
|
|
TPM_RC rc = Serialize_TPM2B_CREATION_DATA(data, &buffer);
|
|
ASSERT_EQ(TPM_RC_SUCCESS, rc);
|
|
EXPECT_EQ(35u, buffer.size());
|
|
TPM2B_CREATION_DATA data2;
|
|
memset(&data2, 0, sizeof(TPM2B_CREATION_DATA));
|
|
std::string buffer_before = buffer;
|
|
std::string buffer_parsed;
|
|
rc = Parse_TPM2B_CREATION_DATA(&buffer, &data2, &buffer_parsed);
|
|
ASSERT_EQ(TPM_RC_SUCCESS, rc);
|
|
EXPECT_EQ(0u, buffer.size());
|
|
EXPECT_EQ(buffer_before, buffer_parsed);
|
|
EXPECT_EQ(buffer_before.size() - 2, data2.size);
|
|
EXPECT_EQ(0, memcmp(&data.creation_data, &data2.creation_data,
|
|
sizeof(TPMS_CREATION_DATA)));
|
|
}
|
|
|
|
TEST(GeneratorTest, SerializeBufferOverflow) {
|
|
TPM2B_MAX_BUFFER value;
|
|
value.size = arraysize(value.buffer) + 1;
|
|
std::string tmp;
|
|
EXPECT_EQ(TPM_RC_INSUFFICIENT, Serialize_TPM2B_MAX_BUFFER(value, &tmp));
|
|
}
|
|
|
|
TEST(GeneratorTest, ParseBufferOverflow) {
|
|
TPM2B_MAX_BUFFER tmp;
|
|
// Case 1: Sufficient source but overflow the destination.
|
|
std::string malformed1 = "\x10\x00";
|
|
malformed1 += std::string(0x1000, 'A');
|
|
ASSERT_GT(0x1000u, sizeof(tmp.buffer));
|
|
EXPECT_EQ(TPM_RC_INSUFFICIENT,
|
|
Parse_TPM2B_MAX_BUFFER(&malformed1, &tmp, nullptr));
|
|
// Case 2: Sufficient destination but overflow the source.
|
|
std::string malformed2 = "\x00\x01";
|
|
EXPECT_EQ(TPM_RC_INSUFFICIENT,
|
|
Parse_TPM2B_MAX_BUFFER(&malformed2, &tmp, nullptr));
|
|
}
|
|
|
|
TEST(GeneratorTest, SynchronousCommand) {
|
|
// A hand-rolled TPM2_Startup command.
|
|
std::string expected_command(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0C" // size=12
|
|
"\x00\x00\x01\x44" // code=TPM_CC_Startup
|
|
"\x00\x00", // param=TPM_SU_CLEAR
|
|
12);
|
|
std::string command_response(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0A" // size=10
|
|
"\x00\x00\x00\x00", // code=TPM_RC_SUCCESS
|
|
10);
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommandAndWait(expected_command))
|
|
.WillOnce(Return(command_response));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(Return(true));
|
|
Tpm tpm(&transceiver);
|
|
EXPECT_EQ(TPM_RC_SUCCESS, tpm.StartupSync(TPM_SU_CLEAR, &authorization));
|
|
}
|
|
|
|
TEST(GeneratorTest, SynchronousCommandWithError) {
|
|
// A hand-rolled TPM2_Startup command.
|
|
std::string expected_command(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0C" // size=12
|
|
"\x00\x00\x01\x44" // code=TPM_CC_Startup
|
|
"\x00\x00", // param=TPM_SU_CLEAR
|
|
12);
|
|
std::string command_response(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0A" // size=10
|
|
"\x00\x00\x01\x01", // code=TPM_RC_FAILURE
|
|
10);
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommandAndWait(expected_command))
|
|
.WillOnce(Return(command_response));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(Return(true));
|
|
Tpm tpm(&transceiver);
|
|
EXPECT_EQ(TPM_RC_FAILURE, tpm.StartupSync(TPM_SU_CLEAR, &authorization));
|
|
}
|
|
|
|
TEST(GeneratorTest, SynchronousCommandResponseTest) {
|
|
std::string auth_in(10, 'A');
|
|
std::string auth_out(10, 'B');
|
|
std::string auth_size("\x00\x00\x00\x0A", 4);
|
|
std::string handle_in("\x40\x00\x00\x07", 4); // primary_handle = TPM_RH_NULL
|
|
std::string handle_out("\x80\x00\x00\x01", 4); // out_handle
|
|
std::string sensitive(
|
|
"\x00\x05" // sensitive.size = 5
|
|
"\x00\x01" // sensitive.auth.size = 1
|
|
"\x61" // sensitive.auth.buffer[0] = 0x65
|
|
"\x00\x00", // sensitive.data.size = 0
|
|
7);
|
|
std::string public_data(
|
|
"\x00\x12" // public.size = 18
|
|
"\x00\x25" // public.type = TPM_ALG_SYMCIPHER
|
|
"\x00\x0B" // public.name_alg = SHA256
|
|
"\x00\x00\x00\x00"
|
|
"\x00\x00" // public.auth_policy.size = 0
|
|
"\x00\x06" // public.sym.alg = TPM_ALG_AES
|
|
"\x00\x80" // public.sym.key_bits = 128
|
|
"\x00\x43" // public.sym.mode = TPM_ALG_CFB
|
|
"\x00\x00", // public.unique.size = 0
|
|
20);
|
|
std::string outside("\x00\x00", 2); // outside_info.size = 0
|
|
std::string pcr_select("\x00\x00\x00\x00", 4); // pcr_select.size = 0
|
|
|
|
std::string data(
|
|
"\x00\x0F" // creation_data.size = 15
|
|
"\x00\x00\x00\x00" // creation.pcr = 0
|
|
"\x00\x00" // creation.digest.size = 0
|
|
"\x00" // creation.locality = 0
|
|
"\x00\x00" // creation.parent_alg = 0
|
|
"\x00\x00" // creation.parent_name.size = 0
|
|
"\x00\x00"
|
|
"\x00\x00", // creation.outside.size = 0
|
|
17);
|
|
std::string hash(
|
|
"\x00\x01"
|
|
"\x62",
|
|
3);
|
|
std::string ticket(
|
|
"\x80\x02" // tag = TPM_ST_SESSIONS
|
|
"\x40\x00\x00\x07" // parent = TPM_RH_NULL
|
|
"\x00\x00",
|
|
8);
|
|
std::string name(
|
|
"\x00\x03"
|
|
"KEY",
|
|
5);
|
|
std::string parameter_size("\x00\x00\x00\x35", 4); // param_size = 38
|
|
|
|
std::string command_tag(
|
|
"\x80\x02" // tag = TPM_ST_SESSIONS
|
|
"\x00\x00\x00\x3D" // size = 61
|
|
"\x00\x00\x01\x31", // code = TPM_CC_CreatePrimary
|
|
10);
|
|
std::string response_tag(
|
|
"\x80\x02" // tag = TPM_ST_SESSIONS
|
|
"\x00\x00\x00\x51" // size = 79
|
|
"\x00\x00\x00\x00", // rc = TPM_RC_SUCCESS
|
|
10);
|
|
|
|
std::string expected_command = command_tag + handle_in + auth_size + auth_in +
|
|
sensitive + public_data + outside + pcr_select;
|
|
std::string command_response = response_tag + handle_out + parameter_size +
|
|
public_data + data + hash + ticket + name +
|
|
auth_out;
|
|
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommandAndWait(expected_command))
|
|
.WillOnce(Return(command_response));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<3>(auth_in), Return(true)));
|
|
EXPECT_CALL(authorization, CheckResponseAuthorization(_, auth_out))
|
|
.WillOnce(Return(true));
|
|
EXPECT_CALL(authorization, EncryptCommandParameter(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(authorization, DecryptResponseParameter(_))
|
|
.WillOnce(Return(true));
|
|
|
|
TPM2B_SENSITIVE_CREATE in_sensitive;
|
|
in_sensitive.size = 5;
|
|
in_sensitive.sensitive.user_auth.size = 1;
|
|
in_sensitive.sensitive.user_auth.buffer[0] = 'a';
|
|
in_sensitive.sensitive.data.size = 0;
|
|
TPM2B_PUBLIC in_public;
|
|
in_public.size = 18;
|
|
in_public.public_area.type = TPM_ALG_SYMCIPHER;
|
|
in_public.public_area.name_alg = TPM_ALG_SHA256;
|
|
in_public.public_area.object_attributes = 0;
|
|
in_public.public_area.auth_policy.size = 0;
|
|
in_public.public_area.parameters.sym_detail.sym.algorithm = TPM_ALG_AES;
|
|
in_public.public_area.parameters.sym_detail.sym.key_bits.aes = 128;
|
|
in_public.public_area.parameters.sym_detail.sym.mode.aes = TPM_ALG_CFB;
|
|
in_public.public_area.unique.sym.size = 0;
|
|
TPM2B_DATA outside_info;
|
|
outside_info.size = 0;
|
|
TPML_PCR_SELECTION create_pcr;
|
|
create_pcr.count = 0;
|
|
|
|
TPM_HANDLE key_handle;
|
|
TPM2B_PUBLIC out_public;
|
|
TPM2B_CREATION_DATA creation_data;
|
|
TPM2B_DIGEST creation_hash;
|
|
TPMT_TK_CREATION creation_ticket;
|
|
TPM2B_NAME key_name;
|
|
|
|
Tpm tpm(&transceiver);
|
|
TPM_RC rc = tpm.CreatePrimarySync(
|
|
trunks::TPM_RH_NULL, "", in_sensitive, in_public, outside_info,
|
|
create_pcr, &key_handle, &out_public, &creation_data, &creation_hash,
|
|
&creation_ticket, &key_name, &authorization);
|
|
ASSERT_EQ(rc, TPM_RC_SUCCESS);
|
|
EXPECT_EQ(key_handle, 0x80000001);
|
|
EXPECT_EQ(out_public.size, 18);
|
|
EXPECT_EQ(creation_data.size, 15);
|
|
EXPECT_EQ(creation_hash.size, 1);
|
|
EXPECT_EQ(creation_hash.buffer[0], 'b');
|
|
EXPECT_EQ(creation_ticket.tag, 0x8002);
|
|
EXPECT_EQ(creation_ticket.hierarchy, 0x40000007u);
|
|
EXPECT_EQ(creation_ticket.digest.size, 0);
|
|
EXPECT_EQ(key_name.size, 3);
|
|
EXPECT_EQ(key_name.name[0], 'K');
|
|
EXPECT_EQ(key_name.name[1], 'E');
|
|
EXPECT_EQ(key_name.name[2], 'Y');
|
|
}
|
|
|
|
// A fixture for asynchronous command flow tests.
|
|
class CommandFlowTest : public testing::Test {
|
|
public:
|
|
CommandFlowTest() : response_code_(TPM_RC_SUCCESS) {}
|
|
~CommandFlowTest() override {}
|
|
|
|
void StartupCallback(TPM_RC response_code) { response_code_ = response_code; }
|
|
|
|
void CertifyCallback(TPM_RC response_code,
|
|
const TPM2B_ATTEST& certify_info,
|
|
const TPMT_SIGNATURE& signature) {
|
|
response_code_ = response_code;
|
|
signed_data_ = StringFrom_TPM2B_ATTEST(certify_info);
|
|
signature_ =
|
|
StringFrom_TPM2B_PUBLIC_KEY_RSA(signature.signature.rsassa.sig);
|
|
}
|
|
|
|
protected:
|
|
void Run() {
|
|
base::RunLoop run_loop;
|
|
run_loop.RunUntilIdle();
|
|
}
|
|
|
|
base::MessageLoop message_loop_;
|
|
TPM_RC response_code_;
|
|
std::string signature_;
|
|
std::string signed_data_;
|
|
};
|
|
|
|
// A functor for posting command responses. This is different than invoking the
|
|
// callback directly (e.g. via InvokeArgument) in that the original call will
|
|
// return before the response callback is invoked. This more closely matches how
|
|
// this code is expected to work when integrated.
|
|
class PostResponse {
|
|
public:
|
|
explicit PostResponse(const std::string& response) : response_(response) {}
|
|
void operator()(const base::Callback<void(const std::string&)>& callback) {
|
|
base::MessageLoop::current()->PostTask(FROM_HERE,
|
|
base::Bind(callback, response_));
|
|
}
|
|
|
|
private:
|
|
std::string response_;
|
|
};
|
|
|
|
// A functor to handle fake encryption / decryption of parameters.
|
|
class Encryptor {
|
|
public:
|
|
Encryptor(const std::string& expected_input, const std::string& output)
|
|
: expected_input_(expected_input), output_(output) {}
|
|
bool operator()(std::string* value) {
|
|
EXPECT_EQ(expected_input_, *value);
|
|
value->assign(output_);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
std::string expected_input_;
|
|
std::string output_;
|
|
};
|
|
|
|
TEST_F(CommandFlowTest, SimpleCommandFlow) {
|
|
// A hand-rolled TPM2_Startup command.
|
|
std::string expected_command(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0C" // size=12
|
|
"\x00\x00\x01\x44" // code=TPM_CC_Startup
|
|
"\x00\x00", // param=TPM_SU_CLEAR
|
|
12);
|
|
std::string command_response(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0A" // size=10
|
|
"\x00\x00\x00\x00", // code=TPM_RC_SUCCESS
|
|
10);
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommand(expected_command, _))
|
|
.WillOnce(WithArg<1>(Invoke(PostResponse(command_response))));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(Return(true));
|
|
Tpm tpm(&transceiver);
|
|
response_code_ = TPM_RC_FAILURE;
|
|
tpm.Startup(
|
|
TPM_SU_CLEAR, &authorization,
|
|
base::Bind(&CommandFlowTest::StartupCallback, base::Unretained(this)));
|
|
Run();
|
|
EXPECT_EQ(TPM_RC_SUCCESS, response_code_);
|
|
}
|
|
|
|
TEST_F(CommandFlowTest, SimpleCommandFlowWithError) {
|
|
// A hand-rolled TPM2_Startup command.
|
|
std::string expected_command(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0C" // size=12
|
|
"\x00\x00\x01\x44" // code=TPM_CC_Startup
|
|
"\x00\x00", // param=TPM_SU_CLEAR
|
|
12);
|
|
std::string command_response(
|
|
"\x80\x01" // tag=TPM_ST_NO_SESSIONS
|
|
"\x00\x00\x00\x0A" // size=10
|
|
"\x00\x00\x01\x01", // code=TPM_RC_FAILURE
|
|
10);
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommand(expected_command, _))
|
|
.WillOnce(WithArg<1>(Invoke(PostResponse(command_response))));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(Return(true));
|
|
Tpm tpm(&transceiver);
|
|
tpm.Startup(
|
|
TPM_SU_CLEAR, &authorization,
|
|
base::Bind(&CommandFlowTest::StartupCallback, base::Unretained(this)));
|
|
Run();
|
|
EXPECT_EQ(TPM_RC_FAILURE, response_code_);
|
|
}
|
|
|
|
// This test is designed to get good coverage of the different types of code
|
|
// generated for command / response processing. It covers:
|
|
// - input handles
|
|
// - authorization
|
|
// - multiple input and output parameters
|
|
// - parameter encryption and decryption
|
|
TEST_F(CommandFlowTest, FullCommandFlow) {
|
|
// A hand-rolled TPM2_Certify command.
|
|
std::string auth_in(10, 'A');
|
|
std::string auth_out(20, 'B');
|
|
std::string user_data(
|
|
"\x00\x0C"
|
|
"ct_user_data",
|
|
14);
|
|
std::string scheme("\x00\x10", 2); // scheme=TPM_ALG_NULL
|
|
std::string signed_data(
|
|
"\x00\x0E"
|
|
"ct_signed_data",
|
|
16);
|
|
std::string signature(
|
|
"\x00\x14" // sig_scheme=RSASSA
|
|
"\x00\x0B" // hash_scheme=SHA256
|
|
"\x00\x09" // signature size
|
|
"signature", // signature bytes
|
|
15);
|
|
std::string expected_command(
|
|
"\x80\x02" // tag=TPM_ST_SESSIONS
|
|
"\x00\x00\x00\x30" // size=48
|
|
"\x00\x00\x01\x48" // code=TPM_CC_Certify
|
|
"\x11\x22\x33\x44" // @objectHandle
|
|
"\x55\x66\x77\x88" // @signHandle
|
|
"\x00\x00\x00\x0A", // auth_size=10
|
|
22);
|
|
expected_command += auth_in + user_data + scheme;
|
|
std::string command_response(
|
|
"\x80\x02" // tag=TPM_ST_SESSIONS
|
|
"\x00\x00\x00\x41" // size=65
|
|
"\x00\x00\x00\x00" // code=TPM_RC_SUCCESS
|
|
"\x00\x00\x00\x1F", // param_size=31
|
|
14);
|
|
command_response += signed_data + signature + auth_out;
|
|
|
|
StrictMock<MockCommandTransceiver> transceiver;
|
|
EXPECT_CALL(transceiver, SendCommand(expected_command, _))
|
|
.WillOnce(WithArg<1>(Invoke(PostResponse(command_response))));
|
|
StrictMock<MockAuthorizationDelegate> authorization;
|
|
EXPECT_CALL(authorization, GetCommandAuthorization(_, _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<3>(auth_in), Return(true)));
|
|
EXPECT_CALL(authorization, CheckResponseAuthorization(_, auth_out))
|
|
.WillOnce(Return(true));
|
|
EXPECT_CALL(authorization, EncryptCommandParameter(_))
|
|
.WillOnce(Invoke(Encryptor("pt_user_data", "ct_user_data")));
|
|
EXPECT_CALL(authorization, DecryptResponseParameter(_))
|
|
.WillOnce(Invoke(Encryptor("ct_signed_data", "pt_signed_data")));
|
|
|
|
TPMT_SIG_SCHEME null_scheme;
|
|
null_scheme.scheme = TPM_ALG_NULL;
|
|
null_scheme.details.rsassa.hash_alg = TPM_ALG_SHA256;
|
|
Tpm tpm(&transceiver);
|
|
tpm.Certify(
|
|
0x11223344u, "object_handle", 0x55667788u, "sign_handle",
|
|
Make_TPM2B_DATA("pt_user_data"), null_scheme, &authorization,
|
|
base::Bind(&CommandFlowTest::CertifyCallback, base::Unretained(this)));
|
|
Run();
|
|
ASSERT_EQ(TPM_RC_SUCCESS, response_code_);
|
|
EXPECT_EQ("pt_signed_data", signed_data_);
|
|
EXPECT_EQ("signature", signature_);
|
|
}
|
|
|
|
} // namespace trunks
|