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_); + } +}