blob: 19f28feb137f40de553baaacf4322e068807a03c [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "src/ledger/bin/encryption/impl/encryption_service_impl.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <algorithm>
#include "src/ledger/bin/encryption/impl/encrypted_commit_generated.h"
#include "src/ledger/bin/encryption/impl/encrypted_entry_generated.h"
#include "src/ledger/bin/encryption/impl/remote_commit_id_generated.h"
#include "src/ledger/bin/encryption/primitives/encrypt.h"
#include "src/ledger/bin/encryption/primitives/hash.h"
#include "src/ledger/bin/encryption/primitives/hmac.h"
#include "src/ledger/bin/encryption/primitives/kdf.h"
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/logging/logging.h"
#include "src/ledger/lib/vmo/strings.h"
#include "third_party/abseil-cpp/absl/strings/str_cat.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
#include "third_party/flatbuffers/include/flatbuffers/flatbuffers.h"
namespace encryption {
namespace {
// Version of the encryption scheme.
// This is used to check that the encryption scheme used in the data obtained from the cloud
// matches the one currently used.
// TODO(mariagl): Use this for the backward compatibility.
constexpr uint32_t kEncryptionVersion = 0;
// Entry id size in bytes.
constexpr size_t kEntryIdSize = 32u;
// Cache size values.
constexpr size_t kKeyIndexCacheSize = 10u;
// Checks whether the given |storage_bytes| are a valid serialization of an
// encrypted commit.
bool CheckValidEncryptedCommitSerialization(absl::string_view storage_bytes) {
flatbuffers::Verifier verifier(reinterpret_cast<const unsigned char*>(storage_bytes.data()),
storage_bytes.size());
return VerifyEncryptedCommitStorageBuffer(verifier);
}
// Checks whether the given |storage_bytes| are a valid serialization of an
// encrypted entry.
bool CheckValidEncryptedEntrySerialization(absl::string_view storage_bytes) {
flatbuffers::Verifier verifier(reinterpret_cast<const unsigned char*>(storage_bytes.data()),
storage_bytes.size());
return VerifyEncryptedEntryStorageBuffer(verifier);
}
} // namespace
EncryptionServiceImpl::EncryptionServiceImpl(ledger::Environment* environment,
std::string namespace_id)
: environment_(environment),
namespace_id_(std::move(namespace_id)),
key_service_(std::make_unique<KeyService>(environment_->dispatcher(), namespace_id_)),
encryption_keys_(
kKeyIndexCacheSize, Status::OK,
[this](auto k, auto c) { key_service_->GetEncryptionKey(std::move(k), std::move(c)); }),
remote_id_keys_(kKeyIndexCacheSize, Status::OK,
[this](auto k, auto c) {
key_service_->GetRemoteObjectIdKey(std::move(k), std::move(c));
}),
chunking_key_(Status::OK, [this](auto c) { key_service_->GetChunkingKey(std::move(c)); }),
page_id_key_(Status::OK, [this](auto c) { key_service_->GetPageIdKey(std::move(c)); }) {}
EncryptionServiceImpl::~EncryptionServiceImpl() = default;
storage::ObjectIdentifier EncryptionServiceImpl::MakeObjectIdentifier(
storage::ObjectIdentifierFactory* factory, storage::ObjectDigest digest) {
return factory->MakeObjectIdentifier(GetCurrentKeyIndex(), std::move(digest));
}
void EncryptionServiceImpl::EncryptCommit(std::string commit_storage,
fit::function<void(Status, std::string)> callback) {
size_t key_index = GetCurrentKeyIndex();
Encrypt(
key_index, std::move(commit_storage),
[key_index, callback = std::move(callback)](Status status, std::string encrypted_storage) {
if (status != Status::OK) {
callback(status, "");
return;
}
flatbuffers::FlatBufferBuilder builder;
auto storage = CreateEncryptedCommitStorage(
builder, key_index, convert::ToFlatBufferVector(&builder, encrypted_storage));
builder.Finish(storage);
callback(Status::OK, convert::ToString(builder));
});
}
std::string EncryptionServiceImpl::EncodeCommitId(std::string commit_id) {
flatbuffers::FlatBufferBuilder builder;
auto storage =
CreateRemoteCommitId(builder, kEncryptionVersion,
convert::ToFlatBufferVector(&builder, SHA256WithLengthHash(commit_id)));
builder.Finish(storage);
return convert::ToString(builder);
}
void EncryptionServiceImpl::GetPageId(std::string page_name,
fit::function<void(Status, std::string)> callback) {
page_id_key_.Get([page_name = std::move(page_name), callback = std::move(callback)](
Status status, std::string page_id_key) {
if (status != Status::OK) {
callback(status, "");
return;
}
callback(Status::OK, SHA256HMAC(page_id_key, page_name));
});
}
void EncryptionServiceImpl::DecryptCommit(convert::ExtendedStringView storage_bytes,
fit::function<void(Status, std::string)> callback) {
if (!CheckValidEncryptedCommitSerialization(storage_bytes)) {
LEDGER_LOG(WARNING) << "Received invalid data. Cannot decrypt commit.";
callback(Status::INVALID_ARGUMENT, "");
return;
}
const EncryptedCommitStorage* encrypted_commit_storage =
GetEncryptedCommitStorage(storage_bytes.data());
Decrypt(encrypted_commit_storage->key_index(),
convert::ToString(encrypted_commit_storage->serialized_encrypted_commit_storage()),
std::move(callback));
}
void EncryptionServiceImpl::EncryptEntryPayload(std::string entry_storage,
fit::function<void(Status, std::string)> callback) {
size_t key_index = GetCurrentKeyIndex();
Encrypt(
key_index, std::move(entry_storage),
[key_index, callback = std::move(callback)](Status status, std::string encrypted_storage) {
if (status != Status::OK) {
callback(status, "");
return;
}
flatbuffers::FlatBufferBuilder builder;
auto storage = CreateEncryptedEntryStorage(
builder, key_index, convert::ToFlatBufferVector(&builder, encrypted_storage));
builder.Finish(storage);
callback(Status::OK, convert::ToString(builder));
});
}
void EncryptionServiceImpl::DecryptEntryPayload(std::string encrypted_data,
fit::function<void(Status, std::string)> callback) {
if (!CheckValidEncryptedEntrySerialization(encrypted_data)) {
LEDGER_LOG(WARNING) << "Received invalid data. Cannot decrypt the entry payload.";
callback(Status::INVALID_ARGUMENT, "");
return;
}
const EncryptedEntryStorage* encrypted_entry_storage =
GetEncryptedEntryStorage(encrypted_data.data());
Decrypt(encrypted_entry_storage->key_index(),
convert::ToString(encrypted_entry_storage->serialized_encrypted_entry_storage()),
std::move(callback));
}
void EncryptionServiceImpl::GetObjectName(storage::ObjectIdentifier object_identifier,
fit::function<void(Status, std::string)> callback) {
remote_id_keys_.Get(
object_identifier.key_index(),
[object_identifier = std::move(object_identifier), callback = std::move(callback)](
Status status, std::string remote_object_id_key) {
if (status != Status::OK) {
callback(status, "");
return;
}
callback(Status::OK,
SHA256HMAC(remote_object_id_key, object_identifier.object_digest().Serialize()));
});
}
void EncryptionServiceImpl::EncryptObject(storage::ObjectIdentifier object_identifier,
absl::string_view content,
fit::function<void(Status, std::string)> callback) {
Encrypt(object_identifier.key_index(), convert::ToString(content), std::move(callback));
}
void EncryptionServiceImpl::DecryptObject(storage::ObjectIdentifier object_identifier,
std::string encrypted_data,
fit::function<void(Status, std::string)> callback) {
Decrypt(object_identifier.key_index(), std::move(encrypted_data), std::move(callback));
}
uint32_t EncryptionServiceImpl::GetCurrentKeyIndex() { return kDefaultKeyIndex; }
void EncryptionServiceImpl::Encrypt(size_t key_index, std::string data,
fit::function<void(Status, std::string)> callback) {
encryption_keys_.Get(
key_index, [environment = environment_, data = std::move(data),
callback = std::move(callback)](Status status, const std::string& key) {
if (status != Status::OK) {
callback(status, "");
return;
}
std::string encrypted_data;
if (!AES128GCMSIVEncrypt(environment->random(), key, data, &encrypted_data)) {
callback(Status::INTERNAL_ERROR, "");
return;
}
callback(Status::OK, std::move(encrypted_data));
});
}
void EncryptionServiceImpl::Decrypt(size_t key_index, std::string encrypted_data,
fit::function<void(Status, std::string)> callback) {
encryption_keys_.Get(key_index,
[encrypted_data = std::move(encrypted_data), callback = std::move(callback)](
Status status, const std::string& key) {
if (status != Status::OK) {
callback(status, "");
return;
}
std::string data;
if (!AES128GCMSIVDecrypt(key, encrypted_data, &data)) {
callback(Status::INTERNAL_ERROR, "");
return;
}
callback(Status::OK, std::move(data));
});
}
void EncryptionServiceImpl::GetChunkingPermutation(
fit::function<void(Status, fit::function<uint64_t(uint64_t)>)> callback) {
chunking_key_.Get(
[callback = std::move(callback)](Status status, const std::string& chunking_key) {
if (status != Status::OK) {
callback(status, nullptr);
return;
}
const uint64_t chunking_permutation_key =
*reinterpret_cast<const uint64_t*>(chunking_key.data());
// TODO(35273): Use some other permutation.
auto chunking_permutation = [chunking_permutation_key](uint64_t chunk_window_hash) {
return chunk_window_hash ^ chunking_permutation_key;
};
callback(Status::OK, std::move(chunking_permutation));
});
}
std::string EncryptionServiceImpl::GetEntryId() {
std::string entry_id;
entry_id.resize(kEntryIdSize);
(environment_->random())->Draw(&entry_id[0], kEntryIdSize);
return entry_id;
}
std::string EncryptionServiceImpl::GetEntryIdForMerge(absl::string_view entry_name,
storage::CommitId left_parent_id,
storage::CommitId right_parent_id,
absl::string_view operation_list) {
// TODO(LE-827): Concatenation is ineffective; consider doing it once per commit.
std::string input = absl::StrCat(entry_name, left_parent_id, right_parent_id, operation_list);
std::string hash = SHA256WithLengthHash(input);
hash.resize(kEntryIdSize);
return hash;
}
bool EncryptionServiceImpl::IsSameVersion(convert::ExtendedStringView remote_commit_id) {
flatbuffers::Verifier verifier(reinterpret_cast<const unsigned char*>(remote_commit_id.data()),
remote_commit_id.size());
if (!VerifyRemoteCommitIdBuffer(verifier)) {
LEDGER_LOG(WARNING) << "Received invalid data. Cannot check the version.";
return false;
}
const RemoteCommitId* data = GetRemoteCommitId(remote_commit_id.data());
return data->version() == kEncryptionVersion;
}
} // namespace encryption