#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