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