// Copyright 2013 The Chromium 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 "components/nacl/loader/nacl_validation_db.h" #include "components/nacl/loader/nacl_validation_query.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 query_signature_[NaClValidationQuery::kDigestLength]; uint8 set_signature_[NaClValidationQuery::kDigestLength]; }; class TestQuery { public: TestQuery(const char* key, const char* version) { db.reset(new MockValidationDB()); context.reset(new NaClValidationQueryContext(db.get(), key, version)); query.reset(context->CreateQuery()); } scoped_ptr db; scoped_ptr context; scoped_ptr query; }; class NaClValidationQueryTest : public ::testing::Test { protected: scoped_ptr query1; scoped_ptr query2; void SetUp() override { query1.reset(new TestQuery(kKey, kVersion)); query2.reset(new 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.reset(new 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.reset(new TestQuery(kKey, kVersionAlt)); query1->query->AddData(kShortData, sizeof(kShortData)); query1->query->QueryKnownToValidate(); query2->query->AddData(kShortData, sizeof(kShortData)); query2->query->QueryKnownToValidate(); AssertQueryDifferent(); } }