• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "components/nacl/loader/nacl_validation_query.h"
11 
12 #include <stdint.h>
13 #include <string.h>
14 
15 #include <string_view>
16 
17 #include "base/check.h"
18 #include "components/nacl/loader/nacl_validation_db.h"
19 #include "crypto/nss_util.h"
20 #include "native_client/src/public/validation_cache.h"
21 
NaClValidationQueryContext(NaClValidationDB * db,const std::string & profile_key,const std::string & nacl_version)22 NaClValidationQueryContext::NaClValidationQueryContext(
23     NaClValidationDB* db,
24     const std::string& profile_key,
25     const std::string& nacl_version)
26     : db_(db),
27       profile_key_(profile_key),
28       nacl_version_(nacl_version) {
29 
30   // Sanity checks.
31   CHECK(profile_key.length() >= 8);
32   CHECK(nacl_version.length() >= 4);
33 }
34 
CreateQuery()35 NaClValidationQuery* NaClValidationQueryContext::CreateQuery() {
36   NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_);
37   // Changing the version effectively invalidates existing hashes.
38   query->AddData(nacl_version_);
39   return query;
40 }
41 
NaClValidationQuery(NaClValidationDB * db,const std::string & profile_key)42 NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db,
43                                          const std::string& profile_key)
44     : state_(READY),
45       hasher_(crypto::HMAC::SHA256),
46       db_(db),
47       buffer_length_(0) {
48   CHECK(hasher_.Init(profile_key));
49 }
50 
AddData(const char * data,size_t length)51 void NaClValidationQuery::AddData(const char* data, size_t length) {
52   CHECK(state_ == READY);
53   CHECK(buffer_length_ <= sizeof(buffer_));
54   // Chrome's HMAC class doesn't support incremental signing.  Work around
55   // this by using a (small) temporary buffer to accumulate data.
56   // Check if there is space in the buffer.
57   if (buffer_length_ + kDigestLength > sizeof(buffer_)) {
58     // Hash the buffer to make space.
59     CompressBuffer();
60   }
61   // Hash the input data into the buffer.  Assumes that sizeof(buffer_) >=
62   // kDigestLength * 2 (the buffer can store at least two digests.)
63   CHECK(hasher_.Sign(std::string_view(data, length),
64                      reinterpret_cast<unsigned char*>(buffer_ + buffer_length_),
65                      kDigestLength));
66   buffer_length_ += kDigestLength;
67 }
68 
AddData(const unsigned char * data,size_t length)69 void NaClValidationQuery::AddData(const unsigned char* data, size_t length) {
70   AddData(reinterpret_cast<const char*>(data), length);
71 }
72 
AddData(std::string_view data)73 void NaClValidationQuery::AddData(std::string_view data) {
74   AddData(data.data(), data.length());
75 }
76 
QueryKnownToValidate()77 int NaClValidationQuery::QueryKnownToValidate() {
78   CHECK(state_ == READY);
79   // It is suspicious if we have less than a digest's worth of data.
80   CHECK(buffer_length_ >= kDigestLength);
81   CHECK(buffer_length_ <= sizeof(buffer_));
82   state_ = GET_CALLED;
83   // Ensure the buffer contains only one digest worth of data.
84   CompressBuffer();
85   return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_));
86 }
87 
SetKnownToValidate()88 void NaClValidationQuery::SetKnownToValidate() {
89   CHECK(state_ == GET_CALLED);
90   CHECK(buffer_length_ == kDigestLength);
91   state_ = SET_CALLED;
92   db_->SetKnownToValidate(std::string(buffer_, buffer_length_));
93 }
94 
95 // Reduce the size of the data in the buffer by hashing it and writing it back
96 // to the buffer.
CompressBuffer()97 void NaClValidationQuery::CompressBuffer() {
98   // Calculate the digest into a temp buffer.  It is likely safe to calculate it
99   // directly back into the buffer, but this is an "accidental" semantic we're
100   // avoiding depending on.
101   unsigned char temp[kDigestLength];
102   CHECK(hasher_.Sign(std::string_view(buffer_, buffer_length_), temp,
103                      kDigestLength));
104   memcpy(buffer_, temp, kDigestLength);
105   buffer_length_ = kDigestLength;
106 }
107 
108 // OO wrappers
109 
CreateQuery(void * handle)110 static void* CreateQuery(void* handle) {
111   return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery();
112 }
113 
AddData(void * query,const uint8_t * data,size_t length)114 static void AddData(void* query, const uint8_t* data, size_t length) {
115   static_cast<NaClValidationQuery*>(query)->AddData(data, length);
116 }
117 
QueryKnownToValidate(void * query)118 static int QueryKnownToValidate(void* query) {
119   return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate();
120 }
121 
SetKnownToValidate(void * query)122 static void SetKnownToValidate(void* query) {
123   static_cast<NaClValidationQuery*>(query)->SetKnownToValidate();
124 }
125 
DestroyQuery(void * query)126 static void DestroyQuery(void* query) {
127   delete static_cast<NaClValidationQuery*>(query);
128 }
129 
CreateValidationCache(NaClValidationDB * db,const std::string & profile_key,const std::string & nacl_version)130 struct NaClValidationCache* CreateValidationCache(
131     NaClValidationDB* db, const std::string& profile_key,
132     const std::string& nacl_version) {
133   NaClValidationCache* cache =
134       static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache)));
135   // Make sure any fields introduced in a cross-repo change are zeroed.
136   memset(cache, 0, sizeof(*cache));
137   cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version);
138   cache->CreateQuery = CreateQuery;
139   cache->AddData = AddData;
140   cache->QueryKnownToValidate = QueryKnownToValidate;
141   cache->SetKnownToValidate = SetKnownToValidate;
142   cache->DestroyQuery = DestroyQuery;
143   return cache;
144 }
145