chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:28 +08:00
commit 29cfe479ab
432 changed files with 68491 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
endif()
install(TARGETS ${testname} DESTINATION "bin/ciphers")
endforeach( testsourcefile ${APP_SOURCES} )
+162
View File
@@ -0,0 +1,162 @@
/**
* @file
* @brief Implementation of the [A1Z26
* cipher](https://www.dcode.fr/letter-number-cipher)
* @details The A1Z26 cipher is a simple substiution cipher where each letter is
* replaced by the number of the order they're in. For example, A corresponds to
* 1, B = 2, C = 3, etc.
*
* @author [Focusucof](https://github.com/Focusucof)
*/
#include <algorithm> /// for std::transform and std::replace
#include <cassert> /// for assert
#include <cstdint> /// for uint8_t
#include <iostream> /// for IO operations
#include <map> /// for std::map
#include <sstream> /// for std::stringstream
#include <string> /// for std::string
#include <vector> /// for std::vector
/**
* @namespace ciphers
* @brief Algorithms for encryption and decryption
*/
namespace ciphers {
/**
* @namespace a1z26
* @brief Functions for [A1Z26](https://www.dcode.fr/letter-number-cipher)
* encryption and decryption implementation
*/
namespace a1z26 {
std::map<uint8_t, char> a1z26_decrypt_map = {
{1, 'a'}, {2, 'b'}, {3, 'c'}, {4, 'd'}, {5, 'e'}, {6, 'f'}, {7, 'g'},
{8, 'h'}, {9, 'i'}, {10, 'j'}, {11, 'k'}, {12, 'l'}, {13, 'm'}, {14, 'n'},
{15, 'o'}, {16, 'p'}, {17, 'q'}, {18, 'r'}, {19, 's'}, {20, 't'}, {21, 'u'},
{22, 'v'}, {23, 'w'}, {24, 'x'}, {25, 'y'}, {26, 'z'},
};
std::map<char, uint8_t> a1z26_encrypt_map = {
{'a', 1}, {'b', 2}, {'c', 3}, {'d', 4}, {'e', 5}, {'f', 6}, {'g', 7},
{'h', 8}, {'i', 9}, {'j', 10}, {'k', 11}, {'l', 12}, {'m', 13}, {'n', 14},
{'o', 15}, {'p', 16}, {'q', 17}, {'r', 18}, {'s', 19}, {'t', 20}, {'u', 21},
{'v', 22}, {'w', 23}, {'x', 24}, {'y', 25}, {'z', 26}};
/**
* @brief a1z26 encryption implementation
* @param text is the plaintext input
* @returns encoded string with dashes to seperate letters
*/
std::string encrypt(std::string text) {
std::string result;
std::transform(text.begin(), text.end(), text.begin(),
::tolower); // convert string to lowercase
std::replace(text.begin(), text.end(), ':', ' ');
for (char letter : text) {
if (letter != ' ') {
result += std::to_string(
a1z26_encrypt_map[letter]); // convert int to string and append
// to result
result += "-"; // space out each set of numbers with spaces
} else {
result.pop_back();
result += ' ';
}
}
result.pop_back(); // remove leading dash
return result;
}
/**
* @brief a1z26 decryption implementation
* @param text is the encrypted text input
* @param bReturnUppercase is if the decoded string should be in uppercase or
* not
* @returns the decrypted string in all uppercase or all lowercase
*/
std::string decrypt(const std::string& text, bool bReturnUppercase = false) {
std::string result;
// split words seperated by spaces into a vector array
std::vector<std::string> word_array;
std::stringstream sstream(text);
std::string word;
while (sstream >> word) {
word_array.push_back(word);
}
for (auto& i : word_array) {
std::replace(i.begin(), i.end(), '-', ' ');
std::vector<std::string> text_array;
std::stringstream ss(i);
std::string res_text;
while (ss >> res_text) {
text_array.push_back(res_text);
}
for (auto& i : text_array) {
result += a1z26_decrypt_map[stoi(i)];
}
result += ' ';
}
result.pop_back(); // remove any leading whitespace
if (bReturnUppercase) {
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
}
return result;
}
} // namespace a1z26
} // namespace ciphers
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// 1st test
std::string input = "Hello World";
std::string expected = "8-5-12-12-15 23-15-18-12-4";
std::string output = ciphers::a1z26::encrypt(input);
std::cout << "Input: " << input << std::endl;
std::cout << "Expected: " << expected << std::endl;
std::cout << "Output: " << output << std::endl;
assert(output == expected);
std::cout << "TEST PASSED";
// 2nd test
input = "12-15-23-5-18-3-1-19-5";
expected = "lowercase";
output = ciphers::a1z26::decrypt(input);
std::cout << "Input: " << input << std::endl;
std::cout << "Expected: " << expected << std::endl;
std::cout << "Output: " << output << std::endl;
assert(output == expected);
std::cout << "TEST PASSED";
// 3rd test
input = "21-16-16-5-18-3-1-19-5";
expected = "UPPERCASE";
output = ciphers::a1z26::decrypt(input, true);
std::cout << "Input: " << input << std::endl;
std::cout << "Expected: " << expected << std::endl;
std::cout << "Output: " << output << std::endl;
assert(output == expected);
std::cout << "TEST PASSED";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+84
View File
@@ -0,0 +1,84 @@
/**
* @file
* @brief [Atbash Cipher](https://en.wikipedia.org/wiki/Atbash) implementation
* @details The Atbash cipher is a subsitution cipher where the letters of the
* alphabet are in reverse. For example, A is replaced with Z, B is replaced
* with Y, etc.
*
* ### Algorithm
* The algorithm takes a string, and looks up the corresponding reversed letter
* for each letter in the word and replaces it. Spaces are ignored and case is
* preserved.
*
* @author [Focusucof](https://github.com/Focusucof)
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include <map> /// for std::map
#include <string> /// for std::string
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** \namespace atbash
* \brief Functions for the [Atbash
* Cipher](https://en.wikipedia.org/wiki/Atbash) implementation
*/
namespace atbash {
std::map<char, char> atbash_cipher_map = {
{'a', 'z'}, {'b', 'y'}, {'c', 'x'}, {'d', 'w'}, {'e', 'v'}, {'f', 'u'},
{'g', 't'}, {'h', 's'}, {'i', 'r'}, {'j', 'q'}, {'k', 'p'}, {'l', 'o'},
{'m', 'n'}, {'n', 'm'}, {'o', 'l'}, {'p', 'k'}, {'q', 'j'}, {'r', 'i'},
{'s', 'h'}, {'t', 'g'}, {'u', 'f'}, {'v', 'e'}, {'w', 'd'}, {'x', 'c'},
{'y', 'b'}, {'z', 'a'}, {'A', 'Z'}, {'B', 'Y'}, {'C', 'X'}, {'D', 'W'},
{'E', 'V'}, {'F', 'U'}, {'G', 'T'}, {'H', 'S'}, {'I', 'R'}, {'J', 'Q'},
{'K', 'P'}, {'L', 'O'}, {'M', 'N'}, {'N', 'M'}, {'O', 'L'}, {'P', 'K'},
{'Q', 'J'}, {'R', 'I'}, {'S', 'H'}, {'T', 'G'}, {'U', 'F'}, {'V', 'E'},
{'W', 'D'}, {'X', 'C'}, {'Y', 'B'}, {'Z', 'A'}, {' ', ' '}
};
/**
* @brief atbash cipher encryption and decryption
* @param text Plaintext to be encrypted
* @returns encoded or decoded string
*/
std::string atbash_cipher(const std::string& text) {
std::string result;
for (char letter : text) {
result += atbash_cipher_map[letter];
}
return result;
}
} // namespace atbash
} // namespace ciphers
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// 1st test
std::string text = "Hello World";
std::string expected = "Svool Dliow";
std::string encrypted_text = ciphers::atbash::atbash_cipher(text);
std::string decrypted_text = ciphers::atbash::atbash_cipher(encrypted_text);
assert(expected == encrypted_text);
assert(text == decrypted_text);
std::cout << "Original text: " << text << std::endl;
std::cout << ", Expected text: " << expected << std::endl;
std::cout << ", Encrypted text: " << encrypted_text << std::endl;
std::cout << ", Decrypted text: " << decrypted_text << std::endl;
std::cout << "\nAll tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+200
View File
@@ -0,0 +1,200 @@
/**
* @brief [Base64 Encoding and
* Decoding](https://en.wikipedia.org/wiki/Base64)
* @details In programming, [Base64](https://en.wikipedia.org/wiki/Base64) is a
* group of binary-to-text encoding schemes that represent binary data (more
* specifically, a sequence of 8-bit bytes) in an ASCII string format by
* translating the data into a radix-64 representation. The term Base64
* originates from a specific MIME content transfer encoding. Each non-final
* Base64 digit represents exactly 6 bits of data. Three 8-bit bytes (i.e., a
* total of 24 bits) can therefore be represented by four 6-bit Base64
* digits.
* @author [Ashish Daulatabad](https://github.com/AshishYUO)
*/
#include <cassert> /// for `assert` operations
#include <cstdint>
#include <iostream> /// for IO operations
/**
* @namespace ciphers
* @brief Cipher algorithms
*/
namespace ciphers {
/**
* @namespace base64_encoding
* @brief Functions for [Base64 Encoding and
* Decoding](https://en.wikipedia.org/wiki/Base64) implementation.
*/
namespace base64_encoding {
// chars denoting the format for encoding and decoding array.
// This array is already decided by
// [RFC4648](https://tools.ietf.org/html/rfc4648#section-4) standard
const std::string chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @brief Base64 Encoder
* @details Converts the given string to Base64 equivalent.
* @param input Input as a string
* @returns Base64 encoded string
*/
std::string base64_encode(const std::string &input) {
std::string base64_string; /// Output of this function: base64 string
// base64 deals with 6-bit chars encoded as per chars, so
// we will always filter 6-bits from input.
for (uint32_t i = 0; i < input.size(); i += 3) {
char first_byte = input[i]; /// First byte of the iteration
// Take first six bits of first character.
// Encode the first six bits with character defined in string `chars`
base64_string.push_back(chars[first_byte >> 2]);
if (i + 1 < input.size()) {
char second_byte = input[i + 1]; /// Second byte of the iteration
// Take remaining two bits of first character, and four first bits
// from second character Combine two numbers as 6-bit digits and
// encode by array chars (first two bits of first byte and next four
// of second byte)
base64_string.push_back(
chars[(((first_byte & 3) << 4) | ((second_byte & 0xF0) >> 4))]);
if (i + 2 < input.size()) {
char third_byte = input[i + 2]; /// Third byte of the iteration
// Take remaining four bits of second character, and first two
// bits from third character Combine two numbers as 6-bit digits
// and encode by array chars (remaining four bits of second byte
// and first two of third byte)
base64_string.push_back(chars[((third_byte & 0xC0) >> 6) |
((second_byte & 0x0F) << 2)]);
// Encode remaining 6-bit of third byte by array chars
base64_string.push_back(chars[(third_byte & 0x3F)]);
} else {
// Take remaining four bits of second character as 6-bit number
base64_string.push_back(chars[((second_byte & 0x0F) << 2)]);
base64_string.push_back('='); // padding characters
}
} else {
// Take remaining two bits of first character as 6-bit number
base64_string.push_back(chars[((first_byte & 3) << 4)]);
base64_string.push_back('='); // padding characters
base64_string.push_back('='); // padding characters
}
}
return base64_string;
}
/**
* @brief Utility function for finding index
* @details Utility function for finding the position of a character in array
* `chars`
* @param c character to search in array `chars`
* @returns integer denoting position of character in array `chars`
*/
uint8_t find_idx(const char c) {
if (c >= 'A' && c <= 'Z') {
return c - 'A';
} else if (c >= 'a' && c <= 'z') {
return c - 'a' + 26;
} else if (c >= '0' && c <= '9') {
return c - '0' + 52;
} else if (c == '+') {
return 62;
} else if (c == '/') {
return 63;
}
return -1;
}
/**
* @brief Base64 Decoder
* @details Decodes the Base64 string
* @param base64_str Input as a Base64 string
* @returns Base64 decoded string
*/
std::string base64_decode(const std::string &base64_str) {
std::string
base64_decoded; /// Output of this function: base64 decoded string
for (uint32_t i = 0; i < base64_str.size(); i += 4) {
/// First 6-bit representation of Base64
char first_byte = base64_str[i];
/// Second 6-bit representation of Base64
char second_byte = base64_str[i + 1];
// Actual str characters are of 8 bits (or 1 byte):
// :: 8 bits are decode by taking 6 bits from 1st byte of base64 string
// and first 2 bits from 2nd byte of base64 string.
char first_actual_byte = static_cast<char>(
(find_idx(first_byte) << 2) | ((find_idx(second_byte)) >> 4));
base64_decoded.push_back(first_actual_byte);
if (i + 2 < base64_str.size() && base64_str[i + 2] != '=') {
/// Third 6-bit representation of Base64
char third_byte = base64_str[i + 2];
// :: Next 8 bits are decode by taking remaining 4 bits from 2nd
// byte of base64 string and first 4 bits from 3rd byte of base64
// string.
char second_actual_byte =
static_cast<char>(((find_idx(second_byte) & 0x0F) << 4) |
(find_idx(third_byte) >> 2));
base64_decoded.push_back(second_actual_byte);
if (i + 3 < base64_str.size() && base64_str[i + 3] != '=') {
/// Fourth 6-bit representation of Base64 string
char fourth_byte = base64_str[i + 3];
// :: Taking remaining 2 bits from 3rd byte of base64 string
// and all 6 bits from 4th byte of base64 string.
char third_actual_byte =
static_cast<char>(((find_idx(third_byte) & 0x03) << 6) |
find_idx(fourth_byte));
base64_decoded.push_back(third_actual_byte);
}
}
}
return base64_decoded;
}
} // namespace base64_encoding
} // namespace ciphers
/**
* @brief Self test-implementations
* @returns void
*/
static void test() {
// 1st Test
std::string str =
"To err is human, but to really foul things up you need a computer.";
std::string base64_str = ciphers::base64_encoding::base64_encode(str);
std::string verify =
"VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIHlvdSBuZW"
"VkIGEgY29tcHV0ZXIu";
// verify encoding
assert(base64_str == verify);
std::string original_str =
ciphers::base64_encoding::base64_decode(base64_str);
// verify decoding
assert(original_str == str);
// 2nd Test from [Wikipedia](https://en.wikipedia.org/wiki/Base64)
str =
"Man is distinguished, not only by his reason, but by this singular "
"passion from other animals, which is a lust of the mind, that by a "
"perseverance of delight in the continued and indefatigable generation "
"of knowledge, exceeds the short vehemence of any carnal pleasure.";
base64_str = ciphers::base64_encoding::base64_encode(str);
verify =
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS"
"B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBh"
"IGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodC"
"BpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25v"
"d2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbG"
"Vhc3VyZS4=";
// verify encoding
assert(base64_str == verify);
original_str = ciphers::base64_encoding::base64_decode(base64_str);
// verify decoding
assert(original_str == str);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+124
View File
@@ -0,0 +1,124 @@
/**
* @file caesar_cipher.cpp
* @brief Implementation of [Caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher) algorithm.
*
* @details
* In cryptography, a Caesar cipher, also known as Caesar's cipher, the shift cipher,
* Caesar's code or Caesar shift, is one of the simplest and most widely known encryption
* techniques. It is a type of substitution cipher in which each letter in the plaintext
* is replaced by a letter some fixed number of positions down the alphabet. For example,
* with a left shift of 3, D would be replaced by A, E would become B, and so on.
* The method is named after Julius Caesar, who used it in his private correspondence.
*
* ### Algorithm
* The encryption can also be represented using modular arithmetic by first transforming
* the letters into numbers, according to the scheme, A → 0, B → 1, ..., Z → 25.
* Encryption of a letter x by a shift n can be described mathematically as,
* \f[ E(x) = (x + n)\;\mbox{mod}\; 26\f]
* while decryption can be described as,
* \f[ D(x) = (x - n) \;\mbox{mod}\; 26\f]
*
* \note This program implements caesar cipher for only uppercase English alphabet characters (i.e. A-Z).
*
* @author [Deep Raval](https://github.com/imdeep2905)
*/
#include <iostream>
#include <string>
#include <cassert>
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** \namespace caesar
* \brief Functions for [Caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher) algorithm.
*/
namespace caesar {
namespace {
/**
* This function finds character for given value (i.e.A-Z)
* @param x value for which we want character
* @returns corresponding character for perticular value
*/
inline char get_char(const int x) {
// By adding 65 we are scaling 0-25 to 65-90.
// Which are in fact ASCII values of A-Z.
return char(x + 65);
}
/**
* This function finds value for given character (i.e.0-25)
* @param c character for which we want value
* @returns returns corresponding value for perticular character
*/
inline int get_value(const char c) {
// A-Z have ASCII values in range 65-90.
// Hence subtracting 65 will scale them to 0-25.
return int(c - 65);
}
} // Unnamed namespace
/**
* Encrypt given text using caesar cipher.
* @param text text to be encrypted
* @param shift number of shifts to be applied
* @returns new encrypted text
*/
std::string encrypt (const std::string &text, const int &shift) {
std::string encrypted_text = ""; // Empty string to store encrypted text
for (char c : text) { // Going through each character
int place_value = get_value(c); // Getting value of character (i.e. 0-25)
place_value = (place_value + shift) % 26; // Applying encryption formula
char new_char = get_char(place_value); // Getting new character from new value (i.e. A-Z)
encrypted_text += new_char; // Appending encrypted character
}
return encrypted_text; // Returning encrypted text
}
/**
* Decrypt given text using caesar cipher.
* @param text text to be decrypted
* @param shift number of shifts to be applied
* @returns new decrypted text
*/
std::string decrypt (const std::string &text, const int &shift) {
std::string decrypted_text = ""; // Empty string to store decrypted text
for (char c : text) { // Going through each character
int place_value = get_value(c); // Getting value of character (i.e. 0-25)
place_value = (place_value - shift) % 26;// Applying decryption formula
if(place_value < 0) { // Handling case where remainder is negative
place_value = place_value + 26;
}
char new_char = get_char(place_value); // Getting original character from decrypted value (i.e. A-Z)
decrypted_text += new_char; // Appending decrypted character
}
return decrypted_text; // Returning decrypted text
}
} // namespace caesar
} // namespace ciphers
/**
* Function to test above algorithm
*/
void test() {
// Test 1
std::string text1 = "ALANTURING";
std::string encrypted1 = ciphers::caesar::encrypt(text1, 17);
std::string decrypted1 = ciphers::caesar::decrypt(encrypted1, 17);
assert(text1 == decrypted1);
std::cout << "Original text : " << text1;
std::cout << " , Encrypted text (with shift = 21) : " << encrypted1;
std::cout << " , Decrypted text : "<< decrypted1 << std::endl;
// Test 2
std::string text2 = "HELLOWORLD";
std::string encrypted2 = ciphers::caesar::encrypt(text2, 1729);
std::string decrypted2 = ciphers::caesar::decrypt(encrypted2, 1729);
assert(text2 == decrypted2);
std::cout << "Original text : " << text2;
std::cout << " , Encrypted text (with shift = 1729) : " << encrypted2;
std::cout << " , Decrypted text : "<< decrypted2 << std::endl;
}
/** Driver Code */
int main() {
// Testing
test();
return 0;
}
+325
View File
@@ -0,0 +1,325 @@
/**
* @file
* @brief Implementation of [Elliptic Curve Diffie Hellman Key
* Exchange](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange).
*
* @details
* The ECDH (Elliptic Curve DiffieHellman Key Exchange) is anonymous key
* agreement scheme, which allows two parties, each having an elliptic-curve
* publicprivate key pair, to establish a shared secret over an insecure
* channel.
* ECDH is very similar to the classical DHKE (DiffieHellman Key Exchange)
* algorithm, but it uses ECC point multiplication instead of modular
* exponentiations. ECDH is based on the following property of EC points:
* (a * G) * b = (b * G) * a
* If we have two secret numbers a and b (two private keys, belonging to Alice
* and Bob) and an ECC elliptic curve with generator point G, we can exchange
* over an insecure channel the values (a * G) and (b * G) (the public keys of
* Alice and Bob) and then we can derive a shared secret:
* secret = (a * G) * b = (b * G) * a.
* Pretty simple. The above equation takes the following form:
* alicePubKey * bobPrivKey = bobPubKey * alicePrivKey = secret
* @author [Ashish Daulatabad](https://github.com/AshishYUO)
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include "uint256_t.hpp" /// for 256-bit integer
/**
* @namespace ciphers
* @brief Cipher algorithms
*/
namespace ciphers {
/**
* @brief namespace elliptic_curve_key_exchange
* @details Demonstration of [Elliptic Curve
* Diffie-Hellman](https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange)
* key exchange.
*/
namespace elliptic_curve_key_exchange {
/**
* @brief Definition of struct Point
* @details Definition of Point in the curve.
*/
typedef struct Point {
uint256_t x, y; /// x and y co-ordinates
/**
* @brief operator == for Point
* @details check whether co-ordinates are equal to the given point
* @param p given point to be checked with this
* @returns true if x and y are both equal with Point p, else false
*/
inline bool operator==(const Point &p) { return x == p.x && y == p.y; }
/**
* @brief ostream operator for printing Point
* @param op ostream operator
* @param p Point to be printed on console
* @returns op, the ostream object
*/
friend std::ostream &operator<<(std::ostream &op, const Point &p) {
op << p.x << " " << p.y;
return op;
}
} Point;
/**
* @brief This function calculates number raised to exponent power under modulo
* mod using [Modular
* Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp).
* @param number integer base
* @param power unsigned integer exponent
* @param mod integer modulo
* @return number raised to power modulo mod
*/
uint256_t exp(uint256_t number, uint256_t power, const uint256_t &mod) {
if (!power) {
return uint256_t(1);
}
uint256_t ans(1);
number = number % mod;
while (power) {
if ((power & 1)) {
ans = (ans * number) % mod;
}
power >>= 1;
if (power) {
number = (number * number) % mod;
}
}
return ans;
}
/**
* @brief Addition of points
* @details Add given point to generate third point. More description can be
* found
* [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition),
* and
* [here](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_doubling)
* @param a First point
* @param b Second point
* @param curve_a_coeff Coefficient `a` of the given curve (y^2 = x^3 + ax + b)
* % mod
* @param mod Given field
* @return the resultant point
*/
Point addition(Point a, Point b, const uint256_t &curve_a_coeff,
uint256_t mod) {
uint256_t lambda(0); /// Slope
uint256_t zero(0); /// value zero
lambda = zero = 0;
uint256_t inf = ~zero;
if (a.x != b.x || a.y != b.y) {
// Slope being infinite.
if (b.x == a.x) {
return {inf, inf};
}
uint256_t num = (b.y - a.y + mod), den = (b.x - a.x + mod);
lambda = (num * (exp(den, mod - 2, mod))) % mod;
} else {
/**
* slope when the line is tangent to curve. This operation is performed
* while doubling. Taking derivative of `y^2 = x^3 + ax + b`
* => `2y dy = (3 * x^2 + a)dx`
* => `(dy/dx) = (3x^2 + a)/(2y)`
*/
/**
* if y co-ordinate is zero, the slope is infinite, return inf.
* else calculate the slope (here % mod and store in lambda)
*/
if (!a.y) {
return {inf, inf};
}
uint256_t axsq = ((a.x * a.x)) % mod;
// Mulitply by 3 adjustment
axsq += (axsq << 1);
axsq %= mod;
// Mulitply by 2 adjustment
uint256_t a_2 = (a.y << 1);
lambda =
(((axsq + curve_a_coeff) % mod) * exp(a_2, mod - 2, mod)) % mod;
}
Point c;
// new point: x = ((lambda^2) - x1 - x2)
// y = (lambda * (x1 - x)) - y1
c.x = ((lambda * lambda) % mod + (mod << 1) - a.x - b.x) % mod;
c.y = (((lambda * (a.x + mod - c.x)) % mod) + mod - a.y) % mod;
return c;
}
/**
* @brief multiply Point and integer
* @details Multiply Point by a scalar factor (here it is a private key p). The
* multiplication is called as [double and add
* method](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add)
* @param a Point to multiply
* @param curve_a_coeff Coefficient of given curve (y^2 = x^3 + ax + b) % mod
* @param p The scalar value
* @param mod Given field
* @returns the resultant point
*/
Point multiply(const Point &a, const uint256_t &curve_a_coeff, uint256_t p,
const uint256_t &mod) {
Point N = a;
N.x %= mod;
N.y %= mod;
uint256_t inf{};
inf = ~uint256_t(0);
Point Q = {inf, inf};
while (p) {
if ((p & 1)) {
if (Q.x == inf && Q.y == inf) {
Q.x = N.x;
Q.y = N.y;
} else {
Q = addition(Q, N, curve_a_coeff, mod);
}
}
p >>= 1;
if (p) {
N = addition(N, N, curve_a_coeff, mod);
}
}
return Q;
}
} // namespace elliptic_curve_key_exchange
} // namespace ciphers
/**
* @brief Function to test the
* uint128_t header
* @returns void
*/
static void uint128_t_tests() {
// 1st test: Operations test
uint128_t a("122"), b("2312");
assert(a + b == 2434);
assert(b - a == 2190);
assert(a * b == 282064);
assert(b / a == 18);
assert(b % a == 116);
assert((a & b) == 8);
assert((a | b) == 2426);
assert((a ^ b) == 2418);
assert((a << 64) == uint128_t("2250502776992565297152"));
assert((b >> 7) == 18);
// 2nd test: Operations test
a = uint128_t("12321421424232142122");
b = uint128_t("23123212");
assert(a + b == uint128_t("12321421424255265334"));
assert(a - b == uint128_t("12321421424209018910"));
assert(a * b == uint128_t("284910839733861759501135864"));
assert(a / b == 532859423865LL);
assert(a % b == 3887742);
assert((a & b) == 18912520);
assert((a | b) == uint128_t("12321421424236352814"));
assert((a ^ b) == uint128_t("12321421424217440294"));
assert((a << 64) == uint128_t("227290107637132170748078080907806769152"));
}
/**
* @brief Function to test the
* uint256_t header
* @returns void
*/
static void uint256_t_tests() {
// 1st test: Operations test
uint256_t a("122"), b("2312");
assert(a + b == 2434);
assert(b - a == 2190);
assert(a * b == 282064);
assert(b / a == 18);
assert(b % a == 116);
assert((a & b) == 8);
assert((a | b) == 2426);
assert((a ^ b) == 2418);
assert((a << 64) == uint256_t("2250502776992565297152"));
assert((b >> 7) == 18);
// 2nd test: Operations test
a = uint256_t("12321423124513251424232142122");
b = uint256_t("23124312431243243215354315132413213212");
assert(a + b == uint256_t("23124312443564666339867566556645355334"));
// Since a < b, the value is greater
assert(a - b == uint256_t("115792089237316195423570985008687907853246860353"
"221642219366742944204948568846"));
assert(a * b == uint256_t("284924437928789743312147393953938013677909398222"
"169728183872115864"));
assert(b / a == uint256_t("1876756621"));
assert(b % a == uint256_t("2170491202688962563936723450"));
assert((a & b) == uint256_t("3553901085693256462344"));
assert((a | b) == uint256_t("23124312443564662785966480863388892990"));
assert((a ^ b) == uint256_t("23124312443564659232065395170132430646"));
assert((a << 128) == uint256_t("4192763024643754272961909047609369343091683"
"376561852756163540549632"));
}
/**
* @brief Function to test the
* provided algorithm above
* @returns void
*/
static void test() {
// demonstration of key exchange using curve secp112r1
// Equation of the form y^2 = (x^3 + ax + b) % P (here p is mod)
uint256_t a("4451685225093714772084598273548424"),
b("2061118396808653202902996166388514"),
mod("4451685225093714772084598273548427");
// Generator value: is pre-defined for the given curve
ciphers::elliptic_curve_key_exchange::Point ptr = {
uint256_t("188281465057972534892223778713752"),
uint256_t("3419875491033170827167861896082688")};
// Shared key generation.
// For alice
std::cout << "For alice:\n";
// Alice's private key (can be generated randomly)
uint256_t alice_private_key("164330438812053169644452143505618");
ciphers::elliptic_curve_key_exchange::Point alice_public_key =
multiply(ptr, a, alice_private_key, mod);
std::cout << "\tPrivate key: " << alice_private_key << "\n";
std::cout << "\tPublic Key: " << alice_public_key << std::endl;
// For Bob
std::cout << "For Bob:\n";
// Bob's private key (can be generated randomly)
uint256_t bob_private_key("1959473333748537081510525763478373");
ciphers::elliptic_curve_key_exchange::Point bob_public_key =
multiply(ptr, a, bob_private_key, mod);
std::cout << "\tPrivate key: " << bob_private_key << "\n";
std::cout << "\tPublic Key: " << bob_public_key << std::endl;
// After public key exchange, create a shared key for communication.
// create shared key:
ciphers::elliptic_curve_key_exchange::Point alice_shared_key = multiply(
bob_public_key, a,
alice_private_key, mod),
bob_shared_key = multiply(
alice_public_key, a,
bob_private_key, mod);
std::cout << "Shared keys:\n";
std::cout << alice_shared_key << std::endl;
std::cout << bob_shared_key << std::endl;
// Check whether shared keys are equal
assert(alice_shared_key == bob_shared_key);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
uint128_t_tests(); // running predefined 128-bit unsigned integer tests
uint256_t_tests(); // running predefined 256-bit unsigned integer tests
test(); // running self-test implementations
return 0;
}
+543
View File
@@ -0,0 +1,543 @@
/**
* @file hill_cipher.cpp
* @brief Implementation of [Hill
* cipher](https://en.wikipedia.org/wiki/Hill_cipher) algorithm.
*
* Program to generate the encryption-decryption key and perform encryption and
* decryption of ASCII text using the famous block cipher algorithm. This is a
* powerful encryption algorithm that is relatively easy to implement with a
* given key. The strength of the algorithm depends on the size of the block
* encryption matrix key; the bigger the matrix, the stronger the encryption and
* more difficult to break it. However, the important requirement for the matrix
* is that:
* 1. matrix should be invertible - all inversion conditions should be satisfied
* and
* 2. its determinant must not have any common factors with the length of
* character set
* Due to this restriction, most implementations only implement with small 3x3
* encryption keys and a small subset of ASCII alphabets.
*
* In the current implementation, I present to you an implementation for
* generating larger encryption keys (I have attempted upto 10x10) and an ASCII
* character set of 97 printable characters. Hence, a typical ASCII text file
* could be easily encrypted with the module. The larger character set increases
* the modulo of cipher and hence the matrix determinants can get very large
* very quickly rendering them ill-defined.
*
* \note This program uses determinant computation using LU decomposition from
* the file lu_decomposition.h
* \note The matrix generation algorithm is very rudimentary and does not
* guarantee an invertible modulus matrix. \todo Better matrix generation
* algorithm.
*
* @author [Krishna Vedala](https://github.com/kvedala)
*/
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#ifdef _OPENMP
#include <omp.h>
#endif
#include "../numerical_methods/lu_decomposition.h"
/**
* operator to print a matrix
*/
template <typename T>
static std::ostream &operator<<(std::ostream &out, matrix<T> const &v) {
const int width = 15;
const char separator = ' ';
for (size_t row = 0; row < v.size(); row++) {
for (size_t col = 0; col < v[row].size(); col++)
out << std::left << std::setw(width) << std::setfill(separator)
<< v[row][col];
out << std::endl;
}
return out;
}
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** dictionary of characters that can be encrypted and decrypted */
static const char *STRKEY =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&"
"*()_+`-=[]{}|;':\",./<>?\\\r\n \0";
/**
* @brief Implementation of [Hill
* Cipher](https://en.wikipedia.org/wiki/Hill_cipher) algorithm
*/
class HillCipher {
private:
/**
* @brief Function to generate a random integer in a given interval
*
* @param a lower limit of interval
* @param b upper limit of interval
* @tparam T type of output
* @return random integer in the interval \f$[a,b)\f$
*/
template <typename T1, typename T2>
static const T2 rand_range(T1 a, T1 b) {
// generate random number between 0 and 1
long double r = static_cast<long double>(std::rand()) / RAND_MAX;
// scale and return random number as integer
return static_cast<T2>(r * (b - a) + a);
}
/**
* @brief Function overload to fill a matrix with random integers in a given
* interval
*
* @param M pointer to matrix to be filled with random numbers
* @param a lower limit of interval
* @param b upper limit of interval
* @tparam T1 type of input range
* @tparam T2 type of matrix
* @return determinant of generated random matrix
*
* @warning There will need to be a balance between the matrix size and the
* range of random numbers. If the matrix is large, the range of random
* numbers must be small to have a well defined keys. Or if the matrix is
* smaller, the random numbers range can be larger. For an 8x8 matrix, range
* should be no more than \f$[0,10]\f$
*/
template <typename T1, typename T2>
static double rand_range(matrix<T2> *M, T1 a, T1 b) {
for (size_t i = 0; i < M->size(); i++) {
for (size_t j = 0; j < M[0][0].size(); j++) {
M[0][i][j] = rand_range<T1, T2>(a, b);
}
}
return determinant_lu(*M);
}
/**
* @brief Compute
* [GCD](https://en.wikipedia.org/wiki/Greatest_common_divisor) of two
* integers using Euler's algorithm
*
* @param a first number
* @param b second number
* @return GCD of \f$a\f$ and \f$b\f$
*/
template <typename T>
static const T gcd(T a, T b) {
if (b > a) // ensure always a < b
std::swap(a, b);
while (b != 0) {
T tmp = b;
b = a % b;
a = tmp;
}
return a;
}
/**
* @brief helper function to perform vector multiplication with encryption
* or decryption matrix
*
* @param vector vector to multiply
* @param key encryption or decryption key matrix
* @return corresponding encrypted or decrypted text
*/
static const std::valarray<uint8_t> mat_mul(
const std::valarray<uint8_t> &vector, const matrix<int> &key) {
std::valarray<uint8_t> out(vector); // make a copy
size_t L = std::strlen(STRKEY);
for (size_t i = 0; i < key.size(); i++) {
int tmp = 0;
for (size_t j = 0; j < vector.size(); j++) {
tmp += key[i][j] * vector[j];
}
out[i] = static_cast<uint8_t>(tmp % L);
}
return out;
}
/**
* @brief Get the character at a given index in the ::STRKEY
*
* @param idx index value
* @return character at the index
*/
static inline char get_idx_char(const uint8_t idx) { return STRKEY[idx]; }
/**
* @brief Get the index of a character in the ::STRKEY
*
* @param ch character to search
* @return index of character
*/
static inline uint8_t get_char_idx(const char ch) {
size_t L = std::strlen(STRKEY);
for (size_t idx = 0; idx <= L; idx++)
if (STRKEY[idx] == ch)
return idx;
std::cerr << __func__ << ":" << __LINE__ << ": (" << ch
<< ") Should not reach here!\n";
return 0;
}
/**
* @brief Convenience function to perform block cipher operations. The
* operations are identical for both encryption and decryption.
*
* @param text input text to encrypt or decrypt
* @param key key for encryption or decryption
* @return encrypted/decrypted output
*/
static const std::string codec(const std::string &text,
const matrix<int> &key) {
size_t text_len = text.length();
size_t key_len = key.size();
// length of output string must be a multiple of key_len
// create output string and initialize with '\0' character
size_t L2 = text_len % key_len == 0
? text_len
: text_len + key_len - (text_len % key_len);
std::string coded_text(L2, '\0');
// temporary array for batch processing
int i;
#ifdef _OPENMP
#pragma parallel omp for private(i)
#endif
for (i = 0; i < L2 - key_len + 1; i += key_len) {
std::valarray<uint8_t> batch_int(key_len);
for (size_t j = 0; j < key_len; j++) {
batch_int[j] = get_char_idx(text[i + j]);
}
batch_int = mat_mul(batch_int, key);
for (size_t j = 0; j < key_len; j++) {
coded_text[i + j] =
STRKEY[batch_int[j]]; // get character at key
}
}
return coded_text;
}
/**
* Get matrix inverse using Row-transformations. Given matrix must
* be a square and non-singular.
* \returns inverse matrix
**/
template <typename T>
static matrix<double> get_inverse(matrix<T> const &A) {
// Assuming A is square matrix
size_t N = A.size();
matrix<double> inverse(N, std::valarray<double>(N));
for (size_t row = 0; row < N; row++) {
for (size_t col = 0; col < N; col++) {
// create identity matrix
inverse[row][col] = (row == col) ? 1.f : 0.f;
}
}
if (A.size() != A[0].size()) {
std::cerr << "A must be a square matrix!" << std::endl;
return inverse;
}
// preallocate a temporary matrix identical to A
matrix<double> temp(N, std::valarray<double>(N));
for (size_t row = 0; row < N; row++) {
for (size_t col = 0; col < N; col++)
temp[row][col] = static_cast<double>(A[row][col]);
}
// start transformations
for (size_t row = 0; row < N; row++) {
for (size_t row2 = row; row2 < N && temp[row][row] == 0; row2++) {
// this to ensure diagonal elements are not 0
temp[row] = temp[row] + temp[row2];
inverse[row] = inverse[row] + inverse[row2];
}
for (size_t col2 = row; col2 < N && temp[row][row] == 0; col2++) {
// this to further ensure diagonal elements are not 0
for (size_t row2 = 0; row2 < N; row2++) {
temp[row2][row] = temp[row2][row] + temp[row2][col2];
inverse[row2][row] =
inverse[row2][row] + inverse[row2][col2];
}
}
if (temp[row][row] == 0) {
// Probably a low-rank matrix and hence singular
std::cerr << "Low-rank matrix, no inverse!" << std::endl;
return inverse;
}
// set diagonal to 1
double divisor = temp[row][row];
temp[row] = temp[row] / divisor;
inverse[row] = inverse[row] / divisor;
// Row transformations
for (size_t row2 = 0; row2 < N; row2++) {
if (row2 == row)
continue;
double factor = temp[row2][row];
temp[row2] = temp[row2] - factor * temp[row];
inverse[row2] = inverse[row2] - factor * inverse[row];
}
}
return inverse;
}
static int modulo(int a, int b) {
int ret = a % b;
if (ret < 0)
ret += b;
return ret;
}
public:
/**
* @brief Generate encryption matrix of a given size. Larger size matrices
* are difficult to generate but provide more security. Important conditions
* are:
* 1. matrix should be invertible
* 2. determinant must not have any common factors with the length of
* character key
* There is no head-fast way to generate hte matrix under the given
* numerical restrictions of the machine but the conditions added achieve
* the goals. Bigger the matrix, greater is the probability of the matrix
* being ill-defined.
*
* @param size size of matrix (typically \f$\text{size}\le10\f$)
* @param limit1 lower limit of range of random elements (default=0)
* @param limit2 upper limit of range of random elements (default=10)
* @return Encryption martix
*/
static matrix<int> generate_encryption_key(size_t size, int limit1 = 0,
int limit2 = 10) {
matrix<int> encrypt_key(size, std::valarray<int>(size));
matrix<int> min_mat = encrypt_key;
int mat_determinant = -1; // because matrix has only ints, the
// determinant will also be an int
int L = std::strlen(STRKEY);
double dd;
do {
// keeping the random number range smaller generates better
// defined matrices with more ease of cracking
dd = rand_range(&encrypt_key, limit1, limit2);
mat_determinant = static_cast<int>(dd);
if (mat_determinant < 0)
mat_determinant = (mat_determinant % L);
} while (std::abs(dd) > 1e3 || // while ill-defined
dd < 0.1 || // while singular or negative determinant
!std::isfinite(dd) || // while determinant is not finite
gcd(mat_determinant, L) != 1); // while no common factors
// std::cout <<
return encrypt_key;
}
/**
* @brief Generate decryption matrix from an encryption matrix key.
*
* @param encrypt_key encryption key for which to create a decrypt key
* @return Decryption martix
*/
static matrix<int> generate_decryption_key(matrix<int> const &encrypt_key) {
size_t size = encrypt_key.size();
int L = std::strlen(STRKEY);
matrix<int> decrypt_key(size, std::valarray<int>(size));
int det_encrypt = static_cast<int>(determinant_lu(encrypt_key));
int mat_determinant = det_encrypt < 0 ? det_encrypt % L : det_encrypt;
matrix<double> tmp_inverse = get_inverse(encrypt_key);
// find co-prime factor for inversion
int det_inv = -1;
for (int i = 0; i < L; i++) {
if (modulo(mat_determinant * i, L) == 1) {
det_inv = i;
break;
}
}
if (det_inv == -1) {
std::cerr << "Could not find a co-prime for inversion\n";
std::exit(EXIT_FAILURE);
}
mat_determinant = det_inv * det_encrypt;
// perform modular inverse of encryption matrix
int i;
#ifdef _OPENMP
#pragma parallel omp for private(i)
#endif
for (i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int temp = std::round(tmp_inverse[i][j] * mat_determinant);
decrypt_key[i][j] = modulo(temp, L);
}
}
return decrypt_key;
}
/**
* @brief Generate encryption and decryption key pair
*
* @param size size of matrix key (typically \f$\text{size}\le10\f$)
* @param limit1 lower limit of range of random elements (default=0)
* @param limit2 upper limit of range of random elements (default=10)
* @return std::pair<matrix<int>, matrix<int>> encryption and decryption
* keys as a pair
*
* @see ::generate_encryption_key
*/
static std::pair<matrix<int>, matrix<int>> generate_keys(size_t size,
int limit1 = 0,
int limit2 = 10) {
matrix<int> encrypt_key = generate_encryption_key(size);
matrix<int> decrypt_key = generate_decryption_key(encrypt_key);
double det2 = determinant_lu(decrypt_key);
while (std::abs(det2) < 0.1 || std::abs(det2) > 1e3) {
encrypt_key = generate_encryption_key(size, limit1, limit2);
decrypt_key = generate_decryption_key(encrypt_key);
det2 = determinant_lu(decrypt_key);
}
return std::make_pair(encrypt_key, decrypt_key);
}
/**
* @brief Encrypt a given text using a given key
*
* @param text string to encrypt
* @param encrypt_key key for encryption
* @return encrypted text
*/
static const std::string encrypt_text(const std::string &text,
const matrix<int> &encrypt_key) {
return codec(text, encrypt_key);
}
/**
* @brief Decrypt a given text using a given key
*
* @param text string to decrypt
* @param decrypt_key key for decryption
* @return decrypted text
*/
static const std::string decrypt_text(const std::string &text,
const matrix<int> &decrypt_key) {
return codec(text, decrypt_key);
}
};
} // namespace ciphers
/**
* @brief Self test 1 - using 3x3 randomly generated key
*
* @param text string to encrypt and decrypt
*/
void test1(const std::string &text) {
// std::string text = "Hello world!";
std::cout << "======Test 1 (3x3 key) ======\nOriginal text:\n\t" << text
<< std::endl;
std::pair<matrix<int>, matrix<int>> p =
ciphers::HillCipher::generate_keys(3, 0, 100);
matrix<int> ekey = p.first;
matrix<int> dkey = p.second;
// matrix<int> ekey = {{22, 28, 25}, {5, 26, 15}, {14, 18, 9}};
// std::cout << "Encryption key: \n" << ekey;
std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
// matrix<int> dkey = ciphers::HillCipher::generate_decryption_key(ekey);
// std::cout << "Decryption key: \n" << dkey;
std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
std::ofstream out_file("hill_cipher_test1.txt");
out_file << "Block size: " << ekey.size() << "\n";
out_file << "Encryption Key:\n" << ekey;
out_file << "\nDecryption Key:\n" << dkey;
out_file.close();
assert(txt_back == text);
std::cout << "Passed :)\n";
}
/**
* @brief Self test 2 - using 8x8 randomly generated key
*
* @param text string to encrypt and decrypt
*/
void test2(const std::string &text) {
// std::string text = "Hello world!";
std::cout << "======Test 2 (8x8 key) ======\nOriginal text:\n\t" << text
<< std::endl;
std::pair<matrix<int>, matrix<int>> p =
ciphers::HillCipher::generate_keys(8, 0, 3);
matrix<int> ekey = p.first;
matrix<int> dkey = p.second;
std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
std::ofstream out_file("hill_cipher_test2.txt");
out_file << "Block size: " << ekey.size() << "\n";
out_file << "Encryption Key:\n" << ekey;
out_file << "\nDecryption Key:\n" << dkey;
out_file.close();
assert(txt_back.compare(0, text.size(), text) == 0);
std::cout << "Passed :)\n";
}
/** Main function */
int main() {
std::srand(std::time(nullptr));
std::cout << "Key dictionary: (" << std::strlen(ciphers::STRKEY) << ")\n\t"
<< ciphers::STRKEY << "\n";
std::string text = "This is a simple text with numb3r5 and exclamat!0n.";
test1(text);
test2(text);
return 0;
}
+272
View File
@@ -0,0 +1,272 @@
/**
* @file
* @author [Deep Raval](https://github.com/imdeep2905)
*
* @brief Implementation of [Morse Code]
* (https://en.wikipedia.org/wiki/Morse_code).
*
* @details
* Morse code is a method used in telecommunication to encode text characters
* as standardized sequences of two different signal durations, called dots
* and dashes or dits and dahs. Morse code is named after Samuel Morse, an
* inventor of the telegraph.
*/
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** \namespace morse
* \brief Functions for [Morse Code]
* (https://en.wikipedia.org/wiki/Morse_code).
*/
namespace morse {
/**
* Get the morse representation for given character.
* @param c Character
* @returns morse representation string of character
*/
std::string char_to_morse(const char &c) {
// return corresponding morse code
switch (c) {
case 'a':
return ".-";
case 'b':
return "-...";
case 'c':
return "-.-.";
case 'd':
return "-..";
case 'e':
return ".";
case 'f':
return "..-.";
case 'g':
return "--.";
case 'h':
return "....";
case 'i':
return "..";
case 'j':
return ".---";
case 'k':
return "-.-";
case 'l':
return ".-..";
case 'm':
return "--";
case 'n':
return "-.";
case 'o':
return "---";
case 'p':
return ".--.";
case 'q':
return "--.-";
case 'r':
return ".-.";
case 's':
return "...";
case 't':
return "-";
case 'u':
return "..-";
case 'v':
return "...-";
case 'w':
return ".--";
case 'x':
return "-..-";
case 'y':
return "-.--";
case 'z':
return "--..";
case '1':
return ".----";
case '2':
return "..---";
case '3':
return "...--";
case '4':
return "....-";
case '5':
return ".....";
case '6':
return "-....";
case '7':
return "--...";
case '8':
return "---..";
case '9':
return "----.";
case '0':
return "-----";
default:
std::cerr << "Found invalid character: " << c << ' ' << std::endl;
std::exit(0);
}
}
/**
* Get character from the morse representation.
* @param s Morse representation
* @returns corresponding character
*/
char morse_to_char(const std::string &s) {
// return corresponding character
if (s == ".-") {
return 'a';
} else if (s == "-...") {
return 'b';
} else if (s == "-.-.") {
return 'c';
} else if (s == "-..") {
return 'd';
} else if (s == ".") {
return 'e';
} else if (s == "..-.") {
return 'f';
} else if (s == "--.") {
return 'g';
} else if (s == "....") {
return 'h';
} else if (s == "..") {
return 'i';
} else if (s == ".---") {
return 'j';
} else if (s == "-.-") {
return 'k';
} else if (s == ".-..") {
return 'l';
} else if (s == "--") {
return 'm';
} else if (s == "-.") {
return 'n';
} else if (s == "---") {
return 'o';
} else if (s == ".--.") {
return 'p';
} else if (s == "--.-") {
return 'q';
} else if (s == ".-.") {
return 'r';
} else if (s == "...") {
return 's';
} else if (s == "-") {
return 't';
} else if (s == "..-") {
return 'u';
} else if (s == "...-") {
return 'v';
} else if (s == ".--") {
return 'w';
} else if (s == "-..-") {
return 'x';
} else if (s == "-.--") {
return 'y';
} else if (s == "--..") {
return 'z';
} else if (s == ".----") {
return '1';
} else if (s == "..---") {
return '2';
} else if (s == "...--") {
return '3';
} else if (s == "....-") {
return '4';
} else if (s == ".....") {
return '5';
} else if (s == "-....") {
return '6';
} else if (s == "--...") {
return '7';
} else if (s == "---..") {
return '8';
} else if (s == "----.") {
return '9';
} else if (s == "-----") {
return '0';
} else {
std::cerr << "Found invalid Morse code: " << s << ' ' << std::endl;
std::exit(0);
}
}
/**
* Encrypt given text using morse code.
* @param text text to be encrypted
* @returns new encrypted text
*/
std::string encrypt(const std::string &text) {
std::string encrypted_text = ""; // Empty string to store encrypted text
// Going through each character of text and converting it
// to morse representation
for (const char &c : text) {
encrypted_text += ciphers::morse::char_to_morse(c) + " ";
}
return encrypted_text; // Returning encrypted text
}
/**
* Decrypt given morse coded text.
* @param text text to be decrypted
* @returns new decrypted text
*/
std::string decrypt(const std::string &text) {
// Going through each character of text and converting it
// back to normal representation.
std::string decrypted_text = ""; // Empty string to store decrypted text
// Spliting string (with delimiter = " ") and storing it
// in vector
std::size_t pos_start = 0, pos_end = 0, delim_len = 1;
std::vector<std::string> splits;
while ((pos_end = text.find(' ', pos_start)) != std::string::npos) {
std::string token = text.substr(pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
splits.push_back(token);
}
// Traversing through each morse code string
for (const std::string &s : splits) {
// Add corresponding character
decrypted_text += ciphers::morse::morse_to_char(s);
}
return decrypted_text; // Returning decrypted text
}
} // namespace morse
} // namespace ciphers
/**
* @brief Function to test above algorithm
* @returns void
*/
static void test() {
// Test 1
std::string text1 = "01234567890";
std::string encrypted1 = ciphers::morse::encrypt(text1);
std::string decrypted1 = ciphers::morse::decrypt(encrypted1);
assert(text1 == decrypted1);
std::cout << "Original text : " << text1 << std::endl;
std::cout << "Encrypted text : " << encrypted1 << std::endl;
std::cout << "Decrypted text : " << decrypted1 << std::endl;
// Test 2
std::string text2 = "abcdefghijklmnopqrstuvwxyz";
std::string encrypted2 = ciphers::morse::encrypt(text2);
std::string decrypted2 = ciphers::morse::decrypt(encrypted2);
assert(text2 == decrypted2);
std::cout << "Original text : " << text2 << std::endl;
std::cout << "Encrypted text : " << encrypted2 << std::endl;
std::cout << "Decrypted text : " << decrypted2 << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
// Testing
test();
return 0;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+135
View File
@@ -0,0 +1,135 @@
/**
* @file vigenere_cipher.cpp
* @brief Implementation of [Vigenère cipher](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) algorithm.
*
* @details
* The Vigenère cipher is a method of encrypting alphabetic text by using a series of interwoven vigenere
* ciphers, based on the letters of a keyword. It employs a form of polyalphabetic substitution.
*
* ### Algorithm
* The encryption can also be represented using modular arithmetic by first transforming
* the letters into numbers, according to the scheme, A → 0, B → 1, ..., Z → 25.
* Encryption of \f$i^{th}\f$ character in Message M by key K can be described mathematically as,
*
* \f[ E_{K}(M_{i}) = (M_{i} + K_{i})\;\mbox{mod}\; 26\f]
*
* while decryption of \f$i^{th}\f$ character in Cipher C by key K can be described mathematically as,
*
* \f[ D_{k}(C_{i}) = (C_{i} - K_{i} + 26)\;\mbox{mod}\; 26\f]
*
* Where \f$K_{i}\f$ denotes corresponding character in key. If \f$|key| < |text|\f$ than
* same key is repeated untill their lengths are equal.
*
* For Example,
* If M = "ATTACKATDAWN" and K = "LEMON" than K becomes "LEMONLEMONLE".
*
* \note Rather than creating new key of equal length this program does this by using modular index for key
* (i.e. \f$(j + 1) \;\mbox{mod}\; |\mbox{key}|\f$)
*
* \note This program implements Vigenère cipher for only uppercase English alphabet characters (i.e. A-Z).
*
* @author [Deep Raval](https://github.com/imdeep2905)
*/
#include <iostream>
#include <string>
#include <cassert>
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** \namespace vigenere
* \brief Functions for [vigenère cipher](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) algorithm.
*/
namespace vigenere {
namespace {
/**
* This function finds character for given value (i.e.A-Z)
* @param x value for which we want character
* @return corresponding character for perticular value
*/
inline char get_char(const int x) {
// By adding 65 we are scaling 0-25 to 65-90.
// Which are in fact ASCII values of A-Z.
return char(x + 65);
}
/**
* This function finds value for given character (i.e.0-25)
* @param c character for which we want value
* @return returns corresponding value for perticular character
*/
inline int get_value(const char c) {
// A-Z have ASCII values in range 65-90.
// Hence subtracting 65 will scale them to 0-25.
return int(c - 65);
}
} // Unnamed namespace
/**
* Encrypt given text using vigenere cipher.
* @param text text to be encrypted
* @param key to be used for encryption
* @return new encrypted text
*/
std::string encrypt (const std::string &text, const std::string &key) {
std::string encrypted_text = ""; // Empty string to store encrypted text
// Going through each character of text and key
// Note that key is visited in circular way hence j = (j + 1) % |key|
for(size_t i = 0, j = 0; i < text.length(); i++, j = (j + 1) % key.length()) {
int place_value_text = get_value(text[i]); // Getting value of character in text
int place_value_key = get_value(key[j]); // Getting value of character in key
place_value_text = (place_value_text + place_value_key) % 26; // Applying encryption
char encrypted_char = get_char(place_value_text); // Getting new character from encrypted value
encrypted_text += encrypted_char; // Appending encrypted character
}
return encrypted_text; // Returning encrypted text
}
/**
* Decrypt given text using vigenere cipher.
* @param text text to be decrypted
* @param key key to be used for decryption
* @return new decrypted text
*/
std::string decrypt (const std::string &text, const std::string &key) {
// Going through each character of text and key
// Note that key is visited in circular way hence j = (j + 1) % |key|
std::string decrypted_text = ""; // Empty string to store decrypted text
for(size_t i = 0, j = 0; i < text.length(); i++, j = (j + 1) % key.length()) {
int place_value_text = get_value(text[i]); // Getting value of character in text
int place_value_key = get_value(key[j]); // Getting value of character in key
place_value_text = (place_value_text - place_value_key + 26) % 26; // Applying decryption
char decrypted_char = get_char(place_value_text); // Getting new character from decrypted value
decrypted_text += decrypted_char; // Appending decrypted character
}
return decrypted_text; // Returning decrypted text
}
} // namespace vigenere
} // namespace ciphers
/**
* Function to test above algorithm
*/
void test() {
// Test 1
std::string text1 = "NIKOLATESLA";
std::string encrypted1 = ciphers::vigenere::encrypt(text1, "TESLA");
std::string decrypted1 = ciphers::vigenere::decrypt(encrypted1, "TESLA");
assert(text1 == decrypted1);
std::cout << "Original text : " << text1;
std::cout << " , Encrypted text (with key = TESLA) : " << encrypted1;
std::cout << " , Decrypted text : "<< decrypted1 << std::endl;
// Test 2
std::string text2 = "GOOGLEIT";
std::string encrypted2 = ciphers::vigenere::encrypt(text2, "REALLY");
std::string decrypted2 = ciphers::vigenere::decrypt(encrypted2, "REALLY");
assert(text2 == decrypted2);
std::cout << "Original text : " << text2;
std::cout << " , Encrypted text (with key = REALLY) : " << encrypted2;
std::cout << " , Decrypted text : "<< decrypted2 << std::endl;
}
/** Driver Code */
int main() {
// Testing
test();
return 0;
}
+99
View File
@@ -0,0 +1,99 @@
/**
* @file xor_cipher.cpp
* @brief Implementation of [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) algorithm.
*
* @details
* In cryptography, the simple XOR cipher is a type of additive cipher, an encryption
* algorithm that operates according to the principles:
*
* * \f$A {\oplus} 0 = A\f$
* * \f$A {\oplus} A = 0\f$
* * \f$ (A {\oplus} B) {\oplus} C = A {\oplus} (B {\oplus} C)\f$
* * \f$ (B {\oplus} A) {\oplus} B = B {\oplus} 0 = B \f$
*
*
* where \f$\oplus\f$ symbol denotes the exclusive disjunction (XOR) operation.
* This operation is sometimes called modulus 2 addition (or subtraction, which is identical).
* With this logic, a string of text can be encrypted by applying the bitwise XOR operator to
* every character using a given key. To decrypt the output, merely reapplying the XOR function
* with the key will remove the cipher.
*
* ### Algorithm
* Choose the key for encryption and apply XOR operation to each character of a string.
* Reapplying XOR operation to each character of encrypted string will give original string back.
*
* \note This program implements XOR Cipher for string with ASCII characters.
*
* @author [Deep Raval](https://github.com/imdeep2905)
*/
#include <iostream>
#include <string>
#include <cassert>
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** \namespace XOR
* \brief Functions for [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) algorithm.
*/
namespace XOR {
/**
* Encrypt given text using XOR cipher.
* @param text text to be encrypted
* @param key to be used for encyption
* @return new encrypted text
*/
std::string encrypt (const std::string &text, const int &key) {
std::string encrypted_text = ""; // Empty string to store encrypted text
for (auto &c: text) { // Going through each character
char encrypted_char = char(c ^ key); // Applying encyption
encrypted_text += encrypted_char; // Appending encrypted character
}
return encrypted_text; // Returning encrypted text
}
/**
* Decrypt given text using XOR cipher.
* @param text text to be encrypted
* @param key to be used for decryption
* @return new decrypted text
*/
std::string decrypt (const std::string &text, const int &key) {
std::string decrypted_text = ""; // Empty string to store decrypted text
for (auto &c : text) { // Going through each character
char decrypted_char = char(c ^ key); // Applying decryption
decrypted_text += decrypted_char; // Appending decrypted character
}
return decrypted_text; // Returning decrypted text
}
} // namespace XOR
} // namespace ciphers
/**
* Function to test above algorithm
*/
void test() {
// Test 1
std::string text1 = "Whipalsh! : Do watch this movie...";
std::string encrypted1 = ciphers::XOR::encrypt(text1, 17);
std::string decrypted1 = ciphers::XOR::decrypt(encrypted1, 17);
assert(text1 == decrypted1);
std::cout << "Original text : " << text1;
std::cout << " , Encrypted text (with key = 17) : " << encrypted1;
std::cout << " , Decrypted text : "<< decrypted1 << std::endl;
// Test 2
std::string text2 = "->Valar M0rghulis<-";
std::string encrypted2 = ciphers::XOR::encrypt(text2, 29);
std::string decrypted2 = ciphers::XOR::decrypt(encrypted2, 29);
assert(text2 == decrypted2);
std::cout << "Original text : " << text2;
std::cout << " , Encrypted text (with key = 29) : " << encrypted2;
std::cout << " , Decrypted text : "<< decrypted2 << std::endl;
}
/** Driver Code */
int main() {
// Testing
test();
return 0;
}