Commit 11083698 authored by jan.koester's avatar jan.koester
Browse files

doc

parent cd80b548
Loading
Loading
Loading
Loading

agent-a054ebb63622e1044 @ f5ec6e97

Original line number Diff line number Diff line
Subproject commit f5ec6e97eaac4329e06cdfdf9a4f0d8a0347eeab
+353 −0

File changed.

Preview size limit exceeded, changes collapsed.

+89 −32
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 * * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

/**
 * @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>
@@ -32,62 +20,131 @@

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.
         * @param key The 64-bit key (only 56 bits are used, 8 bits are parity).
         *
         * 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.
         * @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.
         * @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 data block.
         * @param plaintext The 64-bit block to encrypt.
         * @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 data block.
         * @param ciphertext The 64-bit block to decrypt.
         * @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:
+62 −28
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 * * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

/**
 * @file rc4.h
 * @brief RC4 stream cipher.
 *
 * Declares netplus's implementation of the RC4 stream cipher: a
 * byte-oriented, key-scheduled pseudo-random keystream generator whose
 * output is XORed with data to encrypt or decrypt it (the operation is its
 * own inverse). Retained for interoperability with legacy protocols/formats
 * that still require it.
 *
 * @note RC4 has well-known statistical biases in its keystream and is
 *       considered cryptographically broken for general use; prefer
 *       AES-GCM (see aes.h) for anything requiring real security margin.
 *       This class exists purely for compatibility with legacy consumers.
 */
#include <vector>
#include <string>
#include <cstdint>
@@ -36,28 +26,58 @@
#define SBOX_SIZE 256

namespace netplus {
    /**
     * @brief Implements the RC4 stream cipher (key-scheduled XOR keystream generator).
     *
     * Holds the 256-byte internal permutation state (the "S-box") produced
     * by the Key-Scheduling Algorithm (KSA, see ksa()) and the two PRGA
     * indices used to step the keystream generator (see PRGA_byte()).
     * Because RC4 is a stateful stream cipher, an rc4 instance is
     * single-use per key/stream position: calling crypt() advances the
     * internal state, so it is not safe to call concurrently from multiple
     * threads on the same instance, and the same instance must not be
     * reused to encrypt two different messages under the same key/state
     * (see reset_prga_state() for restarting the keystream from the
     * post-KSA state).
     */
    class rc4 {
    private:
        // The internal state vector S (S-Box)
        // Holds a permutation of the numbers 0 to 255.
        /** @brief The 256-byte internal state vector (a permutation of 0..255), initialized by ksa() and mutated by every PRGA_byte() call. */
        uint8_t S[SBOX_SIZE];

        // Indices for the PRGA part (Pseudo-Random Generation Algorithm)
        /** @brief PRGA index `i`, advanced by one (mod 256) on every keystream byte. */
        int i_prga = 0;
        /** @brief PRGA index `j`, advanced pseudo-randomly (mod 256) based on the current state, on every keystream byte. */
        int j_prga = 0;

        // Helper function to swap two elements
        /** @brief Swaps two state-vector bytes in place. */
        void swap(uint8_t& a, uint8_t& b);

        /**
        * @brief Initializes the state vector S (Key-Scheduling Algorithm - KSA).
        *
        * Initializes S to the identity permutation, then scrambles it using
        * the key bytes (repeated cyclically if shorter than 256 bytes), per
        * the standard RC4 KSA. Also resets the PRGA indices to 0 via
        * reset_prga_state() so the instance is immediately ready to
        * generate keystream.
        * @param key The secret key.
        * @param key_len The length of the key.
        * @param key_len The length of the key in bytes; must be between 1 and 256 inclusive.
        * @throws std::invalid_argument if @p key_len is 0 or greater than 256.
        */
        void ksa(const uint8_t* key, size_t key_len); // ksa name is used here

        /**
        * @brief Generates a single keystream byte (Pseudo-Random Generation Algorithm - PRGA).
        *
        * Advances i_prga and j_prga, swaps the corresponding S-box entries,
        * and returns S[(S[i]+S[j]) mod 256] as the next keystream byte.
        * Mutates the internal state, so each call produces the next byte
        * in the keystream sequence.
        * @return The next keystream byte.
        */
        uint8_t PRGA_byte();
@@ -65,15 +85,24 @@ namespace netplus {
    public:
        /**
        * @brief Constructor, initializes the RC4 instance with a key.
        * @param key The secret key as a std::vector of bytes.
        *
        * Runs the Key-Scheduling Algorithm (ksa()) once to derive the
        * initial S-box permutation from @p key.
        * @param key The secret key as a std::vector of bytes; length must be between 1 and 256 bytes.
        * @throws std::invalid_argument if @p key is empty or longer than 256 bytes.
        */
        rc4(const std::vector<uint8_t>& key);

        /**
        * @brief Encrypts or decrypts data.
        * Since RC4 is a stream cipher and uses XOR, the function is symmetric.
        *
        * Each call consumes the next |data| bytes of keystream (advancing
        * the internal PRGA state), so calling crypt() repeatedly on the
        * same instance continues the keystream from where the previous
        * call left off rather than restarting it.
        * @param data The data to be encrypted/decrypted.
        * @return The encrypted/decrypted data.
        * @return The encrypted/decrypted data, the same length as @p data.
        */
        std::vector<uint8_t> crypt(const std::vector<uint8_t>& data);

@@ -81,6 +110,11 @@ namespace netplus {
        * @brief Resets the PRGA state to start a new encryption/decryption
        * with the same key and the same initialized S-box state.
        * The S-box itself remains preserved.
        *
        * Resets i_prga and j_prga to 0 without re-running the KSA, so the
        * S-box permutation as it stood at the time of the reset (whether
        * freshly keyed or already advanced by prior crypt() calls) becomes
        * the starting point for the next keystream.
        */
        void reset_prga_state();
    };