2020-06-27 08:36:46 +08:00
|
|
|
/**
|
|
|
|
* @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
|
2020-06-27 09:44:02 +08:00
|
|
|
* 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
|
2020-06-27 11:42:16 +08:00
|
|
|
* 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
|
2020-06-28 03:45:48 +08:00
|
|
|
* \note The matrix generation algorithm is very rudimentary and does not
|
|
|
|
* guarantee an invertible modulus matrix. \todo Better matrix generation
|
|
|
|
* algorithm.
|
2020-06-27 11:42:16 +08:00
|
|
|
*
|
|
|
|
* @author [Krishna Vedala](https://github.com/kvedala)
|
2020-06-27 08:36:46 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <cmath>
|
2020-06-27 09:44:02 +08:00
|
|
|
#include <cstring>
|
2020-06-27 08:36:46 +08:00
|
|
|
#include <ctime>
|
2020-06-28 03:45:48 +08:00
|
|
|
#include <fstream>
|
2020-06-27 08:36:46 +08:00
|
|
|
#include <iomanip>
|
|
|
|
#include <iostream>
|
2020-06-27 10:02:21 +08:00
|
|
|
#include <string>
|
2020-06-27 08:36:46 +08:00
|
|
|
#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 */
|
2020-06-27 09:44:02 +08:00
|
|
|
static const char *STRKEY =
|
2020-06-27 08:36:46 +08:00
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&"
|
2020-06-27 11:42:16 +08:00
|
|
|
"*()_+`-=[]{}|;':\",./<>?\\\r\n \0";
|
2020-06-27 08:36:46 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
2020-06-27 10:02:21 +08:00
|
|
|
*
|
|
|
|
* @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$
|
2020-06-27 08:36:46 +08:00
|
|
|
*/
|
|
|
|
template <typename T1, typename T2>
|
2020-06-28 04:00:12 +08:00
|
|
|
static double rand_range(matrix<T2> *M, T1 a, T1 b) {
|
2020-06-27 08:36:46 +08:00
|
|
|
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
|
|
|
|
|
2020-06-27 09:44:02 +08:00
|
|
|
size_t L = std::strlen(STRKEY);
|
|
|
|
|
2020-06-27 08:36:46 +08:00
|
|
|
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];
|
|
|
|
}
|
2020-06-27 09:44:02 +08:00
|
|
|
out[i] = static_cast<uint8_t>(tmp % L);
|
2020-06-27 08:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2020-06-27 09:44:02 +08:00
|
|
|
/**
|
|
|
|
* @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);
|
|
|
|
|
2020-06-28 04:00:12 +08:00
|
|
|
for (size_t idx = 0; idx <= L; idx++)
|
2020-06-27 09:44:02 +08:00
|
|
|
if (STRKEY[idx] == ch)
|
|
|
|
return idx;
|
2020-06-27 11:42:16 +08:00
|
|
|
|
|
|
|
std::cerr << __func__ << ":" << __LINE__ << ": (" << ch
|
|
|
|
<< ") Should not reach here!\n";
|
|
|
|
return 0;
|
2020-06-27 09:44:02 +08:00
|
|
|
}
|
|
|
|
|
2020-06-27 08:36:46 +08:00
|
|
|
/**
|
|
|
|
* @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
|
2020-06-27 11:42:16 +08:00
|
|
|
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);
|
2020-06-27 08:36:46 +08:00
|
|
|
for (size_t j = 0; j < key_len; j++) {
|
2020-06-27 09:44:02 +08:00
|
|
|
batch_int[j] = get_char_idx(text[i + j]);
|
2020-06-27 08:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2020-06-27 09:44:02 +08:00
|
|
|
* 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
|
2020-06-27 10:02:21 +08:00
|
|
|
* 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.
|
2020-06-27 08:36:46 +08:00
|
|
|
*
|
|
|
|
* @param size size of matrix (typically \f$\text{size}\le10\f$)
|
2020-06-27 10:02:21 +08:00
|
|
|
* @param limit1 lower limit of range of random elements (default=0)
|
|
|
|
* @param limit2 upper limit of range of random elements (default=10)
|
2020-06-27 08:36:46 +08:00
|
|
|
* @return Encryption martix
|
|
|
|
*/
|
2020-06-27 10:02:21 +08:00
|
|
|
static matrix<int> generate_encryption_key(size_t size, int limit1 = 0,
|
|
|
|
int limit2 = 10) {
|
2020-06-27 08:36:46 +08:00
|
|
|
matrix<int> encrypt_key(size, std::valarray<int>(size));
|
2020-06-27 09:44:02 +08:00
|
|
|
matrix<int> min_mat = encrypt_key;
|
2020-06-27 08:36:46 +08:00
|
|
|
int mat_determinant = -1; // because matrix has only ints, the
|
|
|
|
// determinant will also be an int
|
2020-06-27 09:44:02 +08:00
|
|
|
int L = std::strlen(STRKEY);
|
2020-06-27 08:36:46 +08:00
|
|
|
|
|
|
|
double dd;
|
|
|
|
do {
|
2020-06-27 09:44:02 +08:00
|
|
|
// keeping the random number range smaller generates better
|
|
|
|
// defined matrices with more ease of cracking
|
2020-06-27 10:02:21 +08:00
|
|
|
dd = rand_range(&encrypt_key, limit1, limit2);
|
2020-06-27 08:36:46 +08:00
|
|
|
mat_determinant = static_cast<int>(dd);
|
|
|
|
|
|
|
|
if (mat_determinant < 0)
|
2020-06-27 09:44:02 +08:00
|
|
|
mat_determinant = (mat_determinant % L);
|
|
|
|
} while (std::abs(dd) > 1e3 || // while ill-defined
|
2020-06-28 03:45:48 +08:00
|
|
|
dd < 0.1 || // while singular or negative determinant
|
2020-06-27 08:36:46 +08:00
|
|
|
!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();
|
2020-06-27 09:44:02 +08:00
|
|
|
int L = std::strlen(STRKEY);
|
2020-06-27 08:36:46 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
double d2 = determinant_lu(decrypt_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$)
|
2020-06-27 10:02:21 +08:00
|
|
|
* @param limit1 lower limit of range of random elements (default=0)
|
|
|
|
* @param limit2 upper limit of range of random elements (default=10)
|
2020-06-27 08:36:46 +08:00
|
|
|
* @return std::pair<matrix<int>, matrix<int>> encryption and decryption
|
|
|
|
* keys as a pair
|
2020-06-27 10:02:21 +08:00
|
|
|
*
|
|
|
|
* @see ::generate_encryption_key
|
2020-06-27 08:36:46 +08:00
|
|
|
*/
|
2020-06-27 10:02:21 +08:00
|
|
|
static std::pair<matrix<int>, matrix<int>> generate_keys(size_t size,
|
|
|
|
int limit1 = 0,
|
|
|
|
int limit2 = 10) {
|
2020-06-27 08:36:46 +08:00
|
|
|
matrix<int> encrypt_key = generate_encryption_key(size);
|
|
|
|
matrix<int> decrypt_key = generate_decryption_key(encrypt_key);
|
|
|
|
double det2 = determinant_lu(decrypt_key);
|
2020-06-27 10:02:21 +08:00
|
|
|
while (std::abs(det2) < 0.1 || std::abs(det2) > 1e3) {
|
|
|
|
encrypt_key = generate_encryption_key(size, limit1, limit2);
|
2020-06-27 08:36:46 +08:00
|
|
|
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
|
|
|
|
|
2020-06-27 11:42:16 +08:00
|
|
|
/**
|
|
|
|
* @brief Self test 1 - using 3x3 randomly generated key
|
|
|
|
*
|
|
|
|
* @param text string to encrypt and decrypt
|
|
|
|
*/
|
|
|
|
void test1(const std::string &text) {
|
2020-06-27 08:36:46 +08:00
|
|
|
// std::string text = "Hello world!";
|
2020-06-27 11:42:16 +08:00
|
|
|
std::cout << "======Test 1 (3x3 key) ======\nOriginal text:\n\t" << text
|
|
|
|
<< std::endl;
|
2020-06-27 08:36:46 +08:00
|
|
|
|
|
|
|
std::pair<matrix<int>, matrix<int>> p =
|
2020-06-27 11:42:16 +08:00
|
|
|
ciphers::HillCipher::generate_keys(3, 0, 100);
|
2020-06-27 08:36:46 +08:00
|
|
|
matrix<int> ekey = p.first;
|
|
|
|
matrix<int> dkey = p.second;
|
2020-06-27 11:42:16 +08:00
|
|
|
|
2020-06-27 08:36:46 +08:00
|
|
|
// matrix<int> ekey = {{22, 28, 25}, {5, 26, 15}, {14, 18, 9}};
|
2020-06-27 10:02:21 +08:00
|
|
|
// std::cout << "Encryption key: \n" << ekey;
|
2020-06-27 08:36:46 +08:00
|
|
|
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);
|
2020-06-27 10:02:21 +08:00
|
|
|
// std::cout << "Decryption key: \n" << dkey;
|
2020-06-27 08:36:46 +08:00
|
|
|
std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
|
|
|
|
std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
|
|
|
|
|
2020-06-28 03:45:48 +08:00
|
|
|
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();
|
|
|
|
|
2020-06-27 11:42:16 +08:00
|
|
|
assert(txt_back == text);
|
2020-06-28 02:43:11 +08:00
|
|
|
std::cout << "Passed :)\n";
|
2020-06-27 11:42:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 =
|
2020-06-28 03:45:48 +08:00
|
|
|
ciphers::HillCipher::generate_keys(8, 0, 3);
|
2020-06-27 11:42:16 +08:00
|
|
|
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;
|
|
|
|
|
2020-06-28 03:45:48 +08:00
|
|
|
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();
|
|
|
|
|
2020-06-28 02:43:11 +08:00
|
|
|
assert(txt_back.compare(0, text.size(), text) == 0);
|
|
|
|
std::cout << "Passed :)\n";
|
2020-06-27 11:42:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/** 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);
|
2020-06-27 08:36:46 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|