foundationdb/fdbserver/SimEncryptVaultProxy.actor.cpp

153 lines
5.4 KiB
C++

/*
* SimEncryptVaulProxy.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <memory>
#include <unordered_map>
#include "fdbrpc/sim_validation.h"
#include "fdbserver/SimEncryptVaultProxy.actor.h"
#include "flow/ActorCollection.h"
#include "flow/Error.h"
#include "flow/IRandom.h"
#include "flow/ITrace.h"
#include "flow/StreamCipher.h"
#include "flow/UnitTest.h"
#include "flow/actorcompiler.h" // This must be the last #include.
struct SimEncryptKeyCtx {
SimEncryptKeyId id;
SimEncryptKey key;
SimEncryptKeyCtx() : id(0) {}
explicit SimEncryptKeyCtx(SimEncryptKeyId kId, const char* data) : id(kId), key(data) {}
};
struct SimEncyrptVaultProxyContext {
uint32_t maxEncryptionKeys;
std::unordered_map<SimEncryptKeyId, std::unique_ptr<SimEncryptKeyCtx>> simEncryptKeyStore;
SimEncyrptVaultProxyContext() : maxEncryptionKeys(0) {}
explicit SimEncyrptVaultProxyContext(uint32_t keyCount) : maxEncryptionKeys(keyCount) {
uint8_t buffer[AES_256_KEY_LENGTH];
// Construct encryption keyStore.
for (int i = 0; i < maxEncryptionKeys; i++) {
generateRandomData(&buffer[0], AES_256_KEY_LENGTH);
SimEncryptKeyCtx ctx(i, reinterpret_cast<const char*>(buffer));
simEncryptKeyStore[i] = std::make_unique<SimEncryptKeyCtx>(i, reinterpret_cast<const char*>(buffer));
}
}
};
ACTOR Future<Void> simEncryptVaultProxyCore(SimEncryptVaultProxyInterface interf, uint32_t maxEncryptKeys) {
state SimEncyrptVaultProxyContext vaultProxyCtx(maxEncryptKeys);
ASSERT(vaultProxyCtx.simEncryptKeyStore.size() == maxEncryptKeys);
TraceEvent("SimEncryptVaultProxy_Init", interf.id()).detail("MaxEncrptKeys", maxEncryptKeys);
loop {
choose {
when(SimGetEncryptKeyByKeyIdRequest req = waitNext(interf.encryptKeyLookupByKeyId.getFuture())) {
SimGetEncryptKeyByKeyIdReply reply;
// Lookup corresponding EncryptKeyCtx for input keyId
if (vaultProxyCtx.simEncryptKeyStore.find(req.encryptKeyId) != vaultProxyCtx.simEncryptKeyStore.end()) {
reply.encryptKey = StringRef(vaultProxyCtx.simEncryptKeyStore[req.encryptKeyId].get()->key);
req.reply.send(reply);
} else {
req.reply.sendError(key_not_found());
}
}
when(SimGetEncryptKeyByDomainIdRequest req = waitNext(interf.encryptKeyLookupByDomainId.getFuture())) {
SimGetEncryptKeyByDomainIdReply reply;
// Map encryptionDomainId to corresponding EncryptKeyCtx element using a modulo operation. This would
// mean multiple domains gets mapped to the same encryption key which is fine, the EncryptKeyStore
// guarantees that keyId -> plaintext encryptKey mapping is idempotent.
reply.encryptKeyId = req.encryptDomainId % maxEncryptKeys;
reply.encryptKey = StringRef(vaultProxyCtx.simEncryptKeyStore[reply.encryptKeyId].get()->key);
req.reply.send(reply);
}
}
}
}
void forceLinkSimEncryptVaultProxyTests() {}
namespace {
ACTOR Future<Void> testRunWorkload(SimEncryptVaultProxyInterface inf, uint32_t nEncryptionKeys) {
state uint32_t maxEncryptionKeys = nEncryptionKeys;
state int maxDomainIds = deterministicRandom()->randomInt(121, 295);
state int maxIterations = deterministicRandom()->randomInt(786, 1786);
state std::unordered_map<SimEncryptDomainId, std::unique_ptr<SimEncryptKeyCtx>> domainIdKeyMap;
state int i = 0;
TraceEvent("RunWorkloadStart").detail("MaxDomainIds", maxDomainIds).detail("MaxIterations", maxIterations);
{
// construct domainId to EncryptKeyCtx map
for (i = 0; i < maxDomainIds; i++) {
SimGetEncryptKeyByDomainIdRequest req;
req.encryptDomainId = i;
SimGetEncryptKeyByDomainIdReply reply = wait(inf.encryptKeyLookupByDomainId.getReply(req));
domainIdKeyMap[i] =
std::make_unique<SimEncryptKeyCtx>(reply.encryptKeyId, reply.encryptKey.toString().c_str());
}
// randomly pick any domainId and validate if lookupByKeyId result matches
for (i = 0; i < maxIterations; i++) {
state int idx = deterministicRandom()->randomInt(0, maxDomainIds);
state SimEncryptKeyCtx* ctx = domainIdKeyMap[idx].get();
SimGetEncryptKeyByKeyIdRequest req(ctx->id);
SimGetEncryptKeyByKeyIdReply reply = wait(inf.encryptKeyLookupByKeyId.getReply(req));
ASSERT(reply.encryptKey.compare(ctx->key) == 0);
}
}
{
// Verify unknown key access returns the error
state SimGetEncryptKeyByKeyIdRequest req;
req.encryptKeyId = maxEncryptionKeys + 1;
try {
SimGetEncryptKeyByKeyIdReply reply = wait(inf.encryptKeyLookupByKeyId.getReply(req));
} catch (Error& e) {
ASSERT(e.code() == error_code_key_not_found);
}
}
TraceEvent("RunWorkloadDone").log();
return Void();
}
} // namespace
TEST_CASE("fdbserver/SimEncryptVaultProxy") {
state SimEncryptVaultProxyInterface inf;
state uint32_t maxEncryptKeys = 64;
loop choose {
when(wait(simEncryptVaultProxyCore(inf, maxEncryptKeys))) { throw internal_error(); }
when(wait(testRunWorkload(inf, maxEncryptKeys))) { break; }
}
return Void();
}