1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/nacl/loader/nacl_validation_db.h"
6#include "components/nacl/loader/nacl_validation_query.h"
7#include "testing/gtest/include/gtest/gtest.h"
8
9// This test makes sure that validation signature generation is performed
10// correctly.  In effect, this means that we are checking all of the data
11// (and no other data) we are passing the signature generator affects the final
12// signature.  To avoid tying the tests to a particular implementation, each
13// test generates two signatures and compares them rather than trying to compare
14// against a specified signature.
15
16namespace {
17
18const char kKey[] = "bogus key for HMAC...";
19const char kKeyAlt[] = "bogus key for HMAC!!!";
20
21const char kVersion[] = "bogus version";
22const char kVersionAlt[] = "bogus!version";
23
24
25const char kShortData[] = "Short data 1234567890";
26const char kAltShortData[] = "Short!data 1234567890";
27
28const char kLongData[] = "Long data."
29    "1234567890123456789012345678901234567890123456789012345678901234567890"
30    "1234567890123456789012345678901234567890123456789012345678901234567890"
31    "1234567890123456789012345678901234567890123456789012345678901234567890"
32    "1234567890123456789012345678901234567890123456789012345678901234567890";
33
34class MockValidationDB : public NaClValidationDB {
35 public:
36  MockValidationDB()
37    : did_query_(false),
38      did_set_(false),
39      status_(true) {
40  }
41
42  virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
43    // The typecast is needed to work around gtest trying to take the address
44    // of a constant.
45    EXPECT_EQ((int) NaClValidationQuery::kDigestLength,
46              (int) signature.length());
47    EXPECT_FALSE(did_query_);
48    EXPECT_FALSE(did_set_);
49    did_query_ = true;
50    memcpy(query_signature_, signature.data(),
51           NaClValidationQuery::kDigestLength);
52    return status_;
53  }
54
55  virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
56    // The typecast is needed to work around gtest trying to take the address
57    // of a constant.
58    ASSERT_EQ((int) NaClValidationQuery::kDigestLength,
59              (int) signature.length());
60    EXPECT_TRUE(did_query_);
61    EXPECT_FALSE(did_set_);
62    did_set_ = true;
63    memcpy(set_signature_, signature.data(),
64           NaClValidationQuery::kDigestLength);
65    // Signatures should be the same.
66    EXPECT_EQ(0, memcmp(query_signature_, set_signature_,
67                        NaClValidationQuery::kDigestLength));
68  }
69
70  virtual bool ResolveFileToken(struct NaClFileToken* file_token, int32* fd,
71                                std::string* path) OVERRIDE {
72    *fd = -1;
73    *path = "";
74    return false;
75  }
76
77  bool did_query_;
78  bool did_set_;
79  bool status_;
80
81  uint8 query_signature_[NaClValidationQuery::kDigestLength];
82  uint8 set_signature_[NaClValidationQuery::kDigestLength];
83};
84
85class TestQuery {
86 public:
87  TestQuery(const char* key, const char* version) {
88    db.reset(new MockValidationDB());
89    context.reset(new NaClValidationQueryContext(db.get(), key, version));
90    query.reset(context->CreateQuery());
91  }
92
93  scoped_ptr<MockValidationDB> db;
94  scoped_ptr<NaClValidationQueryContext> context;
95  scoped_ptr<NaClValidationQuery> query;
96};
97
98class NaClValidationQueryTest : public ::testing::Test {
99 protected:
100  scoped_ptr<TestQuery> query1;
101  scoped_ptr<TestQuery> query2;
102
103  virtual void SetUp() {
104    query1.reset(new TestQuery(kKey, kVersion));
105    query2.reset(new TestQuery(kKey, kVersion));
106  }
107
108  void AssertQuerySame() {
109    ASSERT_TRUE(query1->db->did_query_);
110    ASSERT_TRUE(query2->db->did_query_);
111    ASSERT_EQ(0, memcmp(query1->db->query_signature_,
112                        query2->db->query_signature_,
113                        NaClValidationQuery::kDigestLength));
114  }
115
116  void AssertQueryDifferent() {
117    ASSERT_TRUE(query1->db->did_query_);
118    ASSERT_TRUE(query2->db->did_query_);
119    ASSERT_NE(0, memcmp(query1->db->query_signature_,
120                        query2->db->query_signature_,
121                        NaClValidationQuery::kDigestLength));
122  }
123};
124
125TEST_F(NaClValidationQueryTest, Sanity) {
126  query1->query->AddData(kShortData, sizeof(kShortData));
127  ASSERT_FALSE(query1->db->did_query_);
128  ASSERT_FALSE(query1->db->did_set_);
129  ASSERT_EQ(1, query1->query->QueryKnownToValidate());
130  ASSERT_TRUE(query1->db->did_query_);
131  ASSERT_FALSE(query1->db->did_set_);
132  query1->query->SetKnownToValidate();
133  ASSERT_TRUE(query1->db->did_query_);
134  ASSERT_TRUE(query1->db->did_set_);
135}
136
137TEST_F(NaClValidationQueryTest, ConsistentShort) {
138  query1->query->AddData(kShortData, sizeof(kShortData));
139  query1->query->QueryKnownToValidate();
140
141  query2->query->AddData(kShortData, sizeof(kShortData));
142  query2->query->QueryKnownToValidate();
143
144  AssertQuerySame();
145}
146
147TEST_F(NaClValidationQueryTest, InconsistentShort) {
148  query1->query->AddData(kShortData, sizeof(kShortData));
149  query1->query->QueryKnownToValidate();
150
151  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
152  query2->query->QueryKnownToValidate();
153
154  AssertQueryDifferent();
155}
156
157// Test for a bug caught during development where AddData would accidently
158// overwrite previously written data and add uninitialzied memory to the hash.
159TEST_F(NaClValidationQueryTest, ConsistentShortBug) {
160  query1->query->AddData(kShortData, sizeof(kShortData));
161  query1->query->AddData(kShortData, sizeof(kShortData));
162  query1->query->QueryKnownToValidate();
163
164  query2->query->AddData(kShortData, sizeof(kShortData));
165  query2->query->AddData(kShortData, sizeof(kShortData));
166  query2->query->QueryKnownToValidate();
167
168  AssertQuerySame();
169}
170
171// Test for a bug caught during development where AddData would accidently
172// overwrite previously written data and add uninitialzed memory to the hash.
173TEST_F(NaClValidationQueryTest, InconsistentShortBug1) {
174  query1->query->AddData(kShortData, sizeof(kShortData));
175  query1->query->AddData(kShortData, sizeof(kShortData));
176  query1->query->QueryKnownToValidate();
177
178  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
179  query2->query->AddData(kShortData, sizeof(kShortData));
180  query2->query->QueryKnownToValidate();
181
182  AssertQueryDifferent();
183}
184
185// Make sure we don't ignore the second bit of data.
186TEST_F(NaClValidationQueryTest, InconsistentShort2) {
187  query1->query->AddData(kShortData, sizeof(kShortData));
188  query1->query->AddData(kShortData, sizeof(kShortData));
189  query1->query->QueryKnownToValidate();
190
191  query2->query->AddData(kShortData, sizeof(kShortData));
192  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
193  query2->query->QueryKnownToValidate();
194
195  AssertQueryDifferent();
196}
197
198TEST_F(NaClValidationQueryTest, InconsistentZeroSizedAdd) {
199  query1->query->AddData(kShortData, sizeof(kShortData));
200  query1->query->QueryKnownToValidate();
201
202  query2->query->AddData(kShortData, sizeof(kShortData));
203  query2->query->AddData(kShortData, 0);
204  query2->query->QueryKnownToValidate();
205
206  AssertQueryDifferent();
207}
208
209TEST_F(NaClValidationQueryTest, ConsistentZeroSizedAdd) {
210  query1->query->AddData(kShortData, sizeof(kShortData));
211  query1->query->AddData("a", 0);
212  query1->query->QueryKnownToValidate();
213
214  query2->query->AddData(kShortData, sizeof(kShortData));
215  query2->query->AddData("b", 0);
216  query2->query->QueryKnownToValidate();
217
218  AssertQuerySame();
219}
220
221TEST_F(NaClValidationQueryTest, ConsistentRepeatedShort) {
222  for (int i = 0; i < 30; i++) {
223    query1->query->AddData(kShortData, sizeof(kShortData));
224  }
225  query1->query->QueryKnownToValidate();
226
227  for (int i = 0; i < 30; i++) {
228    query2->query->AddData(kShortData, sizeof(kShortData));
229  }
230  query2->query->QueryKnownToValidate();
231
232  AssertQuerySame();
233}
234
235TEST_F(NaClValidationQueryTest, ConsistentLong) {
236  query1->query->AddData(kLongData, sizeof(kLongData));
237  query1->query->QueryKnownToValidate();
238
239  query2->query->AddData(kLongData, sizeof(kLongData));
240  query2->query->QueryKnownToValidate();
241
242  AssertQuerySame();
243}
244
245TEST_F(NaClValidationQueryTest, ConsistentRepeatedLong) {
246  for (int i = 0; i < 30; i++) {
247    query1->query->AddData(kLongData, sizeof(kLongData));
248  }
249  query1->query->QueryKnownToValidate();
250
251  for (int i = 0; i < 30; i++) {
252    query2->query->AddData(kLongData, sizeof(kLongData));
253  }
254  query2->query->QueryKnownToValidate();
255
256  AssertQuerySame();
257}
258
259TEST_F(NaClValidationQueryTest, PerturbKey) {
260  query2.reset(new TestQuery(kKeyAlt, kVersion));
261
262  query1->query->AddData(kShortData, sizeof(kShortData));
263  query1->query->QueryKnownToValidate();
264
265  query2->query->AddData(kShortData, sizeof(kShortData));
266  query2->query->QueryKnownToValidate();
267
268  AssertQueryDifferent();
269}
270
271TEST_F(NaClValidationQueryTest, PerturbVersion) {
272  query2.reset(new TestQuery(kKey, kVersionAlt));
273
274  query1->query->AddData(kShortData, sizeof(kShortData));
275  query1->query->QueryKnownToValidate();
276
277  query2->query->AddData(kShortData, sizeof(kShortData));
278  query2->query->QueryKnownToValidate();
279
280  AssertQueryDifferent();
281}
282
283}
284