/**
 * @file des.h
 * @brief DES and Triple DES (3DES) block ciphers.
 *
 * Declares netplus's own implementation of the Data Encryption Standard
 * (FIPS 46-3): the classic 16-round Feistel cipher operating on 64-bit
 * blocks with a 56-bit effective key (des), and the Triple DES EDE
 * (Encrypt-Decrypt-Encrypt) construction built from three independent DES
 * instances (trippledes). Both ciphers are legacy algorithms retained for
 * interoperability with protocols/formats that still require them (e.g.
 * PKCS#12); neither should be chosen for new designs given DES's 56-bit
 * key size.
 */
#pragma once

#include <array>
#include <iostream>
#include <cstdint>
#include <vector>

namespace netplus {
    // Define DES constants and types
    /** @brief A 64-bit DES data block (plaintext or ciphertext), stored as 8 bytes. */
    using block64 = std::vector<uint8_t>; // 64-bit data block (8 bytes)
    /** @brief A DES key: 8 bytes (64 bits) on the wire, of which 56 bits are effective key material (the 8th bit of each byte is parity). */
    using key56 = std::vector<uint8_t>;   // 56-bit effective key (8 bytes, where 8th bit is parity)

    /**
     * @brief Implements the Data Encryption Standard (DES) algorithm.
     *
     * A single-key, 16-round Feistel-network block cipher operating on
     * 64-bit blocks (FIPS 46-3). The 64-bit input key is reduced to a
     * 56-bit effective key via PC-1 and expanded into sixteen 48-bit round
     * keys (see generateSubKeys()). Each round applies expansion,
     * key-mixing, S-box substitution and a P-box permutation
     * (feistelFunction()) to the right half of the block. Instances are
     * immutable after construction and safe to use concurrently for
     * encrypt()/decrypt() calls from multiple threads, since no mutable
     * state is touched by either operation.
     *
     * @note DES's 56-bit key is considered cryptographically broken against
     *       brute force by modern standards; use trippledes or, preferably,
     *       an AES/aes_gcm cipher for anything requiring real security
     *       margin. This class exists for interoperability with legacy
     *       formats/protocols.
     */
    class des {
    public:
        /**
         * @brief Constructor for the DES class.
         *
         * Derives the master key and immediately runs the key schedule
         * (generateSubKeys()) to precompute all sixteen round keys.
         * @param key The 64-bit key (only 56 bits are used, 8 bits are parity); must be exactly 8 bytes.
         * @throws std::invalid_argument if @p key is not exactly 8 bytes.
         */
        des(const block64& key);

        // Destructor
        /** @brief Destructor (defaulted; no external resources to release). */
        ~des();

        /**
         * @brief Encrypts a 64-bit data block.
         * @param plaintext The 64-bit block to encrypt; must be exactly 8 bytes.
         * @return The 64-bit ciphertext block.
         * @throws std::invalid_argument if @p plaintext is not exactly 8 bytes.
         */
        block64 encrypt(const block64& plaintext) const;

        /**
         * @brief Decrypts a 64-bit data block.
         * @param ciphertext The 64-bit block to decrypt; must be exactly 8 bytes.
         * @return The 64-bit plaintext block.
         * @throws std::invalid_argument if @p ciphertext is not exactly 8 bytes.
         */
        block64 decrypt(const block64& ciphertext) const;

    private:
        /** @brief The raw 64-bit master key as passed to the constructor (before PC-1 reduction to 56 effective bits). */
        uint64_t m_masterKey;
        /** @brief Precomputed 48-bit round keys K1..K16, one per Feistel round, generated by generateSubKeys(). */
        std::array<uint64_t, 16> m_roundKeys48{}; // Storage for 16 round keys (48-bit, stored as 6-byte block64)

        // --- Core DES Algorithm Steps ---
        /**
         * @brief Runs the DES key schedule: PC-1, sixteen rounds of rotate-left-and-split, then PC-2 per round.
         *
         * Reduces m_masterKey to its 56 effective bits via PC-1, splits the
         * result into 28-bit C/D halves, and for each of the 16 rounds
         * rotates both halves left by the round's shift amount (1 or 2 bits
         * per the fixed DES shift schedule) before recombining and applying
         * PC-2 to produce that round's 48-bit key, stored in m_roundKeys48.
         */
        void generateSubKeys();
        /** @brief Applies the DES Initial Permutation (IP) to an 8-byte block, returning the permuted bits packed into a uint64_t. */
        uint64_t initialPermutation(const block64& block) const;
        /** @brief Applies the DES Final Permutation (FP, the inverse of IP) to the 64-bit Feistel pre-output, returning an 8-byte block. */
        block64 finalPermutation(const uint64_t preoutput) const;
        /**
         * @brief The DES Feistel round function F(R, K).
         *
         * Expands the 32-bit right half to 48 bits (E), XORs in the round
         * key, substitutes each of the eight 6-bit groups through its S-box
         * (S1..S8), and permutes the resulting 32 bits (P).
         * @param rightHalf The 32-bit (4-byte) right half of the current Feistel state.
         * @param roundKey48 The 48-bit round key for this round (as produced by generateSubKeys()).
         * @return The 32-bit (4-byte) F-function output to XOR into the left half.
         */
        block64 feistelFunction(const block64& rightHalf,uint64_t roundKey48) const;
    };

    /**
     * @brief Implements Triple DES (3DES) in EDE (Encrypt-Decrypt-Encrypt) mode with three independent keys.
     *
     * Composes three des instances so that encryption computes
     * C = E_K3(D_K2(E_K1(P))) and decryption computes P = D_K1(E_K2(D_K3(C))),
     * the standard "keying option 1" DES-EDE3 construction. This gives an
     * effective key strength greater than single DES while remaining
     * interoperable with legacy systems (e.g. PKCS#12) that require 3DES.
     *
     * @note Prefer AES (aes128/aes256) for new designs; 3DES is retained
     *       here only for compatibility with legacy formats/protocols.
     */
    class trippledes {
    public:
        /**
         * @brief Constructs a Triple DES cipher from three independent 8-byte DES keys.
         * @param key1 The K1 key used for the outer encrypt step (and the inner decrypt step on decryption).
         * @param key2 The K2 key used for the middle decrypt step (and the middle encrypt step on decryption).
         * @param key3 The K3 key used for the inner encrypt step (and the outer decrypt step on decryption).
         * @throws std::invalid_argument if any key is not exactly 8 bytes (via the underlying des constructors).
         */
        trippledes(const block64& key1, const block64& key2, const block64& key3);
         /**
         * @brief Encrypts a 64-bit block using Triple DES EDE (Encrypt-Decrypt-Encrypt) mode: C = E_K3(D_K2(E_K1(P))).
         * @param plaintext The 64-bit block to encrypt; must be exactly 8 bytes.
         * @return The 64-bit ciphertext block.
         * @throws std::invalid_argument if @p plaintext is not exactly 8 bytes.
         */
        block64 encrypt(const block64& plaintext) const;

        /**
         * @brief Decrypts a 64-bit block using Triple DES EDE mode (inverse of encrypt()): P = D_K1(E_K2(D_K3(C))).
         * @param ciphertext The 64-bit block to decrypt; must be exactly 8 bytes.
         * @return The 64-bit plaintext block.
         * @throws std::invalid_argument if @p ciphertext is not exactly 8 bytes.
         */
        block64 decrypt(const block64& ciphertext) const;
    private:
        des m_des1; // DES instance for K1
        des m_des2; // DES instance for K2
        des m_des3; // DES instance for K3
    };

} // namespace netplus
