// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/nacl/loader/nacl_validation_query.h"
#include <stdint.h>
#include <memory>
#include "components/nacl/loader/nacl_validation_db.h"
#include "testing/gtest/include/gtest/gtest.h"
// This test makes sure that validation signature generation is performed
// correctly. In effect, this means that we are checking all of the data
// (and no other data) we are passing the signature generator affects the final
// signature. To avoid tying the tests to a particular implementation, each
// test generates two signatures and compares them rather than trying to compare
// against a specified signature.
namespace {
const char kKey[] = "bogus key for HMAC...";
const char kKeyAlt[] = "bogus key for HMAC!!!";
const char kVersion[] = "bogus version";
const char kVersionAlt[] = "bogus!version";
const char kShortData[] = "Short data 1234567890";
const char kAltShortData[] = "Short!data 1234567890";
const char kLongData[] = "Long data."
"1234567890123456789012345678901234567890123456789012345678901234567890"
"1234567890123456789012345678901234567890123456789012345678901234567890"
"1234567890123456789012345678901234567890123456789012345678901234567890"
"1234567890123456789012345678901234567890123456789012345678901234567890";
class MockValidationDB : public NaClValidationDB {
public:
MockValidationDB()
: did_query_(false),
did_set_(false),
status_(true) {
}
bool QueryKnownToValidate(const std::string& signature) override {
// The typecast is needed to work around gtest trying to take the address
// of a constant.
EXPECT_EQ((int) NaClValidationQuery::kDigestLength,
(int) signature.length());
EXPECT_FALSE(did_query_);
EXPECT_FALSE(did_set_);
did_query_ = true;
memcpy(query_signature_, signature.data(),
NaClValidationQuery::kDigestLength);
return status_;
}
void SetKnownToValidate(const std::string& signature) override {
// The typecast is needed to work around gtest trying to take the address
// of a constant.
ASSERT_EQ((int) NaClValidationQuery::kDigestLength,
(int) signature.length());
EXPECT_TRUE(did_query_);
EXPECT_FALSE(did_set_);
did_set_ = true;
memcpy(set_signature_, signature.data(),
NaClValidationQuery::kDigestLength);
// Signatures should be the same.
EXPECT_EQ(0, memcmp(query_signature_, set_signature_,
NaClValidationQuery::kDigestLength));
}
bool did_query_;
bool did_set_;
bool status_;
uint8_t query_signature_[NaClValidationQuery::kDigestLength];
uint8_t set_signature_[NaClValidationQuery::kDigestLength];
};
class TestQuery {
public:
TestQuery(const char* key, const char* version) {
db = std::make_unique<MockValidationDB>();
context =
std::make_unique<NaClValidationQueryContext>(db.get(), key, version);
query.reset(context->CreateQuery());
}
std::unique_ptr<MockValidationDB> db;
std::unique_ptr<NaClValidationQueryContext> context;
std::unique_ptr<NaClValidationQuery> query;
};
class NaClValidationQueryTest : public ::testing::Test {
protected:
std::unique_ptr<TestQuery> query1;
std::unique_ptr<TestQuery> query2;
void SetUp() override {
query1 = std::make_unique<TestQuery>(kKey, kVersion);
query2 = std::make_unique<TestQuery>(kKey, kVersion);
}
void AssertQuerySame() {
ASSERT_TRUE(query1->db->did_query_);
ASSERT_TRUE(query2->db->did_query_);
ASSERT_EQ(0, memcmp(query1->db->query_signature_,
query2->db->query_signature_,
NaClValidationQuery::kDigestLength));
}
void AssertQueryDifferent() {
ASSERT_TRUE(query1->db->did_query_);
ASSERT_TRUE(query2->db->did_query_);
ASSERT_NE(0, memcmp(query1->db->query_signature_,
query2->db->query_signature_,
NaClValidationQuery::kDigestLength));
}
};
TEST_F(NaClValidationQueryTest, Sanity) {
query1->query->AddData(kShortData, sizeof(kShortData));
ASSERT_FALSE(query1->db->did_query_);
ASSERT_FALSE(query1->db->did_set_);
ASSERT_EQ(1, query1->query->QueryKnownToValidate());
ASSERT_TRUE(query1->db->did_query_);
ASSERT_FALSE(query1->db->did_set_);
query1->query->SetKnownToValidate();
ASSERT_TRUE(query1->db->did_query_);
ASSERT_TRUE(query1->db->did_set_);
}
TEST_F(NaClValidationQueryTest, ConsistentShort) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
TEST_F(NaClValidationQueryTest, InconsistentShort) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kAltShortData, sizeof(kAltShortData));
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
// Test for a bug caught during development where AddData would accidently
// overwrite previously written data and add uninitialzied memory to the hash.
TEST_F(NaClValidationQueryTest, ConsistentShortBug) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
// Test for a bug caught during development where AddData would accidently
// overwrite previously written data and add uninitialzed memory to the hash.
TEST_F(NaClValidationQueryTest, InconsistentShortBug1) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kAltShortData, sizeof(kAltShortData));
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
// Make sure we don't ignore the second bit of data.
TEST_F(NaClValidationQueryTest, InconsistentShort2) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->AddData(kAltShortData, sizeof(kAltShortData));
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
TEST_F(NaClValidationQueryTest, InconsistentZeroSizedAdd) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->AddData(kShortData, 0);
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
TEST_F(NaClValidationQueryTest, ConsistentZeroSizedAdd) {
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->AddData("a", 0);
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->AddData("b", 0);
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
TEST_F(NaClValidationQueryTest, ConsistentRepeatedShort) {
for (int i = 0; i < 30; i++) {
query1->query->AddData(kShortData, sizeof(kShortData));
}
query1->query->QueryKnownToValidate();
for (int i = 0; i < 30; i++) {
query2->query->AddData(kShortData, sizeof(kShortData));
}
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
TEST_F(NaClValidationQueryTest, ConsistentLong) {
query1->query->AddData(kLongData, sizeof(kLongData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kLongData, sizeof(kLongData));
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
TEST_F(NaClValidationQueryTest, ConsistentRepeatedLong) {
for (int i = 0; i < 30; i++) {
query1->query->AddData(kLongData, sizeof(kLongData));
}
query1->query->QueryKnownToValidate();
for (int i = 0; i < 30; i++) {
query2->query->AddData(kLongData, sizeof(kLongData));
}
query2->query->QueryKnownToValidate();
AssertQuerySame();
}
TEST_F(NaClValidationQueryTest, PerturbKey) {
query2 = std::make_unique<TestQuery>(kKeyAlt, kVersion);
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
TEST_F(NaClValidationQueryTest, PerturbVersion) {
query2 = std::make_unique<TestQuery>(kKey, kVersionAlt);
query1->query->AddData(kShortData, sizeof(kShortData));
query1->query->QueryKnownToValidate();
query2->query->AddData(kShortData, sizeof(kShortData));
query2->query->QueryKnownToValidate();
AssertQueryDifferent();
}
}