Loading agent-a054ebb63622e1044 @ f5ec6e97 Original line number Diff line number Diff line Subproject commit f5ec6e97eaac4329e06cdfdf9a4f0d8a0347eeab src/crypto/aes.h +353 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes src/crypto/des.h +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> Loading @@ -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: Loading src/crypto/rc4.h +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> Loading @@ -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(); Loading @@ -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); Loading @@ -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(); }; Loading Loading
agent-a054ebb63622e1044 @ f5ec6e97 Original line number Diff line number Diff line Subproject commit f5ec6e97eaac4329e06cdfdf9a4f0d8a0347eeab
src/crypto/des.h +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> Loading @@ -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: Loading
src/crypto/rc4.h +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> Loading @@ -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(); Loading @@ -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); Loading @@ -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(); }; Loading