From bea8a84a4fd5d353adc988f6accafadd79d3dadf Mon Sep 17 00:00:00 2001 From: hashlag Date: Sun, 5 Jul 2026 21:34:36 +0300 Subject: [PATCH 01/21] Add ECB-mode encryptor draft implementation. --- Chaos/Cipher/Block/Mode/Ecb.hpp | 113 ++++++++ ChaosTests/CMakeLists.txt | 1 + ChaosTests/Cipher/EcbModeTests.cpp | 439 +++++++++++++++++++++++++++++ 3 files changed, 553 insertions(+) create mode 100644 Chaos/Cipher/Block/Mode/Ecb.hpp create mode 100644 ChaosTests/Cipher/EcbModeTests.cpp diff --git a/Chaos/Cipher/Block/Mode/Ecb.hpp b/Chaos/Cipher/Block/Mode/Ecb.hpp new file mode 100644 index 0000000..a5b2a45 --- /dev/null +++ b/Chaos/Cipher/Block/Mode/Ecb.hpp @@ -0,0 +1,113 @@ +#ifndef CHAOS_CIPHER_BLOCK_MODE_ECB_HPP +#define CHAOS_CIPHER_BLOCK_MODE_ECB_HPP + +#include +#include + +#include "Service/ChaosException.hpp" +#include "Service/SeArray.hpp" + +namespace Chaos::Cipher::Block::Mode +{ + +template +class EcbMode +{ +public: + class Encryptor + { + public: + Encryptor(const typename CipherT::Key & key) + : Encryptor_(key) + , BlockBytesPacked_(0) + { } + + static constexpr uint64_t PredictMaxUpdateOutput(uint64_t in) + { + return in + CipherT::BlockSize - 1; + } + + static constexpr uint64_t PredictMaxFinishOutput() + { + return CipherT::BlockSize; + } + + template + uint64_t Update(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + return UpdateImpl(outBegin, outEnd, inBegin, inEnd); + } + + template + uint64_t Finish(OutputIt outBegin, OutputIt outEnd) + { + PadderT::Pad(Block_.Begin() + BlockBytesPacked_, Block_.End()); + + Encryptor_.EncryptBlock(EncryptedBlock_.Begin(), EncryptedBlock_.End(), + Block_.Begin(), Block_.End()); + EnsureCopy(outBegin, outEnd, EncryptedBlock_.Begin(), EncryptedBlock_.End()); + + return CipherT::BlockSize; + } + + private: + typename CipherT::Encryptor Encryptor_; + + uint64_t BlockBytesPacked_; + Service::SeArray Block_; + + Service::SeArray EncryptedBlock_; + + template + static OutputIt EnsureCopy(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + OutputIt out = outBegin; + InputIt in = inBegin; + + for (; out != outEnd && in != inEnd; ++out, ++in) + { + *out = *in; + } + + if (out == outEnd && in != inEnd) + { + throw Service::ChaosException("EcbMode<>::Encryptor: insufficient output " + "buffer size"); + } + + return out; + } + + template + uint64_t UpdateImpl(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + uint64_t written = 0; + OutputIt out = outBegin; + + for (InputIt in = inBegin; in != inEnd; ++in) + { + Block_[BlockBytesPacked_++] = *in; + + if (BlockBytesPacked_ == Block_.Size()) + { + BlockBytesPacked_ = 0; + + Encryptor_.EncryptBlock(EncryptedBlock_.Begin(), EncryptedBlock_.End(), + Block_.Begin(), Block_.End()); + out = EnsureCopy(out, outEnd, EncryptedBlock_.Begin(), EncryptedBlock_.End()); + + written += CipherT::BlockSize; + } + } + + return written; + } + }; +}; + +} // namespace Chaos::Cipher::Block::Mode + +#endif // CHAOS_CIPHER_BLOCK_MODE_ECB_HPP diff --git a/ChaosTests/CMakeLists.txt b/ChaosTests/CMakeLists.txt index 2345b8d..0e401b4 100644 --- a/ChaosTests/CMakeLists.txt +++ b/ChaosTests/CMakeLists.txt @@ -18,6 +18,7 @@ set(ChaosTests_SOURCE Hash/Md4HasherTests.cpp Cipher/Arc4GenTests.cpp Cipher/Arc4CryptTests.cpp Cipher/DesCryptTests.cpp + Cipher/EcbModeTests.cpp Padding/PadderPkcs7Tests.cpp Padding/PadderIso7816Tests.cpp Service/SeArrayTests.cpp diff --git a/ChaosTests/Cipher/EcbModeTests.cpp b/ChaosTests/Cipher/EcbModeTests.cpp new file mode 100644 index 0000000..d4a28f0 --- /dev/null +++ b/ChaosTests/Cipher/EcbModeTests.cpp @@ -0,0 +1,439 @@ +#include + +#include +#include +#include +#include + +#include "Cipher/Block/Mode/Ecb.hpp" +#include "Cipher/Block/Des/DesCrypt.hpp" +#include "Padding/PadderPkcs7.hpp" +#include "Service/ChaosException.hpp" + +using namespace Chaos::Cipher::Block::Mode; +using namespace Chaos::Cipher::Block; +using namespace Chaos::Padding; + +TEST(EcbModeTests, EncryptTest) +{ + struct Helper + { + std::vector operator()(const std::vector & data, + const std::vector & key) + { + Des::DesCrypt::Key desKey(key.begin(), key.end()); + EcbMode::Encryptor enc(desKey); + + std::vector out; + out.resize(enc.PredictMaxUpdateOutput(data.size()) + + enc.PredictMaxFinishOutput()); + + uint64_t written = enc.Update(out.begin(), out.end(), data.begin(), data.end()); + written += enc.Finish(out.begin() + written, out.end()); + + out.resize(written); + return out; + } + }; + + Helper ecbEnc; + + { + std::vector data = {}; + std::vector key = { 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb }; + + std::vector expected = { 0xef, 0xe2, 0x4d, 0xe9, 0x9d, 0xe7, 0x9b, 0xbf }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + const char * str = "smoke and mirrors"; + std::vector data(str, str + strlen(str)); + std::vector key = { 0x0c, 0xfd, 0x01, 0xa3, 0x60, 0xd0, 0x15, 0xa6 }; + + std::vector expected = { 0x84, 0x39, 0xcd, 0xe5, 0x1f, 0x3d, + 0x2a, 0x11, 0x63, 0xc3, 0x56, 0x28, + 0xf5, 0x89, 0xdb, 0xc4, 0xa6, 0xca, + 0x52, 0xb3, 0xa8, 0xce, 0x64, 0x2d }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x51, 0x89, 0x9f, 0x0c, 0x32, 0x9b, 0x1d }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + std::vector expected = { 0x64, 0x32, 0x13, 0xe7, 0x31, 0x06, 0xc6, 0x6f }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x32, 0x00, 0x93, 0x61, 0xc6, 0x9a, 0x25, 0x25, 0x2e }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + std::vector expected = { 0xee, 0x5b, 0x16, 0x33, 0xad, 0x2d, 0x32, 0x42, + 0xdc, 0x0d, 0x9f, 0x87, 0x55, 0x5b, 0xb1, 0x53 }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x4d, 0x0d, 0xfb, 0x59, 0xe6, 0xc4, 0xf2, 0xe7, + 0xab, 0x46, 0x4f, 0x59, 0x48, 0x11, 0x1b, 0x4f }; + std::vector key = { 0x44, 0x0a, 0xb1, 0x6d, 0xe0, 0x67, 0x28, 0x9d }; + + std::vector expected = { 0x55, 0xc1, 0x6f, 0xbb, 0xb0, 0xf3, 0xd3, 0xa6, 0xb7, 0xe6, 0x12, 0xb4, + 0x49, 0x86, 0x64, 0x07, 0x82, 0xbb, 0x45, 0x46, 0x70, 0xae, 0x64, 0x5f }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x8a, 0xe3, 0x85, 0xe0, 0x59, 0xe7, 0xee, 0xce, 0xc0, 0xbf, 0x50, 0x53, + 0x95, 0xbf, 0x4b, 0xdd, 0x3b, 0x02, 0x2f, 0xcb, 0xd0, 0xd3, 0x62, 0x60 }; + std::vector key = { 0xe4, 0x60, 0xb9, 0xfa, 0x4b, 0x79, 0x6a, 0xf3 }; + + std::vector expected = { 0xf4, 0x46, 0x31, 0xc5, 0xe8, 0xff, 0xdc, 0xac, 0x6c, 0x0e, 0xe8, 0x0e, + 0x9f, 0xc2, 0xeb, 0xdf, 0xca, 0xac, 0xc3, 0xc0, 0xea, 0xba, 0x6e, 0xc2, + 0x33, 0x5d, 0x16, 0x39, 0x74, 0xd1, 0x1d, 0xe1 }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x96, 0x9f, 0x42, 0xd3, 0x47, 0x10, 0x4d, 0xb4, 0x89, 0x54, + 0x5c, 0xad, 0x0a, 0xb9, 0xac, 0x4e, 0xbf, 0x00, 0x1a, 0x47, + 0x3d, 0xd1, 0xe7, 0xb3, 0x70, 0xb7, 0x4e, 0xd9, 0x73, 0x7d, + 0xb3, 0x24 }; + std::vector key = { 0xe9, 0xeb, 0x1f, 0xfa, 0x39, 0x95, 0xf0, 0xb9 }; + + std::vector expected = { 0x53, 0x06, 0x73, 0x79, 0xb8, 0xd7, 0xbe, 0x5d, 0x6e, 0xa0, + 0x1f, 0x03, 0x46, 0x07, 0xd6, 0x48, 0x65, 0x4f, 0x9a, 0x8f, + 0x1a, 0xe4, 0xa7, 0x7f, 0x13, 0xc9, 0x06, 0x8c, 0x57, 0xbd, + 0x79, 0x5e, 0x3d, 0x21, 0x02, 0x13, 0x5f, 0xae, 0xc9, 0x45 }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0xde, 0xbf, 0x28, 0x16, 0x0d, 0xfe, 0x64, 0xbf, 0xc6, 0xfd, + 0x2d, 0x32, 0x45, 0xb0, 0xb7, 0x53, 0xde, 0x5a, 0x8e, 0x45, + 0x87, 0xb3, 0x94, 0x0b, 0xdb, 0x9d, 0x23, 0x33, 0xd7, 0x8e, + 0x63, 0x2d, 0x56 }; + std::vector key = { 0x3b, 0x4d, 0xdd, 0x0a, 0x28, 0xf5, 0x96, 0x36 }; + + std::vector expected = { 0xc2, 0x88, 0xe5, 0x27, 0xd1, 0xb4, 0xa5, 0xf9, 0xb6, 0x02, + 0x43, 0x3e, 0xae, 0x59, 0x43, 0xd4, 0xe5, 0x87, 0x20, 0xfe, + 0xd0, 0x34, 0x28, 0x6a, 0xef, 0xb1, 0x9e, 0x5a, 0xdf, 0xfb, + 0xf7, 0x94, 0xb4, 0x0c, 0xc2, 0x92, 0x4e, 0x73, 0xda, 0xca }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } + + { + std::vector data = { 0x7e, 0x2c, 0x68, 0x20, 0x22, 0xb1, 0xdd, 0x6d, 0x6e, 0x8b, 0x8b, + 0x30, 0x99, 0xaf, 0x79, 0x2f, 0x6c, 0x95, 0x11, 0x8d, 0xd4, 0x2f, + 0xcf, 0x1d, 0xe6, 0xa0, 0xc7, 0x73, 0x48, 0xb1, 0x65, 0xa9, 0xf6, + 0xc5, 0x1c, 0x42, 0x2d, 0xdd, 0xcf, 0xf5, 0xd4, 0xee, 0xa5, 0xa7, + 0x69, 0xf9, 0x27, 0x93, 0xce }; + std::vector key = { 0x3c, 0xd0, 0xab, 0xd3, 0xb4, 0xc3, 0xac, 0x53 }; + + std::vector expected = { 0xf7, 0x44, 0x18, 0x9b, 0x9d, 0x8d, 0xe1, 0x14, 0xcd, 0x0a, 0xdb, 0xbd, + 0x5b, 0xd1, 0x42, 0x6e, 0x17, 0x73, 0x60, 0x9e, 0x14, 0xad, 0xd2, 0x4c, + 0x33, 0x30, 0xdd, 0xb9, 0xf3, 0x46, 0xd0, 0x8d, 0xa6, 0x37, 0x49, 0xd7, + 0x90, 0x23, 0x05, 0xcc, 0x56, 0xde, 0x1f, 0xde, 0x39, 0x6c, 0x04, 0x97, + 0x1d, 0xaf, 0xb5, 0xff, 0xab, 0x47, 0xc8, 0x51 }; + + ASSERT_EQ(expected, ecbEnc(data, key)); + } +} + +TEST(EcbModeTests, EncryptManyUpdatesTest) +{ + struct Helper + { + Helper(const std::vector & key) + : Key_(key.begin(), key.end()) + , Enc_(Key_) + , Written_(0) + { } + + void Update(const std::vector & data) + { + Out_.resize(Out_.size() + Enc_.PredictMaxUpdateOutput(data.size())); + + Written_ += Enc_.Update(Out_.begin() + Written_, Out_.end(), + data.begin(), data.end()); + } + + std::vector Finish() + { + Out_.resize(Out_.size() + Enc_.PredictMaxFinishOutput()); + + Written_ += Enc_.Finish(Out_.begin() + Written_, Out_.end()); + Out_.resize(Written_); + + return Out_; + } + + Des::DesCrypt::Key Key_; + EcbMode::Encryptor Enc_; + + std::vector Out_; + uint64_t Written_; + }; + + { + std::vector key = { 0xab, 0x39, 0x20, 0xea, 0xaa, 0x95, 0x1c, 0x90 }; + + std::vector data1 = { 0x97, 0x65, 0xe2, 0xb1, 0xae, 0x3e, 0x55, 0xff, 0x1d }; + std::vector data2 = { 0xac, 0x8a, 0xa6, 0xac, 0xa5, 0x9a, 0xf9, 0xb6 }; + std::vector data3 = { 0x3c, 0xba, 0x95, 0x5e, 0x78, 0x29, 0x22, 0x7c, 0x12, 0x7e, 0x4c }; + + std::vector expected = { 0xf4, 0xde, 0x9a, 0xe4, 0x78, 0x8c, 0xf2, 0xe6, 0x75, 0x34, 0xe7, + 0x9f, 0xe8, 0x16, 0x09, 0x12, 0x55, 0x75, 0x5b, 0xe4, 0x21, 0x65, + 0x98, 0x0e, 0xc6, 0x7a, 0x62, 0xf6, 0x88, 0xcd, 0x3e, 0x83 }; + + Helper ecbEnc(key); + + ecbEnc.Update(data1); + ecbEnc.Update(data2); + ecbEnc.Update(data3); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } + + { + std::vector key = { 0xd5, 0xc1, 0x3a, 0xcc, 0x6f, 0x86, 0x1d, 0x4b }; + + std::vector data1 = { 0x43, 0x73, 0x1b, 0xde, 0xbd, 0xfe, 0x16, 0x58 }; + std::vector data2 = { 0x8c, 0x16, 0x13, 0x53, 0x22, 0x66, 0xb4, 0xf2 }; + std::vector data3 = { 0x25, 0xcf, 0xcd, 0xab, 0x7d, 0x29, 0x9a, 0xd7 }; + + std::vector expected = { 0x5f, 0x18, 0x61, 0x15, 0x72, 0x75, 0x73, 0xb3, 0x44, + 0xa5, 0xf0, 0x46, 0xa9, 0x4b, 0x7e, 0x5e, 0x5b, 0x88, + 0x46, 0x07, 0x5e, 0x33, 0x16, 0x01, 0x69, 0x13, 0x76, + 0x8b, 0x7f, 0xbc, 0xf2, 0x75 }; + + Helper ecbEnc(key); + + ecbEnc.Update(data1); + ecbEnc.Update(data2); + ecbEnc.Update(data3); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } + + { + std::vector key = { 0xc0, 0x98, 0x56, 0xb3, 0x27, 0xc9, 0x78, 0x89 }; + + std::vector data1 = { 0xb7 }; + std::vector data2 = { 0xb3 }; + std::vector data3 = { 0x95, 0x56, 0xc0, 0x9f, 0x74, 0xd0, 0x49, 0xb5 }; + std::vector data4 = { 0x15 }; + + std::vector expected = { 0xf9, 0x79, 0x6d, 0x46, 0xb6, 0x4a, 0xa8, 0xaa, 0x82, + 0xb9, 0xca, 0x65, 0xe9, 0x3c, 0xa8, 0xdc }; + + Helper ecbEnc(key); + + ecbEnc.Update(data1); + ecbEnc.Update(data2); + ecbEnc.Update(data3); + ecbEnc.Update(data4); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } + + { + std::vector key = { 0xcb, 0x22, 0x90, 0x27, 0x87, 0x42, 0xd1, 0x58 }; + + std::vector data1 = { 0xca, 0x40, 0xeb, 0x08, 0x75, 0x62, 0x41 }; + std::vector data2 = { 0x16, 0xa4, 0x12, 0x21, 0x3d, 0x5a, 0xf2, 0xb4, 0xa7, 0xb5, 0xa7 }; + std::vector data3 = { 0x59, 0x51, 0xd0, 0x41, 0x21, 0xf0, 0x2b, 0x76, + 0xd8, 0xe4, 0x60, 0xb5, 0xab, 0xd8, 0x25, 0x29 }; + std::vector data4 = { 0x94, 0x9f, 0xed }; + std::vector data5 = { 0x1a, 0xa1, 0x57, 0x35, 0x2c }; + + std::vector expected = { 0xfb, 0x9d, 0x21, 0x86, 0x9b, 0xf1, 0x79, 0xeb, 0x6c, + 0xcf, 0xcc, 0xe7, 0x55, 0x48, 0x05, 0x42, 0x37, 0x23, + 0xcc, 0xda, 0x82, 0x8f, 0xe7, 0x5a, 0x08, 0x85, 0x00, + 0x0b, 0x38, 0x5f, 0x54, 0x53, 0xf9, 0x40, 0x59, 0xc7, + 0xf3, 0x06, 0x10, 0x10, 0xed, 0xfc, 0x76, 0xff, 0xe6, + 0x55, 0xb0, 0xe1 }; + + Helper ecbEnc(key); + + ecbEnc.Update(data1); + ecbEnc.Update(data2); + ecbEnc.Update(data3); + ecbEnc.Update(data4); + ecbEnc.Update(data5); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } +} + +TEST(EcbModeTests, EncryptLongInputTest) +{ + struct Helper + { + Helper(const std::vector & key) + : Key_(key.begin(), key.end()) + , Enc_(Key_) + , Written_(0) + { } + + void Update(const std::vector & data) + { + Out_.resize(Out_.size() + Enc_.PredictMaxUpdateOutput(data.size())); + + Written_ += Enc_.Update(Out_.begin() + Written_, Out_.end(), + data.begin(), data.end()); + } + + void Update(std::vector::const_iterator begin, std::vector::const_iterator end) + { + Out_.resize(Out_.size() + Enc_.PredictMaxUpdateOutput(std::distance(begin, end))); + Written_ += Enc_.Update(Out_.begin() + Written_, Out_.end(), begin, end); + } + + std::vector Finish() + { + Out_.resize(Out_.size() + Enc_.PredictMaxFinishOutput()); + + Written_ += Enc_.Finish(Out_.begin() + Written_, Out_.end()); + Out_.resize(Written_); + + return Out_; + } + + Des::DesCrypt::Key Key_; + EcbMode::Encryptor Enc_; + + std::vector Out_; + uint64_t Written_; + }; + + { + std::vector key = { 0x27, 0x07, 0x7c, 0xc9, 0xd2, 0xbe, 0x76, 0x6c }; + std::vector data(2048, 0xab); + + std::vector expected; + + { + std::vector block = { 0x90, 0xf0, 0x72, 0xae, 0xcc, 0x98, 0x93, 0x8d }; + + for (int i = 0; i < 256; ++i) + { + expected.insert(expected.end(), block.begin(), block.end()); + } + + std::vector lastBlock = { 0xce, 0x7c, 0x12, 0x8e, 0x8c, 0xc0, 0x88, 0x02 }; + expected.insert(expected.end(), lastBlock.begin(), lastBlock.end()); + } + + Helper ecbEnc(key); + ecbEnc.Update(data); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } + + { + std::vector key = { 0xcc, 0xc5, 0x08, 0x98, 0xe9, 0x8c, 0xeb, 0x23 }; + std::vector data(4095, 0x7a); + + std::vector expected; + { + std::vector block = { 0xb7, 0x9f, 0x07, 0x2c, 0xe9, 0x54, 0x83, 0xc5 }; + + for (int i = 0; i < 2048; ++i) + { + expected.insert(expected.end(), block.begin(), block.end()); + } + + std::vector lastBlock = { 0x14, 0x59, 0x29, 0x72, 0x45, 0xb3, 0x03, 0xb3 }; + expected.insert(expected.end(), lastBlock.begin(), lastBlock.end()); + } + + Helper ecbEnc(key); + + ecbEnc.Update(data.begin(), data.begin() + 2340); + ecbEnc.Update(data.begin(), data.begin() + 1171); + ecbEnc.Update(data.begin(), data.begin() + 2925); + ecbEnc.Update(data.begin(), data.begin() + 1); + ecbEnc.Update(data.begin(), data.begin() + 4095); + ecbEnc.Update(data.begin(), data.begin() + 586); + ecbEnc.Update(data.begin(), data.begin() + 3510); + ecbEnc.Update(data.begin(), data.begin() + 1756); + + ASSERT_EQ(expected, ecbEnc.Finish()); + } +} + +TEST(EcbModeTests, EncryptInsufficientBufferTest) +{ + std::vector key = { 0xcc, 0xc5, 0x08, 0x98, 0xe9, 0x8c, 0xeb, 0x23 }; + Des::DesCrypt::Key desKey(key.begin(), key.end()); + + { + std::vector data(8, 0xaa); + std::vector out; + + EcbMode::Encryptor enc(desKey); + + ASSERT_THROW(enc.Update(out.begin(), out.end(), data.begin(), data.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data(8, 0xaa); + std::vector out(7, 0); + + EcbMode::Encryptor enc(desKey); + + ASSERT_THROW(enc.Update(out.begin(), out.end(), data.begin(), data.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data1(7, 0xaa); + std::vector data2(2, 0xbb); + + std::vector out1(7, 0); + std::vector out2(2, 0); + + EcbMode::Encryptor enc(desKey); + + enc.Update(out1.begin(), out1.end(), data1.begin(), data1.end()); + + ASSERT_THROW(enc.Update(out2.begin(), out2.end(), data2.begin(), data2.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data(8, 0xaa); + std::vector out(8, 0); + + EcbMode::Encryptor enc(desKey); + + uint64_t written = enc.Update(out.begin(), out.end(), data.begin(), data.end()); + + ASSERT_THROW(written += enc.Finish(out.begin() + written, out.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data(15, 0xaa); + std::vector out(15, 0); + + EcbMode::Encryptor enc(desKey); + + uint64_t written = enc.Update(out.begin(), out.end(), data.begin(), data.end()); + + ASSERT_THROW(written += enc.Finish(out.begin() + written, out.end()), + Chaos::Service::ChaosException); + } +} From 809d243d384c747450fa74866abe27825f39722a Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 02:31:12 +0300 Subject: [PATCH 02/21] Remove trailing whitespace. --- Chaos/Service/SeArray.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chaos/Service/SeArray.hpp b/Chaos/Service/SeArray.hpp index 959924f..77a7f50 100644 --- a/Chaos/Service/SeArray.hpp +++ b/Chaos/Service/SeArray.hpp @@ -43,7 +43,7 @@ public: return Storage_.data(); } - const T * Begin() const noexcept + const T * Begin() const noexcept { return Storage_.data(); } From ac8424b2b4233f149906ec611c4b639999494ea7 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 02:44:37 +0300 Subject: [PATCH 03/21] Make coverage optional for debug builds by introducing CHAOS_COVERAGE flag. --- .gitea/workflows/ChaosCi.yaml | 2 +- ChaosTests/CMakeLists.txt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index 7c33291..30b2826 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -20,7 +20,7 @@ jobs: - name: Configure and build tests [Debug] run: | mkdir build-debug - cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug + cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_COVERAGE=ON cmake --build build-debug/ -t ChaosTests - name: Configure and build benchmarks [Release] diff --git a/ChaosTests/CMakeLists.txt b/ChaosTests/CMakeLists.txt index 0e401b4..d1a20a7 100644 --- a/ChaosTests/CMakeLists.txt +++ b/ChaosTests/CMakeLists.txt @@ -32,7 +32,9 @@ target_include_directories(ChaosTests PRIVATE target_compile_options(ChaosTests PRIVATE -Wunused -Werror=unused) -if(CMAKE_BUILD_TYPE STREQUAL "Debug") +option(CHAOS_COVERAGE "Enable coverage" OFF) + +if(CHAOS_COVERAGE) target_compile_options(ChaosTests PRIVATE --coverage) target_link_options(ChaosTests PRIVATE --coverage) endif() From 8351bf2b7491373991aac37f9c5e441ccb5470c8 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 03:25:30 +0300 Subject: [PATCH 04/21] Add sanitized test run to CI. --- .gitea/workflows/ChaosCi.yaml | 13 +++++++++++-- ChaosTests/CMakeLists.txt | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index 30b2826..bfe6d8d 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -17,21 +17,30 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential cmake valgrind lcov - - name: Configure and build tests [Debug] + - name: Configure and build tests [Debug and Coverage] run: | mkdir build-debug cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_COVERAGE=ON cmake --build build-debug/ -t ChaosTests + - name: Configure and build tests [Debug, ASan and UBSan] + run: | + mkdir build-debug-asanubsan + cmake -S . -B build-debug-asanubsan/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_ASANUBSAN=ON + cmake --build build-debug-asanubsan/ -t ChaosTests + - name: Configure and build benchmarks [Release] run: | mkdir build-release cmake -S . -B build-release/ -D CMAKE_BUILD_TYPE=Release cmake --build build-release/ -t ChaosBenches - - name: Run tests + - name: Run tests under valgrind run: valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all ./build-debug/ChaosTests/ChaosTests + - name: Run tests under ASan and UBSan + run: ./build-debug-asanubsan/ChaosTests/ChaosTests + - name: Run benchmarks run: ./build-release/ChaosBenches/ChaosBenches diff --git a/ChaosTests/CMakeLists.txt b/ChaosTests/CMakeLists.txt index d1a20a7..95ff1cf 100644 --- a/ChaosTests/CMakeLists.txt +++ b/ChaosTests/CMakeLists.txt @@ -33,12 +33,18 @@ target_include_directories(ChaosTests PRIVATE target_compile_options(ChaosTests PRIVATE -Wunused -Werror=unused) option(CHAOS_COVERAGE "Enable coverage" OFF) +option(CHAOS_ASANUBSAN "Enable ASan and UBSan" OFF) if(CHAOS_COVERAGE) target_compile_options(ChaosTests PRIVATE --coverage) target_link_options(ChaosTests PRIVATE --coverage) endif() +if(CHAOS_ASANUBSAN) + target_compile_options(ChaosTests PRIVATE -fsanitize=address,undefined) + target_link_options(ChaosTests PRIVATE -fsanitize=address,undefined) +endif() + include(GoogleTest) gtest_discover_tests(ChaosTests) From b220221c8b19391a1cb43b1cd75155fc5d8c58ed Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 03:41:35 +0300 Subject: [PATCH 05/21] Add enable no-sanitize-recover for all UBSan checks. --- ChaosTests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChaosTests/CMakeLists.txt b/ChaosTests/CMakeLists.txt index 95ff1cf..b85d788 100644 --- a/ChaosTests/CMakeLists.txt +++ b/ChaosTests/CMakeLists.txt @@ -41,8 +41,8 @@ if(CHAOS_COVERAGE) endif() if(CHAOS_ASANUBSAN) - target_compile_options(ChaosTests PRIVATE -fsanitize=address,undefined) - target_link_options(ChaosTests PRIVATE -fsanitize=address,undefined) + target_compile_options(ChaosTests PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=all) + target_link_options(ChaosTests PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=all) endif() include(GoogleTest) From bdd63f560a437824b86bdaa2a9aa02cade624c01 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 03:48:04 +0300 Subject: [PATCH 06/21] CI: Extract benchmarking into a distinct job. --- .gitea/workflows/ChaosCi.yaml | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index bfe6d8d..c12502c 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -3,7 +3,7 @@ name: Chaos Ci on: [push, pull_request] jobs: - test-and-benchmark: + test: runs-on: ubuntu-latest steps: @@ -29,21 +29,12 @@ jobs: cmake -S . -B build-debug-asanubsan/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_ASANUBSAN=ON cmake --build build-debug-asanubsan/ -t ChaosTests - - name: Configure and build benchmarks [Release] - run: | - mkdir build-release - cmake -S . -B build-release/ -D CMAKE_BUILD_TYPE=Release - cmake --build build-release/ -t ChaosBenches - - name: Run tests under valgrind run: valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all ./build-debug/ChaosTests/ChaosTests - name: Run tests under ASan and UBSan run: ./build-debug-asanubsan/ChaosTests/ChaosTests - - name: Run benchmarks - run: ./build-release/ChaosBenches/ChaosBenches - - name: Process coverage data run: | lcov --exclude '*/usr/*' --exclude '*/_deps/*' --exclude '*/ChaosTests/*' -c -d build-debug/ -o lcovout @@ -57,3 +48,26 @@ jobs: if-no-files-found: error include-hidden-files: true retention-days: 10 + + benchmark: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake + + - name: Configure and build benchmarks [Release] + run: | + mkdir build-release + cmake -S . -B build-release/ -D CMAKE_BUILD_TYPE=Release + cmake --build build-release/ -t ChaosBenches + + - name: Run benchmarks + run: ./build-release/ChaosBenches/ChaosBenches From 00dc60eee682a36fa47317ededd447b74c9009f6 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 03:56:16 +0300 Subject: [PATCH 07/21] CI: Do not collect coverage under valgrind. --- .gitea/workflows/ChaosCi.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index c12502c..ca2585a 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -17,12 +17,18 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential cmake valgrind lcov - - name: Configure and build tests [Debug and Coverage] + - name: Configure and build tests [Debug] run: | mkdir build-debug - cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_COVERAGE=ON + cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug cmake --build build-debug/ -t ChaosTests + - name: Configure and build tests [Debug and Coverage] + run: | + mkdir build-debug-coverage + cmake -S . -B build-debug-coverage/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_COVERAGE=ON + cmake --build build-debug-coverage/ -t ChaosTests + - name: Configure and build tests [Debug, ASan and UBSan] run: | mkdir build-debug-asanubsan @@ -35,9 +41,12 @@ jobs: - name: Run tests under ASan and UBSan run: ./build-debug-asanubsan/ChaosTests/ChaosTests + - name: Run tests for coverage + run: ./build-debug-coverage/ChaosTests/ChaosTests + - name: Process coverage data run: | - lcov --exclude '*/usr/*' --exclude '*/_deps/*' --exclude '*/ChaosTests/*' -c -d build-debug/ -o lcovout + lcov --exclude '*/usr/*' --exclude '*/_deps/*' --exclude '*/ChaosTests/*' -c -d build-debug-coverage/ -o lcovout genhtml lcovout -o coverage-report/ - name: Upload coverage report From b2ea94c18b064a81788651f3797474dc2fc4cf1e Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 03:58:23 +0300 Subject: [PATCH 08/21] CI: Simplify steps' naming. --- .gitea/workflows/ChaosCi.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index ca2585a..d32fe7f 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -17,19 +17,19 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential cmake valgrind lcov - - name: Configure and build tests [Debug] + - name: Build tests run: | mkdir build-debug cmake -S . -B build-debug/ -D CMAKE_BUILD_TYPE=Debug cmake --build build-debug/ -t ChaosTests - - name: Configure and build tests [Debug and Coverage] + - name: Build tests with coverage run: | mkdir build-debug-coverage cmake -S . -B build-debug-coverage/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_COVERAGE=ON cmake --build build-debug-coverage/ -t ChaosTests - - name: Configure and build tests [Debug, ASan and UBSan] + - name: Build tests with ASan and UBSan run: | mkdir build-debug-asanubsan cmake -S . -B build-debug-asanubsan/ -D CMAKE_BUILD_TYPE=Debug -D CHAOS_ASANUBSAN=ON @@ -72,7 +72,7 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential cmake - - name: Configure and build benchmarks [Release] + - name: Build benchmarks run: | mkdir build-release cmake -S . -B build-release/ -D CMAKE_BUILD_TYPE=Release From 3c3ff4af154ea0319c60f6faee620fc77dec2a41 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 6 Jul 2026 04:09:50 +0300 Subject: [PATCH 09/21] CI: Set ASan and UBSan verbosity to 1. --- .gitea/workflows/ChaosCi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/ChaosCi.yaml b/.gitea/workflows/ChaosCi.yaml index d32fe7f..f5933c1 100644 --- a/.gitea/workflows/ChaosCi.yaml +++ b/.gitea/workflows/ChaosCi.yaml @@ -39,7 +39,7 @@ jobs: run: valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all ./build-debug/ChaosTests/ChaosTests - name: Run tests under ASan and UBSan - run: ./build-debug-asanubsan/ChaosTests/ChaosTests + run: ASAN_OPTIONS=verbosity=1 UBSAN_OPTIONS=verbosity=1 ./build-debug-asanubsan/ChaosTests/ChaosTests - name: Run tests for coverage run: ./build-debug-coverage/ChaosTests/ChaosTests From c0f2f15d08f12ceb0695ad8dd14e02da692f7e60 Mon Sep 17 00:00:00 2001 From: hashlag Date: Tue, 7 Jul 2026 00:06:06 +0300 Subject: [PATCH 10/21] Add .clangd. --- .clangd | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..2be4694 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: build/debug From a5b7b1d7e1e3eb4418f6c2f3c493ea7d74648b47 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:34:31 +0300 Subject: [PATCH 11/21] Set clangd's unused includes diagnostic to strict explicitly. --- .clangd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.clangd b/.clangd index 2be4694..c7e9d1b 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,5 @@ CompileFlags: CompilationDatabase: build/debug + +Diagnostics: + UnusedIncludes: Strict From bcf96061cd1175cb68ac22e8cbe6884ad0d3af09 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:36:50 +0300 Subject: [PATCH 12/21] Enable clangd's missing includes diagnostic. --- .clangd | 1 + 1 file changed, 1 insertion(+) diff --git a/.clangd b/.clangd index c7e9d1b..24dac01 100644 --- a/.clangd +++ b/.clangd @@ -3,3 +3,4 @@ CompileFlags: Diagnostics: UnusedIncludes: Strict + MissingIncludes: Strict From 34b19434a1fd3a6ccd1ecb0467907e14ce01208c Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:53:13 +0300 Subject: [PATCH 13/21] Fix missing includes. --- Chaos/Cipher/Arc4/Arc4Crypt.hpp | 3 +++ Chaos/Cipher/Arc4/Arc4Gen.hpp | 1 + Chaos/Cipher/Block/Des/DesCrypt.hpp | 3 +++ Chaos/Hash/Md4.hpp | 2 ++ Chaos/Hash/Md5.hpp | 2 ++ Chaos/Hash/Sha1.hpp | 4 ++++ Chaos/Service/ChaosException.hpp | 1 + Chaos/Service/SeArray.hpp | 1 + ChaosTests/Cipher/Arc4CryptTests.cpp | 2 ++ ChaosTests/Cipher/Arc4GenTests.cpp | 5 +++++ ChaosTests/Cipher/DesCryptTests.cpp | 5 +++++ ChaosTests/Hash/Md4HasherTests.cpp | 2 ++ ChaosTests/Hash/Md5HasherTests.cpp | 2 ++ ChaosTests/Hash/Sha1HasherTests.cpp | 2 ++ ChaosTests/Mac/HmacTests.cpp | 4 ++++ ChaosTests/Padding/PadderIso7816Tests.cpp | 1 + ChaosTests/Padding/PadderPkcs7Tests.cpp | 1 + ChaosTests/Service/SeArrayTests.cpp | 1 + 18 files changed, 42 insertions(+) diff --git a/Chaos/Cipher/Arc4/Arc4Crypt.hpp b/Chaos/Cipher/Arc4/Arc4Crypt.hpp index 88ce26b..f7da4ab 100644 --- a/Chaos/Cipher/Arc4/Arc4Crypt.hpp +++ b/Chaos/Cipher/Arc4/Arc4Crypt.hpp @@ -1,6 +1,9 @@ #ifndef CHAOS_CIPHER_ARC4_ARC4CRYPT_HPP #define CHAOS_CIPHER_ARC4_ARC4CRYPT_HPP +#include +#include + #include "Arc4Gen.hpp" #include "Service/SeArray.hpp" #include "Service/ChaosException.hpp" diff --git a/Chaos/Cipher/Arc4/Arc4Gen.hpp b/Chaos/Cipher/Arc4/Arc4Gen.hpp index af2e833..f3f54f8 100644 --- a/Chaos/Cipher/Arc4/Arc4Gen.hpp +++ b/Chaos/Cipher/Arc4/Arc4Gen.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "Service/ChaosException.hpp" diff --git a/Chaos/Cipher/Block/Des/DesCrypt.hpp b/Chaos/Cipher/Block/Des/DesCrypt.hpp index a9e50c5..f303c50 100644 --- a/Chaos/Cipher/Block/Des/DesCrypt.hpp +++ b/Chaos/Cipher/Block/Des/DesCrypt.hpp @@ -3,6 +3,9 @@ #include #include +#include +#include +#include #include "Service/ChaosException.hpp" #include "Service/SeArray.hpp" diff --git a/Chaos/Hash/Md4.hpp b/Chaos/Hash/Md4.hpp index e4c1268..5387cae 100644 --- a/Chaos/Hash/Md4.hpp +++ b/Chaos/Hash/Md4.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "Hash.hpp" #include "Hasher.hpp" diff --git a/Chaos/Hash/Md5.hpp b/Chaos/Hash/Md5.hpp index 9ea28ce..2ef7149 100644 --- a/Chaos/Hash/Md5.hpp +++ b/Chaos/Hash/Md5.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "Hash.hpp" #include "Hasher.hpp" diff --git a/Chaos/Hash/Sha1.hpp b/Chaos/Hash/Sha1.hpp index 1dd74a7..d8adf50 100644 --- a/Chaos/Hash/Sha1.hpp +++ b/Chaos/Hash/Sha1.hpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include "Hash.hpp" #include "Hasher.hpp" diff --git a/Chaos/Service/ChaosException.hpp b/Chaos/Service/ChaosException.hpp index 761c406..888dedc 100644 --- a/Chaos/Service/ChaosException.hpp +++ b/Chaos/Service/ChaosException.hpp @@ -2,6 +2,7 @@ #define CHAOS_SERVICE_CHAOSEXCEPTION_HPP #include +#include namespace Chaos::Service { diff --git a/Chaos/Service/SeArray.hpp b/Chaos/Service/SeArray.hpp index 77a7f50..986e04e 100644 --- a/Chaos/Service/SeArray.hpp +++ b/Chaos/Service/SeArray.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Chaos::Service { diff --git a/ChaosTests/Cipher/Arc4CryptTests.cpp b/ChaosTests/Cipher/Arc4CryptTests.cpp index 9375c38..200931f 100644 --- a/ChaosTests/Cipher/Arc4CryptTests.cpp +++ b/ChaosTests/Cipher/Arc4CryptTests.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include "Cipher/Arc4/Arc4Crypt.hpp" #include "Service/ChaosException.hpp" diff --git a/ChaosTests/Cipher/Arc4GenTests.cpp b/ChaosTests/Cipher/Arc4GenTests.cpp index 988d694..81fe1b1 100644 --- a/ChaosTests/Cipher/Arc4GenTests.cpp +++ b/ChaosTests/Cipher/Arc4GenTests.cpp @@ -1,6 +1,11 @@ #include +#include +#include +#include +#include #include "Cipher/Arc4/Arc4Gen.hpp" +#include "Service/ChaosException.hpp" using namespace Chaos::Cipher::Arc4; diff --git a/ChaosTests/Cipher/DesCryptTests.cpp b/ChaosTests/Cipher/DesCryptTests.cpp index 9a06d00..6d4e7f7 100644 --- a/ChaosTests/Cipher/DesCryptTests.cpp +++ b/ChaosTests/Cipher/DesCryptTests.cpp @@ -1,7 +1,12 @@ #include +#include +#include +#include #include "Cipher/Block/Des/DesCrypt.hpp" #include "Cipher/Block/Encryptor.hpp" +#include "Cipher/Block/Decryptor.hpp" +#include "Service/ChaosException.hpp" using namespace Chaos::Cipher::Block::Des; using namespace Chaos::Cipher::Block; diff --git a/ChaosTests/Hash/Md4HasherTests.cpp b/ChaosTests/Hash/Md4HasherTests.cpp index f234dd3..ab50e04 100644 --- a/ChaosTests/Hash/Md4HasherTests.cpp +++ b/ChaosTests/Hash/Md4HasherTests.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "Hash/Md4.hpp" diff --git a/ChaosTests/Hash/Md5HasherTests.cpp b/ChaosTests/Hash/Md5HasherTests.cpp index c17ef33..e2a4c6c 100644 --- a/ChaosTests/Hash/Md5HasherTests.cpp +++ b/ChaosTests/Hash/Md5HasherTests.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "Hash/Md5.hpp" diff --git a/ChaosTests/Hash/Sha1HasherTests.cpp b/ChaosTests/Hash/Sha1HasherTests.cpp index 2fce13a..38dccf5 100644 --- a/ChaosTests/Hash/Sha1HasherTests.cpp +++ b/ChaosTests/Hash/Sha1HasherTests.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "Hash/Sha1.hpp" diff --git a/ChaosTests/Mac/HmacTests.cpp b/ChaosTests/Mac/HmacTests.cpp index a782bd6..7882697 100644 --- a/ChaosTests/Mac/HmacTests.cpp +++ b/ChaosTests/Mac/HmacTests.cpp @@ -1,8 +1,12 @@ #include +#include +#include +#include #include "Hash/Md5.hpp" #include "Hash/Sha1.hpp" #include "Mac/Hmac.hpp" +#include "Service/ChaosException.hpp" using namespace Chaos::Mac::Hmac; using namespace Chaos::Hash::Md5; diff --git a/ChaosTests/Padding/PadderIso7816Tests.cpp b/ChaosTests/Padding/PadderIso7816Tests.cpp index e29b0c0..bd03976 100644 --- a/ChaosTests/Padding/PadderIso7816Tests.cpp +++ b/ChaosTests/Padding/PadderIso7816Tests.cpp @@ -4,6 +4,7 @@ #include #include "Padding/PadderIso7816.hpp" +#include "Padding/Padder.hpp" using namespace Chaos::Padding; diff --git a/ChaosTests/Padding/PadderPkcs7Tests.cpp b/ChaosTests/Padding/PadderPkcs7Tests.cpp index 874a704..0071238 100644 --- a/ChaosTests/Padding/PadderPkcs7Tests.cpp +++ b/ChaosTests/Padding/PadderPkcs7Tests.cpp @@ -4,6 +4,7 @@ #include #include "Padding/PadderPkcs7.hpp" +#include "Padding/Padder.hpp" #include "Service/ChaosException.hpp" using namespace Chaos::Padding; diff --git a/ChaosTests/Service/SeArrayTests.cpp b/ChaosTests/Service/SeArrayTests.cpp index aa08a12..ae224c0 100644 --- a/ChaosTests/Service/SeArrayTests.cpp +++ b/ChaosTests/Service/SeArrayTests.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "Service/SeArray.hpp" From a9c1e52c4522267db97d5e0c3ab5d9319c202b70 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:53:46 +0300 Subject: [PATCH 14/21] Remove trailing whitespaces. --- ChaosTests/Service/SeArrayTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChaosTests/Service/SeArrayTests.cpp b/ChaosTests/Service/SeArrayTests.cpp index ae224c0..9c703e0 100644 --- a/ChaosTests/Service/SeArrayTests.cpp +++ b/ChaosTests/Service/SeArrayTests.cpp @@ -17,7 +17,7 @@ TEST(SeArrayTests, InitializationTest) ASSERT_EQ(0, arr[i]); } } - + { SeArray arr; @@ -59,7 +59,7 @@ TEST(SeArrayTests, EraseTest) ASSERT_EQ(0, arr[i]); } } - + { SeArray arr; From 1dc703922b687be289fc45a014ad1fa997600081 Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:54:15 +0300 Subject: [PATCH 15/21] Remove unused #include. --- ChaosTests/Cipher/Arc4CryptTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ChaosTests/Cipher/Arc4CryptTests.cpp b/ChaosTests/Cipher/Arc4CryptTests.cpp index 200931f..8453504 100644 --- a/ChaosTests/Cipher/Arc4CryptTests.cpp +++ b/ChaosTests/Cipher/Arc4CryptTests.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include From 242a35bd96563ddd496868654ef196d5c3de707d Mon Sep 17 00:00:00 2001 From: hashlag Date: Mon, 20 Jul 2026 02:20:54 +0300 Subject: [PATCH 16/21] Implement unpad for PKCS#7 padding. --- Chaos/Padding/Padder.hpp | 6 + Chaos/Padding/PadderPkcs7.hpp | 112 +++++++++++++ ChaosTests/Padding/PadderPkcs7Tests.cpp | 207 ++++++++++++++++++++++++ 3 files changed, 325 insertions(+) diff --git a/Chaos/Padding/Padder.hpp b/Chaos/Padding/Padder.hpp index 2f75e6e..5ae50e0 100644 --- a/Chaos/Padding/Padder.hpp +++ b/Chaos/Padding/Padder.hpp @@ -14,6 +14,12 @@ public: Impl().Pad(begin, end); } + template + auto ComputeUnpad(InputIt begin, InputIt end) const noexcept + { + return Impl().ComputeUnpad(begin, end); + } + protected: Padder() = default; diff --git a/Chaos/Padding/PadderPkcs7.hpp b/Chaos/Padding/PadderPkcs7.hpp index f94f43c..479e404 100644 --- a/Chaos/Padding/PadderPkcs7.hpp +++ b/Chaos/Padding/PadderPkcs7.hpp @@ -1,9 +1,12 @@ #ifndef CHAOS_PADDING_PADDERPKCS7_HPP #define CHAOS_PADDING_PADDERPKCS7_HPP +#include +#include #include #include #include +#include #include "Padding/Padder.hpp" #include "Service/ChaosException.hpp" @@ -19,6 +22,7 @@ public: { auto dist = std::distance(begin, end); + // TODO: dist > 0 if (dist >= 0 && dist <= std::numeric_limits::max()) { for (OutputIt it = begin; it != end; ++it) @@ -31,6 +35,114 @@ public: throw Service::ChaosException("PadderPkcs7::Pad(): invalid range"); } } + + struct ComputeUnpadResult + { + bool IsOkay_; + uint8_t PadSize_; + }; + + template + static ComputeUnpadResult ComputeUnpad(InputIt begin, InputIt end) noexcept + { + if (begin == end) + { + return { .IsOkay_ = false, .PadSize_ = 0 }; + } + + const uint8_t padSize = *std::prev(end); + uint8_t isOkay = ~BlIsZero(padSize); + + size_t suffixSize = 0; + InputIt it = end; + while (it != begin && suffixSize < std::numeric_limits::max()) + { + --it; + ++suffixSize; + + isOkay &= (BlEq(*it, padSize) | + BlGt(suffixSize, padSize)); + } + + isOkay &= BlGe(suffixSize, padSize); + + return + { + .IsOkay_ = static_cast(isOkay), + .PadSize_ = BlSel(isOkay, padSize, 0) + }; + } + +private: + template + static constexpr OutUInt BlMsb(InUInt in) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + + constexpr uint8_t shift = (sizeof(InUInt) * CHAR_BIT) - 1; + return static_cast(0) - (in >> shift); + } + + template + static constexpr OutUInt BlLt(InUInt lhs, InUInt rhs) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + static_assert(sizeof(InUInt) <= sizeof(uint64_t)); + + const uint64_t lhsEx = lhs; + const uint64_t rhsEx = rhs; + + return BlMsb(lhsEx ^ ((lhsEx ^ rhsEx) | ((lhsEx - rhsEx) ^ lhsEx))); + } + + template + static constexpr OutUInt BlIsZero(InUInt in) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + static_assert(sizeof(InUInt) <= sizeof(uint64_t)); + + const uint64_t inEx = in; + + return BlMsb(~inEx & (inEx - 1U)); + } + + template + static constexpr OutUInt BlEq(InUInt lhs, InUInt rhs) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + + return BlIsZero(lhs ^ rhs); + } + + template + static constexpr OutUInt BlGe(InUInt lhs, InUInt rhs) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + + return ~BlLt(lhs, rhs); + } + + template + static constexpr OutUInt BlGt(InUInt lhs, InUInt rhs) noexcept + { + static_assert(std::is_unsigned_v && + std::is_unsigned_v); + + return BlLt(rhs, lhs); + } + + template + static constexpr UInt BlSel(UInt mask, UInt onTrue, UInt onFalse) noexcept + { + static_assert(std::is_unsigned_v); + + return (mask & onTrue) | (~mask & onFalse); + } }; } // namespace Chaos::Padding diff --git a/ChaosTests/Padding/PadderPkcs7Tests.cpp b/ChaosTests/Padding/PadderPkcs7Tests.cpp index 0071238..0e656e7 100644 --- a/ChaosTests/Padding/PadderPkcs7Tests.cpp +++ b/ChaosTests/Padding/PadderPkcs7Tests.cpp @@ -142,3 +142,210 @@ TEST(PadPkcs7Tests, PadThroughBaseTest) ASSERT_EQ(expected, fact); } } + +TEST(PadPkcs7Tests, UnpadTest) +{ + { + std::array data = { 0x01 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(1, result.PadSize_); + } + + { + std::array data = { 0xaa, 0xbb, 0x02, 0x02 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(2, result.PadSize_); + } + + { + std::array data = { 0xaa, 0xbb, 0xcc, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(7, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0x01 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(1, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0xa2, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(7, result.PadSize_); + } + + { + std::array data = { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(10, result.PadSize_); + } + + { + std::array data = { 0x03, 0x03, 0x03, 0x03 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(3, result.PadSize_); + } + + { + std::array data; + data.fill(0x41); + *std::prev(data.end()) = 1; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(1, result.PadSize_); + } + + { + std::array data; + data.fill(0x41); + std::fill(data.end() - 100, data.end(), 100); + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(100, result.PadSize_); + } + + { + std::array data; + data.fill(255); + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(255, result.PadSize_); + } + + { + std::array data; + data.fill(0x9a); + std::fill(data.end() - 255, data.end(), 255); + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(255, result.PadSize_); + } +} + +TEST(PadPkcs7Tests, UnpadErrorTest) +{ + { + std::array data = { }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0x00 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0xa2, 0xa3, 0xff }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0xa2, 0xa3, 0x06 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0xa2, 0xa3, 0x05 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0xa0, 0xa1, 0x03, 0x02, 0x03 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0xff }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } + + { + std::array data = { 0x03, 0x03 }; + + auto result = PadderPkcs7::ComputeUnpad(data.begin(), data.end()); + + ASSERT_FALSE(result.IsOkay_); + ASSERT_EQ(0, result.PadSize_); + } +} + +template +auto ComputeUnpadThroughBase(const Padder & padder, OutputIt begin, OutputIt end) +{ + return padder.ComputeUnpad(begin, end); +} + +TEST(PadPkcs7Tests, ComputeUnpadThroughBaseTest) +{ + { + std::array data = { 0x05, 0x05, 0x05, 0x05, 0x05 }; + + const PadderPkcs7 padder; + auto result = ComputeUnpadThroughBase(padder, data.begin(), data.end()); + + ASSERT_TRUE(result.IsOkay_); + ASSERT_EQ(5, result.PadSize_); + } +} From 78ab3a8c27f3e18b9d21984e196de7a3815f71a5 Mon Sep 17 00:00:00 2001 From: hashlag Date: Sun, 26 Jul 2026 18:55:28 +0300 Subject: [PATCH 17/21] Add copy assignment operator for SeArray. --- Chaos/Service/SeArray.hpp | 2 +- ChaosTests/Service/SeArrayTests.cpp | 35 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Chaos/Service/SeArray.hpp b/Chaos/Service/SeArray.hpp index 986e04e..24c7875 100644 --- a/Chaos/Service/SeArray.hpp +++ b/Chaos/Service/SeArray.hpp @@ -21,7 +21,7 @@ public: SeArray(const SeArray & other) = delete; SeArray(SeArray && other) = delete; - SeArray & operator=(const SeArray & other) = delete; + SeArray & operator=(const SeArray & other) = default; SeArray & operator=(SeArray && other) = delete; ~SeArray() diff --git a/ChaosTests/Service/SeArrayTests.cpp b/ChaosTests/Service/SeArrayTests.cpp index 9c703e0..cd5400c 100644 --- a/ChaosTests/Service/SeArrayTests.cpp +++ b/ChaosTests/Service/SeArrayTests.cpp @@ -254,3 +254,38 @@ TEST(SeArrayTests, FillTest) ASSERT_EQ(-3, arr[i]); } } + +TEST(SeArrayTests, CopyAssignTest) +{ + { + SeArray arr1; + + SeArray arr2; + arr2[0] = 0xaa; + arr2[1] = 0xbb; + + arr1 = arr2; + + ASSERT_EQ(0xaa, arr1[0]); + ASSERT_EQ(0xbb, arr1[1]); + } + + { + SeArray arr1; + arr1[0] = 0x33; + arr1[1] = 0x44; + arr1[2] = 0x55; + arr1[3] = 0x66; + arr1[4] = 0x77; + + SeArray arr2; + + arr1 = arr2; + + ASSERT_EQ(0, arr1[0]); + ASSERT_EQ(0, arr1[1]); + ASSERT_EQ(0, arr1[2]); + ASSERT_EQ(0, arr1[3]); + ASSERT_EQ(0, arr1[4]); + } +} From 6ae4df4bfc7fc8af7d26c1d8e291666620345916 Mon Sep 17 00:00:00 2001 From: hashlag Date: Wed, 29 Jul 2026 02:25:51 +0300 Subject: [PATCH 18/21] Add ECB-mode decryptor draft implementation. --- Chaos/Cipher/Block/Mode/Ecb.hpp | 146 ++++++++ ChaosTests/Cipher/EcbModeTests.cpp | 529 +++++++++++++++++++++++++++++ 2 files changed, 675 insertions(+) diff --git a/Chaos/Cipher/Block/Mode/Ecb.hpp b/Chaos/Cipher/Block/Mode/Ecb.hpp index a5b2a45..8b22341 100644 --- a/Chaos/Cipher/Block/Mode/Ecb.hpp +++ b/Chaos/Cipher/Block/Mode/Ecb.hpp @@ -106,6 +106,152 @@ public: return written; } }; + + class Decryptor + { + public: + Decryptor(const typename CipherT::Key & key) + : Decryptor_(key) + , BlockBytesPacked_(0) + , PreviousBlockSaved_(false) + { } + + static constexpr uint64_t PredictMaxUpdateOutput(uint64_t in) + { + return in + CipherT::BlockSize - 1; + } + + static constexpr uint64_t PredictMaxFinishOutput() + { + return CipherT::BlockSize; + } + + template + uint64_t Update(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + return UpdateImpl(outBegin, outEnd, inBegin, inEnd); + } + + template + uint64_t Finish(OutputIt outBegin, OutputIt outEnd) + { + if (BlockBytesPacked_ != 0) + { + throw Service::ChaosException("EcbMode<>::Decryptor: ciphertext size " + "is not a multiple of the algorithm's " + "block size"); + } + + if (!PreviousBlockSaved_) + { + return 0; + } + + auto unpadResult = PadderT::ComputeUnpad(PreviousBlock_.Begin(), + PreviousBlock_.End()); + if (!unpadResult.IsOkay_) + { + throw Service::ChaosException("EcbMode<>::Decryptor: invalid ciphertext"); + } + + const uint64_t lastChunkSize = CipherT::BlockSize - unpadResult.PadSize_; + + EnsureCopy(outBegin, outEnd, + PreviousBlock_.Begin(), + PreviousBlock_.Begin() + lastChunkSize); + + return lastChunkSize; + } + + private: + using BlockArray = Service::SeArray; + + typename CipherT::Decryptor Decryptor_; + + uint64_t BlockBytesPacked_; + BlockArray Block_; + + BlockArray DecryptedBlock_; + + bool PreviousBlockSaved_; + BlockArray PreviousBlock_; + + template + static OutputIt EnsureCopy(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + OutputIt out = outBegin; + InputIt in = inBegin; + + for (; out != outEnd && in != inEnd; ++out, ++in) + { + *out = *in; + } + + if (out == outEnd && in != inEnd) + { + throw Service::ChaosException("EcbMode<>::Decryptor: insufficient output " + "buffer size"); + } + + return out; + } + + template + struct DelayedFlushResult + { + OutputIt Next_; + uint64_t Written_; + }; + + template + DelayedFlushResult DelayedFlush(OutputIt outBegin, OutputIt outEnd) + { + if (PreviousBlockSaved_) + { + OutputIt result = EnsureCopy(outBegin, outEnd, + PreviousBlock_.Begin(), PreviousBlock_.End()); + PreviousBlock_ = DecryptedBlock_; + + return { .Next_ = result, .Written_ = CipherT::BlockSize }; + } + else + { + PreviousBlock_ = DecryptedBlock_; + PreviousBlockSaved_ = true; + + return { .Next_ = outBegin, .Written_ = 0 }; + } + } + + template + uint64_t UpdateImpl(OutputIt outBegin, OutputIt outEnd, + InputIt inBegin, InputIt inEnd) + { + uint64_t written = 0; + OutputIt out = outBegin; + + for (InputIt in = inBegin; in != inEnd; ++in) + { + Block_[BlockBytesPacked_++] = *in; + + if (BlockBytesPacked_ == Block_.Size()) + { + BlockBytesPacked_ = 0; + + Decryptor_.DecryptBlock(DecryptedBlock_.Begin(), DecryptedBlock_.End(), + Block_.Begin(), Block_.End()); + auto result = DelayedFlush(out, outEnd); + + out = result.Next_; + written += result.Written_; + } + } + + return written; + } + }; }; } // namespace Chaos::Cipher::Block::Mode diff --git a/ChaosTests/Cipher/EcbModeTests.cpp b/ChaosTests/Cipher/EcbModeTests.cpp index d4a28f0..9ee83e9 100644 --- a/ChaosTests/Cipher/EcbModeTests.cpp +++ b/ChaosTests/Cipher/EcbModeTests.cpp @@ -437,3 +437,532 @@ TEST(EcbModeTests, EncryptInsufficientBufferTest) Chaos::Service::ChaosException); } } + +TEST(EcbModeTests, DecryptTest) +{ + struct Helper + { + std::vector operator()(const std::vector & data, + const std::vector & key) + { + Des::DesCrypt::Key desKey(key.begin(), key.end()); + EcbMode::Decryptor dec(desKey); + + std::vector out; + out.resize(dec.PredictMaxUpdateOutput(data.size()) + + dec.PredictMaxFinishOutput()); + + uint64_t written = dec.Update(out.begin(), out.end(), data.begin(), data.end()); + written += dec.Finish(out.begin() + written, out.end()); + + out.resize(written); + return out; + } + }; + + Helper ecbDec; + + { + std::vector data = { }; + std::vector key = { 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb }; + + std::vector expected = { }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0xef, 0xe2, 0x4d, 0xe9, 0x9d, 0xe7, 0x9b, 0xbf }; + std::vector key = { 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb }; + + std::vector expected = { }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0x84, 0x39, 0xcd, 0xe5, 0x1f, 0x3d, + 0x2a, 0x11, 0x63, 0xc3, 0x56, 0x28, + 0xf5, 0x89, 0xdb, 0xc4, 0xa6, 0xca, + 0x52, 0xb3, 0xa8, 0xce, 0x64, 0x2d }; + std::vector key = { 0x0c, 0xfd, 0x01, 0xa3, 0x60, 0xd0, 0x15, 0xa6 }; + + const char * str = "smoke and mirrors"; + std::vector expected(str, str + strlen(str)); + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0x64, 0x32, 0x13, 0xe7, 0x31, 0x06, 0xc6, 0x6f }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + std::vector expected = { 0x51, 0x89, 0x9f, 0x0c, 0x32, 0x9b, 0x1d }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0xee, 0x5b, 0x16, 0x33, 0xad, 0x2d, 0x32, 0x42, + 0xdc, 0x0d, 0x9f, 0x87, 0x55, 0x5b, 0xb1, 0x53 }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + std::vector expected = { 0x32, 0x00, 0x93, 0x61, 0xc6, 0x9a, 0x25, 0x25, 0x2e }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0x55, 0xc1, 0x6f, 0xbb, 0xb0, 0xf3, 0xd3, 0xa6, 0xb7, 0xe6, 0x12, 0xb4, + 0x49, 0x86, 0x64, 0x07, 0x82, 0xbb, 0x45, 0x46, 0x70, 0xae, 0x64, 0x5f }; + std::vector key = { 0x44, 0x0a, 0xb1, 0x6d, 0xe0, 0x67, 0x28, 0x9d }; + + std::vector expected = { 0x4d, 0x0d, 0xfb, 0x59, 0xe6, 0xc4, 0xf2, 0xe7, + 0xab, 0x46, 0x4f, 0x59, 0x48, 0x11, 0x1b, 0x4f }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0xf4, 0x46, 0x31, 0xc5, 0xe8, 0xff, 0xdc, 0xac, 0x6c, 0x0e, 0xe8, 0x0e, + 0x9f, 0xc2, 0xeb, 0xdf, 0xca, 0xac, 0xc3, 0xc0, 0xea, 0xba, 0x6e, 0xc2, + 0x33, 0x5d, 0x16, 0x39, 0x74, 0xd1, 0x1d, 0xe1 }; + std::vector key = { 0xe4, 0x60, 0xb9, 0xfa, 0x4b, 0x79, 0x6a, 0xf3 }; + + std::vector expected = { 0x8a, 0xe3, 0x85, 0xe0, 0x59, 0xe7, 0xee, 0xce, 0xc0, 0xbf, 0x50, 0x53, + 0x95, 0xbf, 0x4b, 0xdd, 0x3b, 0x02, 0x2f, 0xcb, 0xd0, 0xd3, 0x62, 0x60 }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0x53, 0x06, 0x73, 0x79, 0xb8, 0xd7, 0xbe, 0x5d, 0x6e, 0xa0, + 0x1f, 0x03, 0x46, 0x07, 0xd6, 0x48, 0x65, 0x4f, 0x9a, 0x8f, + 0x1a, 0xe4, 0xa7, 0x7f, 0x13, 0xc9, 0x06, 0x8c, 0x57, 0xbd, + 0x79, 0x5e, 0x3d, 0x21, 0x02, 0x13, 0x5f, 0xae, 0xc9, 0x45 }; + std::vector key = { 0xe9, 0xeb, 0x1f, 0xfa, 0x39, 0x95, 0xf0, 0xb9 }; + + std::vector expected = { 0x96, 0x9f, 0x42, 0xd3, 0x47, 0x10, 0x4d, 0xb4, 0x89, 0x54, + 0x5c, 0xad, 0x0a, 0xb9, 0xac, 0x4e, 0xbf, 0x00, 0x1a, 0x47, + 0x3d, 0xd1, 0xe7, 0xb3, 0x70, 0xb7, 0x4e, 0xd9, 0x73, 0x7d, + 0xb3, 0x24 }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0xc2, 0x88, 0xe5, 0x27, 0xd1, 0xb4, 0xa5, 0xf9, 0xb6, 0x02, + 0x43, 0x3e, 0xae, 0x59, 0x43, 0xd4, 0xe5, 0x87, 0x20, 0xfe, + 0xd0, 0x34, 0x28, 0x6a, 0xef, 0xb1, 0x9e, 0x5a, 0xdf, 0xfb, + 0xf7, 0x94, 0xb4, 0x0c, 0xc2, 0x92, 0x4e, 0x73, 0xda, 0xca }; + std::vector key = { 0x3b, 0x4d, 0xdd, 0x0a, 0x28, 0xf5, 0x96, 0x36 }; + + std::vector expected = { 0xde, 0xbf, 0x28, 0x16, 0x0d, 0xfe, 0x64, 0xbf, 0xc6, 0xfd, + 0x2d, 0x32, 0x45, 0xb0, 0xb7, 0x53, 0xde, 0x5a, 0x8e, 0x45, + 0x87, 0xb3, 0x94, 0x0b, 0xdb, 0x9d, 0x23, 0x33, 0xd7, 0x8e, + 0x63, 0x2d, 0x56 }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } + + { + std::vector data = { 0xf7, 0x44, 0x18, 0x9b, 0x9d, 0x8d, 0xe1, 0x14, 0xcd, 0x0a, 0xdb, 0xbd, + 0x5b, 0xd1, 0x42, 0x6e, 0x17, 0x73, 0x60, 0x9e, 0x14, 0xad, 0xd2, 0x4c, + 0x33, 0x30, 0xdd, 0xb9, 0xf3, 0x46, 0xd0, 0x8d, 0xa6, 0x37, 0x49, 0xd7, + 0x90, 0x23, 0x05, 0xcc, 0x56, 0xde, 0x1f, 0xde, 0x39, 0x6c, 0x04, 0x97, + 0x1d, 0xaf, 0xb5, 0xff, 0xab, 0x47, 0xc8, 0x51 }; + std::vector key = { 0x3c, 0xd0, 0xab, 0xd3, 0xb4, 0xc3, 0xac, 0x53 }; + + std::vector expected = { 0x7e, 0x2c, 0x68, 0x20, 0x22, 0xb1, 0xdd, 0x6d, 0x6e, 0x8b, 0x8b, + 0x30, 0x99, 0xaf, 0x79, 0x2f, 0x6c, 0x95, 0x11, 0x8d, 0xd4, 0x2f, + 0xcf, 0x1d, 0xe6, 0xa0, 0xc7, 0x73, 0x48, 0xb1, 0x65, 0xa9, 0xf6, + 0xc5, 0x1c, 0x42, 0x2d, 0xdd, 0xcf, 0xf5, 0xd4, 0xee, 0xa5, 0xa7, + 0x69, 0xf9, 0x27, 0x93, 0xce }; + + ASSERT_EQ(expected, ecbDec(data, key)); + } +} + +TEST(EcbModeTests, DecryptManyUpdatesTest) +{ + struct Helper + { + Helper(const std::vector & key) + : Key_(key.begin(), key.end()) + , Dec_(Key_) + , Written_(0) + { } + + void Update(const std::vector & data) + { + Out_.resize(Out_.size() + Dec_.PredictMaxUpdateOutput(data.size())); + + Written_ += Dec_.Update(Out_.begin() + Written_, Out_.end(), + data.begin(), data.end()); + } + + std::vector Finish() + { + Out_.resize(Out_.size() + Dec_.PredictMaxFinishOutput()); + + Written_ += Dec_.Finish(Out_.begin() + Written_, Out_.end()); + Out_.resize(Written_); + + return Out_; + } + + Des::DesCrypt::Key Key_; + EcbMode::Decryptor Dec_; + + std::vector Out_; + uint64_t Written_; + }; + + { + std::vector key = { 0xab, 0x39, 0x20, 0xea, 0xaa, 0x95, 0x1c, 0x90 }; + + std::vector data1 = { 0xf4, 0xde, 0x9a, 0xe4, 0x78, 0x8c, 0xf2, 0xe6, 0x75 }; + std::vector data2 = { 0x34, 0xe7, 0x9f, 0xe8, 0x16, 0x09, 0x12, 0x55 }; + std::vector data3 = { 0x75, 0x5b, 0xe4, 0x21, 0x65, + 0x98, 0x0e, 0xc6, 0x7a, 0x62, 0xf6, 0x88, 0xcd, 0x3e, 0x83 }; + + std::vector expected = { 0x97, 0x65, 0xe2, 0xb1, 0xae, 0x3e, 0x55, 0xff, 0x1d, + 0xac, 0x8a, 0xa6, 0xac, 0xa5, 0x9a, 0xf9, 0xb6, + 0x3c, 0xba, 0x95, 0x5e, 0x78, 0x29, 0x22, 0x7c, 0x12, 0x7e, 0x4c }; + + Helper ecbDec(key); + + ecbDec.Update(data1); + ecbDec.Update(data2); + ecbDec.Update(data3); + + ASSERT_EQ(expected, ecbDec.Finish()); + } + + { + std::vector key = { 0xd5, 0xc1, 0x3a, 0xcc, 0x6f, 0x86, 0x1d, 0x4b }; + + std::vector data1 = { 0x5f, 0x18, 0x61, 0x15, 0x72, 0x75, 0x73, 0xb3 }; + std::vector data2 = { 0x44, 0xa5, 0xf0, 0x46, 0xa9, 0x4b, 0x7e, 0x5e }; + std::vector data3 = { 0x5b, 0x88, 0x46, 0x07, 0x5e, 0x33, 0x16, 0x01 }; + std::vector data4 = { 0x69, 0x13, 0x76, 0x8b, 0x7f, 0xbc, 0xf2, 0x75 }; + + std::vector expected = { 0x43, 0x73, 0x1b, 0xde, 0xbd, 0xfe, 0x16, 0x58, + 0x8c, 0x16, 0x13, 0x53, 0x22, 0x66, 0xb4, 0xf2, + 0x25, 0xcf, 0xcd, 0xab, 0x7d, 0x29, 0x9a, 0xd7 }; + + Helper ecbDec(key); + + ecbDec.Update(data1); + ecbDec.Update(data2); + ecbDec.Update(data3); + ecbDec.Update(data4); + + ASSERT_EQ(expected, ecbDec.Finish()); + } + + { + std::vector key = { 0xc0, 0x98, 0x56, 0xb3, 0x27, 0xc9, 0x78, 0x89 }; + + std::vector data1 = { 0xf9 }; + std::vector data2 = { 0x79 }; + std::vector data3 = { 0x6d, 0x46, 0xb6, 0x4a, 0xa8, 0xaa, 0x82, 0xb9 }; + std::vector data4 = { 0xca }; + std::vector data5 = { 0x65, 0xe9, 0x3c, 0xa8, 0xdc }; + + std::vector expected = { 0xb7, 0xb3, 0x95, 0x56, 0xc0, 0x9f, 0x74, 0xd0, + 0x49, 0xb5, 0x15 }; + + Helper ecbDec(key); + + ecbDec.Update(data1); + ecbDec.Update(data2); + ecbDec.Update(data3); + ecbDec.Update(data4); + ecbDec.Update(data5); + + ASSERT_EQ(expected, ecbDec.Finish()); + } + + { + std::vector key = { 0xcb, 0x22, 0x90, 0x27, 0x87, 0x42, 0xd1, 0x58 }; + + std::vector data1 = { 0xfb, 0x9d, 0x21, 0x86, 0x9b, 0xf1, 0x79 }; + std::vector data2 = { 0xeb, 0x6c, 0xcf, 0xcc, 0xe7, 0x55, 0x48, 0x05, 0x42, 0x37, 0x23 }; + std::vector data3 = { 0xcc, 0xda, 0x82, 0x8f, 0xe7, 0x5a, 0x08, 0x85, + 0x00, 0x0b, 0x38, 0x5f, 0x54, 0x53, 0xf9, 0x40 }; + std::vector data4 = { 0x59, 0xc7, 0xf3 }; + std::vector data5 = { 0x06, 0x10, 0x10, 0xed, 0xfc }; + std::vector data6 = { 0x76, 0xff, 0xe6, 0x55, 0xb0, 0xe1 }; + + std::vector expected = { 0xca, 0x40, 0xeb, 0x08, 0x75, 0x62, 0x41, 0x16, + 0xa4, 0x12, 0x21, 0x3d, 0x5a, 0xf2, 0xb4, 0xa7, + 0xb5, 0xa7, 0x59, 0x51, 0xd0, 0x41, 0x21, 0xf0, + 0x2b, 0x76, 0xd8, 0xe4, 0x60, 0xb5, 0xab, 0xd8, + 0x25, 0x29, 0x94, 0x9f, 0xed, 0x1a, 0xa1, 0x57, + 0x35, 0x2c }; + + Helper ecbDec(key); + + ecbDec.Update(data1); + ecbDec.Update(data2); + ecbDec.Update(data3); + ecbDec.Update(data4); + ecbDec.Update(data5); + ecbDec.Update(data6); + + ASSERT_EQ(expected, ecbDec.Finish()); + } +} + +TEST(EcbModeTests, DecryptLongInputTest) +{ + struct Helper + { + Helper(const std::vector & key) + : Key_(key.begin(), key.end()) + , Dec_(Key_) + , Written_(0) + { } + + void Update(const std::vector & data) + { + Out_.resize(Out_.size() + Dec_.PredictMaxUpdateOutput(data.size())); + + Written_ += Dec_.Update(Out_.begin() + Written_, Out_.end(), + data.begin(), data.end()); + } + + void Update(std::vector::const_iterator begin, std::vector::const_iterator end) + { + Out_.resize(Out_.size() + Dec_.PredictMaxUpdateOutput(std::distance(begin, end))); + Written_ += Dec_.Update(Out_.begin() + Written_, Out_.end(), begin, end); + } + + std::vector Finish() + { + Out_.resize(Out_.size() + Dec_.PredictMaxFinishOutput()); + + Written_ += Dec_.Finish(Out_.begin() + Written_, Out_.end()); + Out_.resize(Written_); + + return Out_; + } + + Des::DesCrypt::Key Key_; + EcbMode::Decryptor Dec_; + + std::vector Out_; + uint64_t Written_; + }; + + { + std::vector key = { 0x27, 0x07, 0x7c, 0xc9, 0xd2, 0xbe, 0x76, 0x6c }; + + std::vector data; + { + std::vector block = { 0x90, 0xf0, 0x72, 0xae, 0xcc, 0x98, 0x93, 0x8d }; + + for (int i = 0; i < 256; ++i) + { + data.insert(data.end(), block.begin(), block.end()); + } + + std::vector lastBlock = { 0xce, 0x7c, 0x12, 0x8e, 0x8c, 0xc0, 0x88, 0x02 }; + data.insert(data.end(), lastBlock.begin(), lastBlock.end()); + } + + std::vector expected(2048, 0xab); + + Helper ecbDec(key); + ecbDec.Update(data); + + ASSERT_EQ(expected, ecbDec.Finish()); + } + + { + std::vector key = { 0xcc, 0xc5, 0x08, 0x98, 0xe9, 0x8c, 0xeb, 0x23 }; + + std::vector data; + { + std::vector block = { 0xb7, 0x9f, 0x07, 0x2c, 0xe9, 0x54, 0x83, 0xc5 }; + + for (int i = 0; i < 2048; ++i) + { + data.insert(data.end(), block.begin(), block.end()); + } + + std::vector lastBlock = { 0x14, 0x59, 0x29, 0x72, 0x45, 0xb3, 0x03, 0xb3 }; + data.insert(data.end(), lastBlock.begin(), lastBlock.end()); + } + + std::vector expected(16384, 0x7a); + + Helper ecbDec(key); + + ecbDec.Update(data.begin(), data.begin() + 2340); + ecbDec.Update(data.begin() + 2340, data.begin() + 3511); + ecbDec.Update(data.begin() + 3511, data.begin() + 6436); + ecbDec.Update(data.begin() + 6436, data.begin() + 6437); + ecbDec.Update(data.begin() + 6437, data.begin() + 10532); + ecbDec.Update(data.begin() + 10532, data.begin() + 11118); + ecbDec.Update(data.begin() + 11118, data.begin() + 14628); + ecbDec.Update(data.begin() + 14628, data.begin() + 16384); + ecbDec.Update(data.begin() + 16384, data.end()); + + ASSERT_EQ(expected, ecbDec.Finish()); + } +} + +TEST(EcbModeTests, DecryptInsufficientBufferTest) +{ + std::vector key = { 0xcc, 0xc5, 0x08, 0x98, 0xe9, 0x8c, 0xeb, 0x23 }; + Des::DesCrypt::Key desKey(key.begin(), key.end()); + + { + std::vector data = { 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x14, 0x59, 0x29, 0x72, 0x45, 0xb3, 0x03, 0xb3 }; + std::vector out; + + EcbMode::Decryptor dec(desKey); + + ASSERT_THROW(dec.Update(out.begin(), out.end(), data.begin(), data.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data = { 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x14, 0x59, 0x29, 0x72, 0x45, 0xb3, 0x03, 0xb3 }; + std::vector out(8, 0); + + EcbMode::Decryptor dec(desKey); + + ASSERT_THROW(dec.Update(out.begin(), out.end(), data.begin(), data.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data1 = { 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea }; + std::vector data2 = { 0x14, 0x59, 0x29, 0x72, 0x45, 0xb3, 0x03, 0xb3 }; + + std::vector out1(16, 0); + std::vector out2(2, 0); + + EcbMode::Decryptor dec(desKey); + + dec.Update(out1.begin(), out1.end(), data1.begin(), data1.end()); + + ASSERT_THROW(dec.Update(out2.begin(), out2.end(), data2.begin(), data2.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data = { 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x62, 0x3a, 0x45, 0xdd, 0x62, 0xda, 0xb0, 0x45 }; + std::vector out(8, 0); + + EcbMode::Decryptor dec(desKey); + + uint64_t written = dec.Update(out.begin(), out.end(), data.begin(), data.end()); + + ASSERT_THROW(written += dec.Finish(out.begin() + written, out.end()), + Chaos::Service::ChaosException); + } + + { + std::vector data = { 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0x65, 0x35, 0xa5, 0x01, 0x29, 0xf4, 0xfd, 0xea, + 0xbd, 0x11, 0x44, 0xf4, 0x99, 0x95, 0x53, 0xd3 }; + std::vector out(17, 0); + + EcbMode::Decryptor dec(desKey); + + uint64_t written = dec.Update(out.begin(), out.end(), data.begin(), data.end()); + + ASSERT_THROW(written += dec.Finish(out.begin() + written, out.end()), + Chaos::Service::ChaosException); + } +} + +TEST(EcbModeTests, DecryptNotRoundSizeTest) +{ + struct Helper + { + std::vector operator()(const std::vector & data, + const std::vector & key) + { + Des::DesCrypt::Key desKey(key.begin(), key.end()); + EcbMode::Decryptor dec(desKey); + + std::vector out; + out.resize(dec.PredictMaxUpdateOutput(data.size()) + + dec.PredictMaxFinishOutput()); + + uint64_t written = dec.Update(out.begin(), out.end(), data.begin(), data.end()); + written += dec.Finish(out.begin() + written, out.end()); + + out.resize(written); + return out; + } + }; + + Helper ecbDec; + + { + std::vector data = { 0x64, 0x32, 0x13, 0xe7, 0x31, 0x06, 0xc6 }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + ASSERT_THROW(ecbDec(data, key), Chaos::Service::ChaosException); + } + + { + std::vector data = { 0xee, 0x5b, 0x16, 0x33, 0xad, 0x2d, 0x32, 0x42, + 0xdc, 0x0d, 0x9f, 0x87 }; + std::vector key = { 0x28, 0x1c, 0xf3, 0x11, 0xce, 0xc6, 0xc2, 0x38 }; + + ASSERT_THROW(ecbDec(data, key), Chaos::Service::ChaosException); + } +} + +TEST(EcbModeTests, DecryptInvalidPaddingTest) +{ + struct Helper + { + std::vector operator()(const std::vector & data, + const std::vector & key) + { + Des::DesCrypt::Key desKey(key.begin(), key.end()); + EcbMode::Decryptor dec(desKey); + + std::vector out; + out.resize(dec.PredictMaxUpdateOutput(data.size()) + + dec.PredictMaxFinishOutput()); + + uint64_t written = dec.Update(out.begin(), out.end(), data.begin(), data.end()); + written += dec.Finish(out.begin() + written, out.end()); + + out.resize(written); + return out; + } + }; + + Helper ecbDec; + + { + std::vector data = { 0x60, 0xa7, 0x4b, 0x8c, 0x68, 0x03, 0x70, 0x0c }; + std::vector key = { 0xaa, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf1, 0x12 }; + + ASSERT_THROW(ecbDec(data, key), Chaos::Service::ChaosException); + } + + { + std::vector data = { 0xf6, 0x61, 0x7a, 0x71, 0xe8, 0xa4, 0xb9, 0x70, + 0xbe, 0xd2, 0x51, 0x7e, 0x4f, 0x39, 0xfe, 0xa2 }; + std::vector key = { 0xaa, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf1, 0x12 }; + + ASSERT_THROW(ecbDec(data, key), Chaos::Service::ChaosException); + } +} From c9a262bac41b55bd480a35bfde75980c6d26220e Mon Sep 17 00:00:00 2001 From: hashlag Date: Thu, 30 Jul 2026 01:06:21 +0300 Subject: [PATCH 19/21] EcbMode: Inline the DelayedFlush() method. --- Chaos/Cipher/Block/Mode/Ecb.hpp | 43 ++++++++++----------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/Chaos/Cipher/Block/Mode/Ecb.hpp b/Chaos/Cipher/Block/Mode/Ecb.hpp index 8b22341..1160cd5 100644 --- a/Chaos/Cipher/Block/Mode/Ecb.hpp +++ b/Chaos/Cipher/Block/Mode/Ecb.hpp @@ -198,33 +198,6 @@ public: return out; } - template - struct DelayedFlushResult - { - OutputIt Next_; - uint64_t Written_; - }; - - template - DelayedFlushResult DelayedFlush(OutputIt outBegin, OutputIt outEnd) - { - if (PreviousBlockSaved_) - { - OutputIt result = EnsureCopy(outBegin, outEnd, - PreviousBlock_.Begin(), PreviousBlock_.End()); - PreviousBlock_ = DecryptedBlock_; - - return { .Next_ = result, .Written_ = CipherT::BlockSize }; - } - else - { - PreviousBlock_ = DecryptedBlock_; - PreviousBlockSaved_ = true; - - return { .Next_ = outBegin, .Written_ = 0 }; - } - } - template uint64_t UpdateImpl(OutputIt outBegin, OutputIt outEnd, InputIt inBegin, InputIt inEnd) @@ -242,10 +215,20 @@ public: Decryptor_.DecryptBlock(DecryptedBlock_.Begin(), DecryptedBlock_.End(), Block_.Begin(), Block_.End()); - auto result = DelayedFlush(out, outEnd); - out = result.Next_; - written += result.Written_; + if (PreviousBlockSaved_) + { + out = EnsureCopy(out, outEnd, + PreviousBlock_.Begin(), PreviousBlock_.End()); + PreviousBlock_ = DecryptedBlock_; + + written += CipherT::BlockSize; + } + else + { + PreviousBlock_ = DecryptedBlock_; + PreviousBlockSaved_ = true; + } } } From 192cd67deb0154cf26e383927acff12916d26b7a Mon Sep 17 00:00:00 2001 From: hashlag Date: Thu, 30 Jul 2026 01:17:47 +0300 Subject: [PATCH 20/21] EcbMode: Optimize data management on decryption hotpath. --- Chaos/Cipher/Block/Mode/Ecb.hpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Chaos/Cipher/Block/Mode/Ecb.hpp b/Chaos/Cipher/Block/Mode/Ecb.hpp index 1160cd5..c2e51ca 100644 --- a/Chaos/Cipher/Block/Mode/Ecb.hpp +++ b/Chaos/Cipher/Block/Mode/Ecb.hpp @@ -172,8 +172,6 @@ public: uint64_t BlockBytesPacked_; BlockArray Block_; - BlockArray DecryptedBlock_; - bool PreviousBlockSaved_; BlockArray PreviousBlock_; @@ -213,22 +211,17 @@ public: { BlockBytesPacked_ = 0; - Decryptor_.DecryptBlock(DecryptedBlock_.Begin(), DecryptedBlock_.End(), - Block_.Begin(), Block_.End()); - if (PreviousBlockSaved_) { out = EnsureCopy(out, outEnd, PreviousBlock_.Begin(), PreviousBlock_.End()); - PreviousBlock_ = DecryptedBlock_; written += CipherT::BlockSize; } - else - { - PreviousBlock_ = DecryptedBlock_; - PreviousBlockSaved_ = true; - } + + Decryptor_.DecryptBlock(PreviousBlock_.Begin(), PreviousBlock_.End(), + Block_.Begin(), Block_.End()); + PreviousBlockSaved_ = true; } } From 9af224954729e4a68075be697f628554a8371ce1 Mon Sep 17 00:00:00 2001 From: hashlag Date: Thu, 30 Jul 2026 01:21:41 +0300 Subject: [PATCH 21/21] EcbMode: Rename PreviousBlock_ --> LastBlock_. --- Chaos/Cipher/Block/Mode/Ecb.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Chaos/Cipher/Block/Mode/Ecb.hpp b/Chaos/Cipher/Block/Mode/Ecb.hpp index c2e51ca..9d52147 100644 --- a/Chaos/Cipher/Block/Mode/Ecb.hpp +++ b/Chaos/Cipher/Block/Mode/Ecb.hpp @@ -113,7 +113,7 @@ public: Decryptor(const typename CipherT::Key & key) : Decryptor_(key) , BlockBytesPacked_(0) - , PreviousBlockSaved_(false) + , LastBlockSaved_(false) { } static constexpr uint64_t PredictMaxUpdateOutput(uint64_t in) @@ -143,13 +143,13 @@ public: "block size"); } - if (!PreviousBlockSaved_) + if (!LastBlockSaved_) { return 0; } - auto unpadResult = PadderT::ComputeUnpad(PreviousBlock_.Begin(), - PreviousBlock_.End()); + auto unpadResult = PadderT::ComputeUnpad(LastBlock_.Begin(), + LastBlock_.End()); if (!unpadResult.IsOkay_) { throw Service::ChaosException("EcbMode<>::Decryptor: invalid ciphertext"); @@ -158,8 +158,8 @@ public: const uint64_t lastChunkSize = CipherT::BlockSize - unpadResult.PadSize_; EnsureCopy(outBegin, outEnd, - PreviousBlock_.Begin(), - PreviousBlock_.Begin() + lastChunkSize); + LastBlock_.Begin(), + LastBlock_.Begin() + lastChunkSize); return lastChunkSize; } @@ -172,8 +172,8 @@ public: uint64_t BlockBytesPacked_; BlockArray Block_; - bool PreviousBlockSaved_; - BlockArray PreviousBlock_; + bool LastBlockSaved_; + BlockArray LastBlock_; template static OutputIt EnsureCopy(OutputIt outBegin, OutputIt outEnd, @@ -211,17 +211,17 @@ public: { BlockBytesPacked_ = 0; - if (PreviousBlockSaved_) + if (LastBlockSaved_) { out = EnsureCopy(out, outEnd, - PreviousBlock_.Begin(), PreviousBlock_.End()); + LastBlock_.Begin(), LastBlock_.End()); written += CipherT::BlockSize; } - Decryptor_.DecryptBlock(PreviousBlock_.Begin(), PreviousBlock_.End(), + Decryptor_.DecryptBlock(LastBlock_.Begin(), LastBlock_.End(), Block_.Begin(), Block_.End()); - PreviousBlockSaved_ = true; + LastBlockSaved_ = true; } }