Add ScopeExit to flow and remove scattered impls

This commit is contained in:
Junhyun Shim 2022-05-11 11:46:08 +02:00
parent e5de8ba260
commit 8789232df4
5 changed files with 86 additions and 100 deletions

View File

@ -24,7 +24,9 @@
#include "flow/Arena.h"
#include "flow/Error.h"
#include "flow/IRandom.h"
#include "flow/MkCert.h"
#include "flow/Platform.h"
#include "flow/ScopeExit.h"
#include "flow/Trace.h"
#include "flow/UnitTest.h"
#include <type_traits>
@ -35,16 +37,6 @@
namespace {
template <typename Func>
class ExitGuard {
std::decay_t<Func> fn;
public:
ExitGuard(Func&& fn) : fn(std::forward<Func>(fn)) {}
~ExitGuard() { fn(); }
};
[[noreturn]] void traceAndThrow(const char* type) {
auto te = TraceEvent(SevWarnAlways, type);
te.suppressFor(60);
@ -53,68 +45,16 @@ public:
0,
};
::ERR_error_string_n(err, buf, sizeof(buf));
te.detail("OpenSSLError", buf);
te.detail("OpenSSLError", static_cast<const char*>(buf));
}
throw digital_signature_ops_error();
}
struct KeyPairRef {
StringRef privateKey;
StringRef publicKey;
};
Standalone<KeyPairRef> generateEcdsaKeyPair() {
auto params = std::add_pointer_t<EVP_PKEY>();
{
auto pctx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
ASSERT(pctx);
auto ctxGuard = ExitGuard([pctx]() { ::EVP_PKEY_CTX_free(pctx); });
ASSERT_LT(0, ::EVP_PKEY_paramgen_init(pctx));
ASSERT_LT(0, ::EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1));
ASSERT_LT(0, ::EVP_PKEY_paramgen(pctx, &params));
ASSERT(params);
}
auto paramsGuard = ExitGuard([params]() { ::EVP_PKEY_free(params); });
// keygen
auto kctx = ::EVP_PKEY_CTX_new(params, nullptr);
ASSERT(kctx);
auto kctxGuard = ExitGuard([kctx]() { ::EVP_PKEY_CTX_free(kctx); });
auto key = std::add_pointer_t<EVP_PKEY>();
{
ASSERT_LT(0, ::EVP_PKEY_keygen_init(kctx));
ASSERT_LT(0, ::EVP_PKEY_keygen(kctx, &key));
}
ASSERT(key);
auto keyGuard = ExitGuard([key]() { ::EVP_PKEY_free(key); });
auto ret = Standalone<KeyPairRef>{};
auto& arena = ret.arena();
{
auto len = 0;
len = ::i2d_PrivateKey(key, nullptr);
ASSERT_LT(0, len);
auto buf = new (arena) uint8_t[len];
auto out = std::add_pointer_t<uint8_t>(buf);
len = ::i2d_PrivateKey(key, &out);
ret.privateKey = StringRef(buf, len);
}
{
auto len = 0;
len = ::i2d_PUBKEY(key, nullptr);
ASSERT_LT(0, len);
auto buf = new (arena) uint8_t[len];
auto out = std::add_pointer_t<uint8_t>(buf);
len = ::i2d_PUBKEY(key, &out);
ret.publicKey = StringRef(buf, len);
}
return ret;
}
} // namespace
Standalone<SignedAuthTokenRef> signToken(AuthTokenRef token, StringRef keyName, StringRef privateKeyDer) {
auto ret = Standalone<SignedAuthTokenRef>{};
auto arena = ret.arena();
auto& arena = ret.arena();
auto writer = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, IncludeVersion());
writer.serialize(token);
auto tokenStr = writer.toStringRef();
@ -124,11 +64,11 @@ Standalone<SignedAuthTokenRef> signToken(AuthTokenRef token, StringRef keyName,
if (!key) {
traceAndThrow("SignTokenBadKey");
}
auto keyGuard = ExitGuard([key]() { ::EVP_PKEY_free(key); });
auto keyGuard = ScopeExit([key]() { ::EVP_PKEY_free(key); });
auto mdctx = ::EVP_MD_CTX_create();
if (!mdctx)
traceAndThrow("SignTokenInitFail");
auto mdctxGuard = ExitGuard([mdctx]() { ::EVP_MD_CTX_free(mdctx); });
auto mdctxGuard = ScopeExit([mdctx]() { ::EVP_MD_CTX_free(mdctx); });
if (1 != ::EVP_DigestSignInit(mdctx, nullptr, ::EVP_sha256() /*Parameterize?*/, nullptr, key))
traceAndThrow("SignTokenInitFail");
if (1 != ::EVP_DigestSignUpdate(mdctx, tokenStr.begin(), tokenStr.size()))
@ -150,11 +90,11 @@ bool verifyToken(SignedAuthTokenRef signedToken, StringRef publicKeyDer) {
auto key = ::d2i_PUBKEY(nullptr, &rawPubKeyDer, publicKeyDer.size());
if (!key)
traceAndThrow("VerifyTokenBadKey");
auto keyGuard = ExitGuard([key]() { ::EVP_PKEY_free(key); });
auto keyGuard = ScopeExit([key]() { ::EVP_PKEY_free(key); });
auto mdctx = ::EVP_MD_CTX_create();
if (!mdctx)
traceAndThrow("VerifyTokenInitFail");
auto mdctxGuard = ExitGuard([mdctx]() { ::EVP_MD_CTX_free(mdctx); });
auto mdctxGuard = ScopeExit([mdctx]() { ::EVP_MD_CTX_free(mdctx); });
if (1 != ::EVP_DigestVerifyInit(mdctx, nullptr, ::EVP_sha256(), nullptr, key))
traceAndThrow("VerifyTokenInitFail");
if (1 != ::EVP_DigestVerifyUpdate(mdctx, signedToken.token.begin(), signedToken.token.size()))
@ -179,9 +119,10 @@ void forceLinkTokenSignTests() {}
TEST_CASE("/fdbrpc/TokenSign") {
const auto numIters = 100;
for (auto i = 0; i < numIters; i++) {
auto keyPair = generateEcdsaKeyPair();
auto kpArena = Arena();
auto keyPair = mkcert::KeyPairRef::make(kpArena);
auto token = Standalone<AuthTokenRef>{};
auto arena = token.arena();
auto& arena = token.arena();
auto& rng = *deterministicRandom();
token.expiresAt = timer_monotonic() * (0.5 + rng.random01());
if (auto setIp = rng.randomInt(0, 3)) {
@ -206,15 +147,15 @@ TEST_CASE("/fdbrpc/TokenSign") {
token.tenants.push_back(arena, genRandomStringRef());
}
auto keyName = genRandomStringRef();
auto signedToken = signToken(token, keyName, keyPair.privateKey);
const auto verifyExpectOk = verifyToken(signedToken, keyPair.publicKey);
auto signedToken = signToken(token, keyName, keyPair.privateKeyDer);
const auto verifyExpectOk = verifyToken(signedToken, keyPair.publicKeyDer);
ASSERT(verifyExpectOk);
// try tampering with signed token by adding one more tenant
token.tenants.push_back(arena, genRandomStringRef());
auto writer = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, IncludeVersion());
writer.serialize(token);
signedToken.token = writer.toStringRef();
const auto verifyExpectFail = verifyToken(signedToken, keyPair.publicKey);
const auto verifyExpectFail = verifyToken(signedToken, keyPair.publicKeyDer);
ASSERT(!verifyExpectFail);
}
printf("%d runs OK\n", numIters);

View File

@ -56,6 +56,7 @@ set(FLOW_SRCS
Platform.h
Profiler.actor.cpp
Profiler.h
ScopeExit.h
SendBufferIterator.h
SignalSafeUnwind.cpp
SignalSafeUnwind.h

View File

@ -21,6 +21,7 @@
#include "flow/Arena.h"
#include "flow/IRandom.h"
#include "flow/MkCert.h"
#include "flow/ScopeExit.h"
#include <limits>
#include <memory>
@ -34,16 +35,6 @@
namespace {
template <typename Func>
class ExitGuard {
std::decay_t<Func> fn;
public:
ExitGuard(Func&& fn) : fn(std::forward<Func>(fn)) {}
~ExitGuard() { fn(); }
};
[[noreturn]] void traceAndThrow(const char* condition, const char* file, int line) {
auto te = TraceEvent(SevWarnAlways, "ErrorTLSKeyOrCertGen");
te.suppressFor(60).detail("File", file).detail("Line", line).detail("Condition", condition);
@ -118,7 +109,7 @@ void printPrivateKey(FILE* out, StringRef privateKeyPem) {
auto key = readPrivateKeyPem(privateKeyPem);
auto bio = ::BIO_new_fp(out, BIO_NOCLOSE);
OSSL_ASSERT(bio);
auto bioGuard = ExitGuard([bio]() { ::BIO_free(bio); });
auto bioGuard = ScopeExit([bio]() { ::BIO_free(bio); });
OSSL_ASSERT(0 < ::EVP_PKEY_print_private(bio, key.get(), 0, nullptr));
}
@ -127,17 +118,17 @@ std::shared_ptr<EVP_PKEY> makeEllipticCurveKeyPairNative() {
{
auto pctx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
OSSL_ASSERT(pctx);
auto ctxGuard = ExitGuard([pctx]() { ::EVP_PKEY_CTX_free(pctx); });
auto ctxGuard = ScopeExit([pctx]() { ::EVP_PKEY_CTX_free(pctx); });
OSSL_ASSERT(0 < ::EVP_PKEY_paramgen_init(pctx));
OSSL_ASSERT(0 < ::EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1));
OSSL_ASSERT(0 < ::EVP_PKEY_paramgen(pctx, &params));
OSSL_ASSERT(params);
}
auto paramsGuard = ExitGuard([params]() { ::EVP_PKEY_free(params); });
auto paramsGuard = ScopeExit([params]() { ::EVP_PKEY_free(params); });
// keygen
auto kctx = ::EVP_PKEY_CTX_new(params, nullptr);
OSSL_ASSERT(kctx);
auto kctxGuard = ExitGuard([kctx]() { ::EVP_PKEY_CTX_free(kctx); });
auto kctxGuard = ScopeExit([kctx]() { ::EVP_PKEY_CTX_free(kctx); });
auto key = std::add_pointer_t<EVP_PKEY>();
OSSL_ASSERT(0 < ::EVP_PKEY_keygen_init(kctx));
OSSL_ASSERT(0 < ::EVP_PKEY_keygen(kctx, &key));
@ -149,7 +140,7 @@ std::shared_ptr<X509> readX509CertPem(StringRef x509CertPem) {
ASSERT(!x509CertPem.empty());
auto bio_mem = ::BIO_new_mem_buf(x509CertPem.begin(), x509CertPem.size());
OSSL_ASSERT(bio_mem);
auto bioGuard = ExitGuard([bio_mem]() { ::BIO_free(bio_mem); });
auto bioGuard = ScopeExit([bio_mem]() { ::BIO_free(bio_mem); });
auto ret = ::PEM_read_bio_X509(bio_mem, nullptr, nullptr, nullptr);
OSSL_ASSERT(ret);
return std::shared_ptr<X509>(ret, &::X509_free);
@ -159,7 +150,7 @@ std::shared_ptr<EVP_PKEY> readPrivateKeyPem(StringRef privateKeyPem) {
ASSERT(!privateKeyPem.empty());
auto bio_mem = ::BIO_new_mem_buf(privateKeyPem.begin(), privateKeyPem.size());
OSSL_ASSERT(bio_mem);
auto bioGuard = ExitGuard([bio_mem]() { ::BIO_free(bio_mem); });
auto bioGuard = ScopeExit([bio_mem]() { ::BIO_free(bio_mem); });
auto ret = ::PEM_read_bio_PrivateKey(bio_mem, nullptr, nullptr, nullptr);
OSSL_ASSERT(ret);
return std::shared_ptr<EVP_PKEY>(ret, &::EVP_PKEY_free);
@ -168,7 +159,7 @@ std::shared_ptr<EVP_PKEY> readPrivateKeyPem(StringRef privateKeyPem) {
StringRef writeX509CertPem(Arena& arena, const std::shared_ptr<X509>& nativeCert) {
auto mem = ::BIO_new(::BIO_s_secmem());
OSSL_ASSERT(mem);
auto memGuard = ExitGuard([mem]() { ::BIO_free(mem); });
auto memGuard = ScopeExit([mem]() { ::BIO_free(mem); });
OSSL_ASSERT(::PEM_write_bio_X509(mem, nativeCert.get()));
auto bioBuf = std::add_pointer_t<char>{};
auto const len = ::BIO_get_mem_data(mem, &bioBuf);
@ -181,7 +172,7 @@ StringRef writeX509CertPem(Arena& arena, const std::shared_ptr<X509>& nativeCert
StringRef writePrivateKeyPem(Arena& arena, const std::shared_ptr<EVP_PKEY>& nativePrivateKey) {
auto mem = ::BIO_new(::BIO_s_secmem());
OSSL_ASSERT(mem);
auto memGuard = ExitGuard([mem]() { ::BIO_free(mem); });
auto memGuard = ScopeExit([mem]() { ::BIO_free(mem); });
OSSL_ASSERT(::PEM_write_bio_PrivateKey(mem, nativePrivateKey.get(), nullptr, nullptr, 0, 0, nullptr));
auto bioBuf = std::add_pointer_t<char>{};
auto const len = ::BIO_get_mem_data(mem, &bioBuf);
@ -223,7 +214,7 @@ CertAndKeyNative makeCertNative(CertSpecRef spec, CertAndKeyNative issuer) {
auto nativeKeyPair = makeEllipticCurveKeyPairNative();
auto newX = ::X509_new();
OSSL_ASSERT(newX);
auto x509Guard = ExitGuard([&newX]() {
auto x509Guard = ScopeExit([&newX]() {
if (newX)
::X509_free(newX);
});
@ -262,7 +253,7 @@ CertAndKeyNative makeCertNative(CertSpecRef spec, CertAndKeyNative issuer) {
auto extValue = entry.bytes.toString();
auto ext = ::X509V3_EXT_conf(nullptr, &ctx, extName.c_str(), extValue.c_str());
OSSL_ASSERT(ext);
auto extGuard = ExitGuard([ext]() { ::X509_EXTENSION_free(ext); });
auto extGuard = ScopeExit([ext]() { ::X509_EXTENSION_free(ext); });
OSSL_ASSERT(::X509_add_ext(x, ext, -1));
}
OSSL_ASSERT(::X509_sign(x, (isSelfSigned ? nativeKeyPair.get() : issuer.privateKey.get()), ::EVP_sha256()));

View File

@ -26,9 +26,10 @@
#include "flow/Arena.h"
#include "flow/Error.h"
#include "flow/MkCert.h"
#include "flow/SimpleOpt.h"
#include "flow/Platform.h"
#include "flow/network.h"
#include "flow/Platform.h"
#include "flow/ScopeExit.h"
#include "flow/SimpleOpt.h"
#include "flow/TLSConfig.actor.h"
#include "flow/Trace.h"
@ -196,8 +197,16 @@ int main(int argc, char** argv) {
platformInit();
Error::init();
g_network = newNet2(TLSConfig());
TraceEvent::setNetworkThread();
openTraceFile(NetworkAddress(), 10 << 20, 10 << 20, ".", "mkcert");
auto thread = std::thread([]() {
TraceEvent::setNetworkThread();
g_network->run();
});
auto cleanUpGuard = ScopeExit([&thread]() {
flushTraceFileVoid();
g_network->stop();
thread.join();
});
serverCertFile = abspath(serverCertFile);
serverKeyFile = abspath(serverKeyFile);
@ -223,12 +232,23 @@ int main(int argc, char** argv) {
clientCaFile);
using FileStream = std::ofstream;
auto checkStream = [](FileStream& fs, std::string_view filename) {
if (!fs) {
throw std::runtime_error(fmt::format("Cannot open '{}' for writing", filename));
}
};
auto ofsServerCert = FileStream(serverCertFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsServerCert, serverCertFile);
auto ofsServerKey = FileStream(serverKeyFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsServerKey, serverKeyFile);
auto ofsServerCa = FileStream(serverCaFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsServerCa, serverCaFile);
auto ofsClientCert = FileStream(clientCertFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsClientCert, clientCertFile);
auto ofsClientKey = FileStream(clientKeyFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsClientKey, clientKeyFile);
auto ofsClientCa = FileStream(clientCaFile, std::ofstream::out | std::ofstream::trunc);
checkStream(ofsClientCa, clientCaFile);
if (serverChainLen) {
auto arena = Arena();
auto specs = mkcert::makeCertChainSpec(arena, std::abs(serverChainLen), mkcert::ESide::Server);
@ -269,10 +289,7 @@ int main(int argc, char** argv) {
ofsClientCert.close();
ofsClientKey.close();
ofsClientCa.close();
auto thread = std::thread([]() { g_network->run(); });
flushTraceFileVoid();
g_network->stop();
thread.join();
fmt::print("OK\n");
return FDB_EXIT_SUCCESS;
} catch (const Error& e) {
fmt::print(stderr, "ERROR: {}\n", e.name());

36
flow/ScopeExit.h Normal file
View File

@ -0,0 +1,36 @@
/*
* ScopeExit.h
*
* 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.
*/
#ifndef FLOW_SCOPE_EXIT_H
#define FLOW_SCOPE_EXIT_H
#pragma once
// Execute lambda as this object goes out of scope
template <typename Func>
class ScopeExit {
std::decay_t<Func> fn;
public:
ScopeExit(Func&& fn) : fn(std::forward<Func>(fn)) {}
~ScopeExit() { fn(); }
};
#endif /*FLOW_SCOPE_EXIT_H*/