diff --git a/Chaos/Cipher/Des/DesCrypt.hpp b/Chaos/Cipher/Des/DesCrypt.hpp new file mode 100644 index 0000000..c2bf738 --- /dev/null +++ b/Chaos/Cipher/Des/DesCrypt.hpp @@ -0,0 +1,162 @@ +#ifndef CHAOS_CIPHER_DES_DESCRYPT_HPP +#define CHAOS_CIPHER_DES_DESCRYPT_HPP + +#include "Service/SeArray.hpp" + +namespace Chaos::Cipher::Des::Inner_ +{ + +struct Bitwise +{ + template + static uint8_t GetBit(uint64_t value, int_fast8_t bitNumber) + { + return (value >> (BitsUsed - bitNumber)) & 0b1; + } + + template + static void SetBit(uint64_t & value, int_fast8_t bitNumber) + { + value |= (static_cast(0b1) << (BitsUsed - bitNumber)); + } + + template + static uint64_t TableChoice(uint64_t value, InputIt tableBegin, InputIt tableEnd) + { + uint64_t result = 0; + + int_fast8_t i = 1; + for (InputIt it = tableBegin; it != tableEnd; ++it, ++i) + { + if (GetBit(value, *it)) + { + SetBit(result, i); + } + } + + return result; + } + + template + static constexpr uint64_t Mask() + { + return (static_cast(0b1) << Bits) - static_cast(0b1); + } + + template + static void Rotl(uint64_t & value, int_fast8_t shift) + { + value = ((value << shift) | (value >> (BitsUsed - shift))) & Mask(); + } + + template + static std::pair Split(uint64_t value) + { + return { value >> BitsUsedOut, value & Mask() }; + } + + template + static uint64_t Merge(uint64_t lhs, uint64_t rhs) + { + return (lhs << BitsUsedIn) | rhs; + } +}; + +using RawKeyArray = Service::SeArray; + +class KeySchedule +{ +public: + using Key64 = uint64_t; + using Key56 = uint64_t; + + using RoundKey48 = uint64_t; + + KeySchedule(const RawKeyArray & rawKeyArray) + { + Key64 key64 = 0; + + key64 |= static_cast(rawKeyArray[7]) << 0; + key64 |= static_cast(rawKeyArray[6]) << 8; + key64 |= static_cast(rawKeyArray[5]) << 16; + key64 |= static_cast(rawKeyArray[4]) << 24; + key64 |= static_cast(rawKeyArray[3]) << 32; + key64 |= static_cast(rawKeyArray[2]) << 40; + key64 |= static_cast(rawKeyArray[1]) << 48; + key64 |= static_cast(rawKeyArray[0]) << 56; + + Key56 key56 = Pc1(key64); + + auto [c28, d28] = Bitwise::Split<28>(key56); + + for (int_fast8_t i = 0; i < Schedule_.Size(); ++i) + { + if (i == 0 || i == 1 || i == 8 || i == 15) + { + Bitwise::Rotl<28>(c28, 1); + Bitwise::Rotl<28>(d28, 1); + } + else + { + Bitwise::Rotl<28>(c28, 2); + Bitwise::Rotl<28>(d28, 2); + } + + Schedule_[i] = Pc2(Bitwise::Merge<28>(c28, d28)); + } + } + + RoundKey48 operator[](int_fast8_t i) const + { + return Schedule_[i]; + } + +private: + Service::SeArray Schedule_; + + static Key56 Pc1(Key64 key) + { + constexpr int_fast8_t PC1_TABLE[] = + { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 + }; + + static_assert(std::size(PC1_TABLE) == 56); + + return Bitwise::TableChoice<64, 56>(key, + PC1_TABLE, + PC1_TABLE + std::size(PC1_TABLE)); + } + + static RoundKey48 Pc2(Key56 key) + { + constexpr int_fast8_t PC2_TABLE[] = + { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 + }; + + static_assert(std::size(PC2_TABLE) == 48); + + return Bitwise::TableChoice<56, 48>(key, + PC2_TABLE, + PC2_TABLE + std::size(PC2_TABLE)); + } +}; + +} // namespace Chaos::Cipher::Des::Inner_ + +#endif // CHAOS_CIPHER_DES_DESCRYPT_HPP diff --git a/ChaosTests/CMakeLists.txt b/ChaosTests/CMakeLists.txt index ec4e058..58d83f8 100644 --- a/ChaosTests/CMakeLists.txt +++ b/ChaosTests/CMakeLists.txt @@ -17,6 +17,7 @@ set(ChaosTests_SOURCE Hash/Md4HasherTests.cpp Mac/HmacTests.cpp Cipher/Arc4GenTests.cpp Cipher/Arc4CryptTests.cpp + Cipher/DesCryptTests.cpp Service/SeArrayTests.cpp) add_executable(ChaosTests ${ChaosTests_SOURCE}) diff --git a/ChaosTests/Cipher/DesCryptTests.cpp b/ChaosTests/Cipher/DesCryptTests.cpp new file mode 100644 index 0000000..1816b7e --- /dev/null +++ b/ChaosTests/Cipher/DesCryptTests.cpp @@ -0,0 +1,38 @@ +#include + +#include "Cipher/Des/DesCrypt.hpp" + +using namespace Chaos::Cipher::Des; + +TEST(DesCryptTests, KeyScheduleTest) +{ + Inner_::RawKeyArray key; + + key[0] = 0b00010011; + key[1] = 0b00110100; + key[2] = 0b01010111; + key[3] = 0b01111001; + key[4] = 0b10011011; + key[5] = 0b10111100; + key[6] = 0b11011111; + key[7] = 0b11110001; + + Inner_::KeySchedule schedule(key); + + ASSERT_EQ(0b000110110000001011101111111111000111000001110010ULL, schedule[0]); + ASSERT_EQ(0b011110011010111011011001110110111100100111100101ULL, schedule[1]); + ASSERT_EQ(0b010101011111110010001010010000101100111110011001ULL, schedule[2]); + ASSERT_EQ(0b011100101010110111010110110110110011010100011101ULL, schedule[3]); + ASSERT_EQ(0b011111001110110000000111111010110101001110101000ULL, schedule[4]); + ASSERT_EQ(0b011000111010010100111110010100000111101100101111ULL, schedule[5]); + ASSERT_EQ(0b111011001000010010110111111101100001100010111100ULL, schedule[6]); + ASSERT_EQ(0b111101111000101000111010110000010011101111111011ULL, schedule[7]); + ASSERT_EQ(0b111000001101101111101011111011011110011110000001ULL, schedule[8]); + ASSERT_EQ(0b101100011111001101000111101110100100011001001111ULL, schedule[9]); + ASSERT_EQ(0b001000010101111111010011110111101101001110000110ULL, schedule[10]); + ASSERT_EQ(0b011101010111000111110101100101000110011111101001ULL, schedule[11]); + ASSERT_EQ(0b100101111100010111010001111110101011101001000001ULL, schedule[12]); + ASSERT_EQ(0b010111110100001110110111111100101110011100111010ULL, schedule[13]); + ASSERT_EQ(0b101111111001000110001101001111010011111100001010ULL, schedule[14]); + ASSERT_EQ(0b110010110011110110001011000011100001011111110101ULL, schedule[15]); +}