From 86918d8730ede4a3f924aae184047cb2bc00c282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20G=C3=BCndling?= Date: Thu, 28 Dec 2023 15:09:33 +0100 Subject: [PATCH] wip --- .pkg.lock | 4 ++-- exe/server.cc | 50 ++++++++++++++++++++++++++++--------------- include/adr/crypto.h | 13 +++++++---- include/adr/request.h | 12 +++++++++-- src/crypto.cc | 48 ++++++++++++++++++++++++++++------------- src/request.cc | 25 ++++++++++++++++------ test/crypto_test.cc | 31 ++++++++++++++++++--------- 7 files changed, 126 insertions(+), 57 deletions(-) diff --git a/.pkg.lock b/.pkg.lock index 22f238b..0f9cfed 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,4 +1,4 @@ -884132695651941781 +13283054806179763047 FTXUI dd6a5d371fd7a3e2937bb579955003c54b727233 utf8proc 1cb28a66ca79a0845e99433fd1056257456cef8b cista e5a58c750d5432cc4ea2855bbfc42bcea591d0f7 @@ -30,4 +30,4 @@ unordered_dense c11595a7743d20622637584bddf77243d72ae152 rtree.c 498f993c38e80f21d55ab72f14675b72fca991cc tg 9d3db9c9ccbde60449e25338550b14865ea1b98e libressl 390253a44ceef00eb620c38606588414326e9f23 -uWebSockets 22e346ec2087089cfe46a59d3fc4a328b63561e3 +uWebSockets 6d73aac9097121a3c42b862d6c3019f560764dd7 diff --git a/exe/server.cc b/exe/server.cc index 87dd1dc..a2b3854 100644 --- a/exe/server.cc +++ b/exe/server.cc @@ -90,7 +90,7 @@ int main(int ac, char** av) { auto ctx = adr::guess_context{cache}; auto lang_list = std::basic_string{adr::kDefaultLang}; - auto c = adr::crypto{iv, key}; + auto c = adr::crypto{key}; auto h = adr::http{}; ctx.resize(*t); @@ -108,23 +108,39 @@ int main(int ac, char** av) { server.post("/", [&](uWS::HttpResponse* res, uWS::HttpRequest* req) { + auto const iv_header = req->getHeader("x-iv"); auto aborted = std::make_shared(false); - res->onData([&c, &h, res, req_body = std::make_shared(), - aborted](std::string_view chunk, bool const fin) mutable { - (*req_body) += chunk; - if (fin && !*aborted) { - adr::decode(c, - std::span{reinterpret_cast( - req_body->data()), - req_body->size()}, - h); - std::cout << h << "\n"; - auto const response = adr::encode(c, {.body_ = "Hello World!"}); - res->end({reinterpret_cast(response.data()), - response.size()}); - } - }); - res->onAborted([aborted]() { *aborted = true; }); + try { + res->onData([&c, &h, res, req_body = std::make_shared(), + iv = adr::crypto::decode_base64(iv_header, 16), aborted]( + std::string_view chunk, bool const fin) mutable { + try { + (*req_body) += chunk; + if (fin && !*aborted) { + adr::decode(c, iv, + std::span{reinterpret_cast( + req_body->data()), + req_body->size()}, + h); + std::cout << h << "\n"; + auto const response = adr::encode(c, {.body_ = "Hello World!"}); + res->writeHeader("X-Iv", response.iv_); + res->end( + {reinterpret_cast(response.encrypted_.data()), + response.encrypted_.size()}); + } + } catch (...) { + *aborted = true; + res->writeStatus("500 Internal Server Error"); + res->endWithoutBody(std::nullopt, true); + } + }); + res->onAborted([aborted]() { *aborted = true; }); + } catch (...) { + *aborted = true; + res->writeStatus("500 Internal Server Error"); + res->endWithoutBody(std::nullopt, true); + } }); server.listen(port, [&](auto* listen_socket) { if (listen_socket) { diff --git a/include/adr/crypto.h b/include/adr/crypto.h index be070b7..c1e9646 100644 --- a/include/adr/crypto.h +++ b/include/adr/crypto.h @@ -7,6 +7,7 @@ #include #include "openssl/evp.h" +#include "openssl/rand.h" namespace adr { @@ -21,18 +22,22 @@ struct crypto { static std::vector read_base64_file( std::filesystem::path const&, std::size_t); + static std::vector decode_base64(std::string_view, std::size_t); + static std::string encode_base64(std::span); + static std::vector generate_random_bytes(std::size_t n); - crypto(std::vector const& iv, - std::vector const& key); + crypto(std::vector const& key); crypto(crypto const&) = delete; crypto(crypto&&) = delete; crypto& operator=(crypto const&) = delete; crypto& operator=(crypto&&) = delete; ~crypto(); - std::span crypt(std::span, mode); + std::span crypt(std::span iv, + std::span input, + mode); - std::vector const &iv_, &key_; + std::vector const& key_; std::vector buf_; EVP_CIPHER_CTX* ctx_{nullptr}; }; diff --git a/include/adr/request.h b/include/adr/request.h index 9478c4a..991a3cb 100644 --- a/include/adr/request.h +++ b/include/adr/request.h @@ -16,8 +16,16 @@ struct http { std::string body_; }; -void decode(crypto& c, std::span encoded, http& req); +struct encoded { + std::string iv_; + std::span encrypted_; +}; + +void decode(crypto& c, + std::span iv, + std::span encoded, + http& req); -std::span encode(crypto& c, http const& res); +encoded encode(crypto& c, http const& res); } // namespace adr \ No newline at end of file diff --git a/src/crypto.cc b/src/crypto.cc index 51ef9b5..c7826d1 100644 --- a/src/crypto.cc +++ b/src/crypto.cc @@ -8,11 +8,8 @@ namespace adr { -crypto::crypto(std::vector const& iv, - std::vector const& key) - : iv_{iv}, key_{key}, ctx_{EVP_CIPHER_CTX_new()} { - utl::verify(iv.size() == 16U, - "crypto: IV should be 16 bytes (128 bit), size={}", iv.size()); +crypto::crypto(std::vector const& key) + : key_{key}, ctx_{EVP_CIPHER_CTX_new()} { utl::verify(key.size() == 32U, "crypto: key should be 32 bytes (256 bit), size={}", key.size()); utl::verify(ctx_ != nullptr, "decrypt_ctx: EVP_CIPHER_CTX_new -> nullptr"); @@ -20,16 +17,15 @@ crypto::crypto(std::vector const& iv, crypto::~crypto() { EVP_CIPHER_CTX_free(ctx_); } -std::vector crypto::read_base64_file( - std::filesystem::path const& path, std::size_t const expected_size) { - auto const content = utl::read_file(path.generic_string().c_str()); - utl::verify(content.has_value(), - "read_base64_file: count not read file \"{}\"", path); +std::vector crypto::decode_base64( + std::string_view const s, std::size_t const expected_size) { + if (s.empty()) { + return {}; + } - auto out = std::vector(3U * content->size() / 4U + 1U); + auto out = std::vector(3U * s.size() / 4U + 1U); auto const length = EVP_DecodeBlock( - out.data(), reinterpret_cast(content->data()), - content->size()); + out.data(), reinterpret_cast(s.data()), s.size()); utl::verify(length + 1U == out.size(), "read_base64_file: expected {} bytes, got {}", out.size(), length); @@ -40,10 +36,32 @@ std::vector crypto::read_base64_file( return out; } -std::span crypto::crypt(std::span input, +std::vector crypto::read_base64_file( + std::filesystem::path const& path, std::size_t const expected_size) { + auto const content = utl::read_file(path.generic_string().c_str()); + utl::verify(content.has_value(), + "read_base64_file: count not read file \"{}\"", path); + return decode_base64(*content, expected_size); +} + +std::string crypto::encode_base64(std::span bytes) { + auto out = std::string{}; + out.resize(4 * ((bytes.size() + 2) / 3)); + auto const length = EVP_EncodeBlock( + reinterpret_cast(out.data()), bytes.data(), bytes.size()); + utl::verify(length == out.size(), "encode_base64: expected {} bytes, got {}", + out.size(), length); + return out; +} + +std::span crypto::crypt(std::span iv, + std::span input, mode const m) { + utl::verify(iv.size() == 16U, + "crypto: IV should be 16 bytes (128 bit), size={}", iv.size()); + EVP_CIPHER_CTX_reset(ctx_); - EVP_CipherInit(ctx_, EVP_aes_256_cbc(), key_.data(), iv_.data(), m); + EVP_CipherInit(ctx_, EVP_aes_256_cbc(), key_.data(), iv.data(), m); auto out_size = 0; buf_.resize(input.size() + AES_BLOCK_SIZE); diff --git a/src/request.cc b/src/request.cc index 73c09e5..4030ae6 100644 --- a/src/request.cc +++ b/src/request.cc @@ -1,5 +1,7 @@ #include "adr/request.h" +#include "openssl/rand.h" + #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" @@ -10,8 +12,11 @@ namespace json = rapidjson; namespace adr { -void decode(crypto& c, std::span encoded, http& req) { - auto const decrypted = c.crypt(encoded, adr::crypto::kDecrypt); +void decode(crypto& c, + std::span iv, + std::span encoded, + http& req) { + auto const decrypted = c.crypt(iv, encoded, adr::crypto::kDecrypt); auto const input = std::string_view{ reinterpret_cast(decrypted.data()), decrypted.size()}; @@ -38,7 +43,7 @@ void decode(crypto& c, std::span encoded, http& req) { } } -std::span encode(crypto& c, http const& res) { +encoded encode(crypto& c, http const& res) { auto buf = json::StringBuffer{}; auto w = json::Writer{buf}; @@ -53,10 +58,16 @@ std::span encode(crypto& c, http const& res) { } w.EndObject(); - return c.crypt( - std::span{reinterpret_cast(buf.GetString()), - buf.GetLength()}, - adr::crypto::kEncrypt); + auto iv = std::vector(16); + RAND_bytes(iv.data(), iv.size()); + + return encoded{ + .iv_ = crypto::encode_base64(iv), + .encrypted_ = c.crypt( + iv, + std::span{reinterpret_cast(buf.GetString()), + buf.GetLength()}, + adr::crypto::kEncrypt)}; } } // namespace adr \ No newline at end of file diff --git a/test/crypto_test.cc b/test/crypto_test.cc index a4811a9..eee6a9f 100644 --- a/test/crypto_test.cc +++ b/test/crypto_test.cc @@ -14,21 +14,21 @@ TEST(crypto, crypto) { auto const key = std::vector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - auto c = adr::crypto{iv, key}; + auto c = adr::crypto{key}; auto const input = std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; fmt::print("input: {}, size={}\n", input, input.size()); for (auto i = 0U; i != 10; ++i) { - auto const encrypted = c.crypt(std::span{input}, adr::crypto::kEncrypt); + auto const encrypted = c.crypt(iv, std::span{input}, adr::crypto::kEncrypt); auto const encrypted_copy = std::vector{encrypted.begin(), encrypted.end()}; fmt::print("encrypted: {}, size={}\n", encrypted_copy, encrypted_copy.size()); auto const decrypted = - c.crypt(std::span{encrypted_copy}, adr::crypto::kDecrypt); + c.crypt(iv, std::span{encrypted_copy}, adr::crypto::kDecrypt); auto const decrypted_copy = std::vector{decrypted.begin(), decrypted.end()}; fmt::print("decrypted: {}, size={}\n", decrypted_copy, @@ -53,17 +53,17 @@ TEST(crypto, read_key_iv) { std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; fmt::print("input: {}, size={}\n", input, input.size()); - auto c = adr::crypto{iv, key}; + auto c = adr::crypto{key}; for (auto i = 0U; i != 10; ++i) { - auto const encrypted = c.crypt(std::span{input}, adr::crypto::kEncrypt); + auto const encrypted = c.crypt(iv, std::span{input}, adr::crypto::kEncrypt); auto const encrypted_copy = std::vector{encrypted.begin(), encrypted.end()}; fmt::print("encrypted: {}, size={}\n", encrypted_copy, encrypted_copy.size()); auto const decrypted = - c.crypt(std::span{encrypted_copy}, adr::crypto::kDecrypt); + c.crypt(iv, std::span{encrypted_copy}, adr::crypto::kDecrypt); auto const decrypted_copy = std::vector{decrypted.begin(), decrypted.end()}; fmt::print("decrypted: {}, size={}\n", decrypted_copy, @@ -76,15 +76,26 @@ TEST(crypto, read_key_iv) { TEST(crypto, request_response) { auto const key = adr::crypto::read_base64_file("test/key.txt", 32); auto const iv = adr::crypto::read_base64_file("test/iv.txt", 16); - auto c = adr::crypto{iv, key}; + auto c = adr::crypto{key}; auto const h = adr::http{.path_ = "abc", .body_ = "def"}; auto const encoded = adr::encode(c, h); - auto const encoded_copy = - std::vector{encoded.begin(), encoded.end()}; + auto const encoded_copy = std::vector{ + encoded.encrypted_.begin(), encoded.encrypted_.end()}; auto decoded = adr::http{}; - adr::decode(c, encoded_copy, decoded); + adr::decode(c, adr::crypto::decode_base64(encoded.iv_, 16), encoded_copy, + decoded); EXPECT_EQ(h, decoded); +} + +TEST(crypto, base64_roundtrip) { + auto const str = std::string_view{"1234567890abcdef"}; + auto const data = std::vector{str.begin(), str.end()}; + + auto const b64_encoded = adr::crypto::encode_base64(data); + auto const decoded = adr::crypto::decode_base64(b64_encoded, 16); + + EXPECT_EQ(data, decoded); } \ No newline at end of file