blob: e27597ead2f8e15a542d2ce8181e9e9a4ceaeff0 [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 "peridot/bin/ledger/encryption/impl/encryption_service_impl.h"
#include <flatbuffers/flatbuffers.h>
#include <lib/async/cpp/task.h>
#include <lib/callback/scoped_callback.h>
#include <lib/fit/function.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/memory/weak_ptr.h>
#include <lib/fxl/strings/concatenate.h>
#include "peridot/bin/ledger/encryption/impl/encrypted_commit_generated.h"
#include "peridot/bin/ledger/encryption/primitives/encrypt.h"
#include "peridot/bin/ledger/encryption/primitives/kdf.h"
namespace encryption {
namespace {
// The default encryption values. Only used until real encryption is
// implemented: LE-286
//
// Use max_int32 for key_index as it will never be used in practice as it is not
// expected that any user will change its key 2^32 times.
constexpr uint32_t kDefaultKeyIndex = std::numeric_limits<uint32_t>::max();
// Use max_int32 - 1 for default deletion scoped id. max_int32 has a special
// meaning in the specification and is used to have per object deletion scope.
constexpr uint32_t kDefaultDeletionScopeId =
std::numeric_limits<uint32_t>::max() - 1;
// Special deletion scope id that produces a per-object deletion scope.
constexpr uint32_t kPerObjectDeletionScopedId =
std::numeric_limits<uint32_t>::max();
// Size of keys. Key must have 128 bits of entropy. Randomly generated keys can
// be 128 bits long, but derived ones need to be twice as big because of the
// birthday paradox.
// Size of the randomly generated key.
constexpr size_t kRandomlyGeneratedKeySize = 16u;
// Size of the derived keys.
constexpr size_t kDerivedKeySize = 32u;
// Cache size values.
constexpr size_t kKeyIndexCacheSize = 10u;
constexpr size_t kReferenceKeysCacheSize = 10u;
// Checks whether the given |storage_bytes| are a valid serialization of an
// encrypted commit.
bool CheckValidSerialization(fxl::StringView storage_bytes) {
flatbuffers::Verifier verifier(
reinterpret_cast<const unsigned char*>(storage_bytes.data()),
storage_bytes.size());
return VerifyEncryptedCommitStorageBuffer(verifier);
}
} // namespace
// Fake implementation of a key service for the Ledger.
//
// This implementation generate fake keys and will need to be replaced by a
// real component.
class EncryptionServiceImpl::KeyService {
public:
explicit KeyService(async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher), weak_factory_(this) {}
// Retrieves the master key.
void GetMasterKey(uint32_t key_index,
fit::function<void(std::string)> callback) {
async::PostTask(dispatcher_,
callback::MakeScoped(
weak_factory_.GetWeakPtr(),
[key_index, callback = std::move(callback)]() {
std::string master_key(16u, 0);
memcpy(&master_key[0], &key_index, sizeof(key_index));
callback(std::move(master_key));
}));
}
// Retrieves the reference key associated to the given namespace and reference
// key. If the id is not yet associated with a reference key, generates a new
// one and associates it with the id before returning.
void GetReferenceKey(const std::string& namespace_id,
const std::string& reference_key_id,
fit::function<void(const std::string&)> callback) {
std::string result =
HMAC256KDF(fxl::Concatenate({namespace_id, reference_key_id}),
kRandomlyGeneratedKeySize);
async::PostTask(
dispatcher_,
callback::MakeScoped(
weak_factory_.GetWeakPtr(),
[result = std::move(result),
callback = std::move(callback)]() mutable { callback(result); }));
}
private:
async_dispatcher_t* const dispatcher_;
fxl::WeakPtrFactory<EncryptionServiceImpl::KeyService> weak_factory_;
};
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())),
master_keys_(kKeyIndexCacheSize, Status::OK,
[this](auto k, auto c) {
FetchMasterKey(std::move(k), std::move(c));
}),
namespace_keys_(kKeyIndexCacheSize, Status::OK,
[this](auto k, auto c) {
FetchNamespaceKey(std::move(k), std::move(c));
}),
reference_keys_(kReferenceKeysCacheSize, Status::OK,
[this](auto k, auto c) {
FetchReferenceKey(std::move(k), std::move(c));
}) {}
EncryptionServiceImpl::~EncryptionServiceImpl() {}
storage::ObjectIdentifier EncryptionServiceImpl::MakeObjectIdentifier(
storage::ObjectDigest digest) {
return {GetCurrentKeyIndex(), kDefaultDeletionScopeId, 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, std::string(reinterpret_cast<const char*>(
builder.GetBufferPointer()),
builder.GetSize()));
});
}
void EncryptionServiceImpl::DecryptCommit(
convert::ExtendedStringView storage_bytes,
fit::function<void(Status, std::string)> callback) {
if (!CheckValidSerialization(storage_bytes)) {
FXL_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::GetObjectName(
storage::ObjectIdentifier object_identifier,
fit::function<void(Status, std::string)> callback) {
GetReferenceKey(object_identifier, [object_identifier,
callback = std::move(callback)](
const std::string& reference_key) {
callback(Status::OK,
HMAC256KDF(fxl::Concatenate(
{reference_key,
object_identifier.object_digest().Serialize()}),
kDerivedKeySize));
});
}
void EncryptionServiceImpl::EncryptObject(
storage::ObjectIdentifier object_identifier, fsl::SizedVmo content,
fit::function<void(Status, std::string)> callback) {
std::string data;
if (!fsl::StringFromVmo(content, &data)) {
callback(Status::IO_ERROR, "");
return;
}
Encrypt(object_identifier.key_index(), std::move(data), 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::GetReferenceKey(
storage::ObjectIdentifier object_identifier,
fit::function<void(const std::string&)> callback) {
std::string deletion_scope_seed;
if (object_identifier.deletion_scope_id() == kPerObjectDeletionScopedId) {
deletion_scope_seed = object_identifier.object_digest().Serialize();
} else {
const uint32_t deletion_scope_id = object_identifier.deletion_scope_id();
deletion_scope_seed =
std::string(reinterpret_cast<const char*>(&deletion_scope_id),
sizeof(deletion_scope_id));
}
DeletionScopeSeed seed = {object_identifier.key_index(),
std::move(deletion_scope_seed)};
reference_keys_.Get(
seed, [callback = std::move(callback)](
Status status, const std::string& value) { callback(value); });
}
void EncryptionServiceImpl::Encrypt(
size_t key_index, std::string data,
fit::function<void(Status, std::string)> callback) {
master_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) {
master_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::FetchMasterKey(
size_t key_index, fit::function<void(Status, std::string)> callback) {
key_service_->GetMasterKey(
key_index, [callback = std::move(callback)](std::string master_key) {
callback(Status::OK, std::move(master_key));
});
}
void EncryptionServiceImpl::FetchNamespaceKey(
size_t key_index, fit::function<void(Status, std::string)> callback) {
master_keys_.Get(
key_index, [this, callback = std::move(callback)](
Status status, const std::string& master_key) {
if (status != Status::OK) {
callback(status, "");
return;
}
callback(Status::OK,
HMAC256KDF(fxl::Concatenate({master_key, namespace_id_}),
kDerivedKeySize));
});
}
void EncryptionServiceImpl::FetchReferenceKey(
DeletionScopeSeed deletion_scope_seed,
fit::function<void(Status, std::string)> callback) {
namespace_keys_.Get(
deletion_scope_seed.first,
[this, deletion_scope_seed = std::move(deletion_scope_seed),
callback = std::move(callback)](
Status status, const std::string& namespace_key) mutable {
if (status != Status::OK) {
callback(status, "");
return;
}
key_service_->GetReferenceKey(
namespace_id_,
HMAC256KDF(
fxl::Concatenate({namespace_key, deletion_scope_seed.second}),
kDerivedKeySize),
[callback = std::move(callback)](std::string reference_key) {
callback(Status::OK, std::move(reference_key));
});
});
}
} // namespace encryption