57 const char separator =
' ';
59 for (
size_t row = 0; row < v.size(); row++) {
60 for (
size_t col = 0; col < v[row].size(); col++)
61 out << std::left << std::setw(width) << std::setfill(separator)
75 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&"
76 "*()_+`-=[]{}|;':\",./<>?\\\r\n \0";
92 template <
typename T1,
typename T2>
95 long double r =
static_cast<long double>(std::rand()) / RAND_MAX;
98 return static_cast<T2
>(r * (b - a) + a);
118 template <
typename T1,
typename T2>
120 for (
size_t i = 0; i < M->size(); i++) {
121 for (
size_t j = 0; j < M[0][0].size(); j++) {
138 template <
typename T>
139 static const T
gcd(T a, T b) {
161 const std::valarray<uint8_t> &vector,
const matrix<int> &key) {
162 std::valarray<uint8_t> out(vector);
164 size_t L = std::strlen(
STRKEY);
166 for (
size_t i = 0; i < key.size(); i++) {
168 for (
size_t j = 0; j < vector.size(); j++) {
169 tmp += key[i][j] * vector[j];
171 out[i] =
static_cast<uint8_t
>(tmp % L);
192 size_t L = std::strlen(
STRKEY);
194 for (
size_t idx = 0; idx <= L; idx++)
198 std::cerr << __func__ <<
":" << __LINE__ <<
": (" << ch
199 <<
") Should not reach here!\n";
211 static const std::string
codec(
const std::string &text,
213 size_t text_len = text.length();
214 size_t key_len = key.size();
218 size_t L2 = text_len % key_len == 0
220 : text_len + key_len - (text_len % key_len);
221 std::string coded_text(L2,
'\0');
226#pragma parallel omp for private(i)
228 for (i = 0; i < L2 - key_len + 1; i += key_len) {
229 std::valarray<uint8_t> batch_int(key_len);
230 for (
size_t j = 0; j < key_len; j++) {
234 batch_int =
mat_mul(batch_int, key);
236 for (
size_t j = 0; j < key_len; j++) {
250 template <
typename T>
256 for (
size_t row = 0; row < N; row++) {
257 for (
size_t col = 0; col < N; col++) {
259 inverse[row][col] = (row == col) ? 1.f : 0.f;
263 if (A.size() != A[0].size()) {
264 std::cerr <<
"A must be a square matrix!" << std::endl;
270 for (
size_t row = 0; row < N; row++) {
271 for (
size_t col = 0; col < N; col++)
272 temp[row][col] =
static_cast<double>(A[row][col]);
276 for (
size_t row = 0; row < N; row++) {
277 for (
size_t row2 = row; row2 < N && temp[row][row] == 0; row2++) {
279 temp[row] = temp[row] + temp[row2];
280 inverse[row] = inverse[row] + inverse[row2];
283 for (
size_t col2 = row; col2 < N && temp[row][row] == 0; col2++) {
285 for (
size_t row2 = 0; row2 < N; row2++) {
286 temp[row2][row] = temp[row2][row] + temp[row2][col2];
288 inverse[row2][row] + inverse[row2][col2];
292 if (temp[row][row] == 0) {
294 std::cerr <<
"Low-rank matrix, no inverse!" << std::endl;
299 double divisor = temp[row][row];
300 temp[row] = temp[row] / divisor;
301 inverse[row] = inverse[row] / divisor;
303 for (
size_t row2 = 0; row2 < N; row2++) {
306 double factor = temp[row2][row];
307 temp[row2] = temp[row2] - factor * temp[row];
308 inverse[row2] = inverse[row2] - factor * inverse[row];
315 static int modulo(
int a,
int b) {
342 matrix<int> encrypt_key(size, std::valarray<int>(size));
344 int mat_determinant = -1;
346 int L = std::strlen(
STRKEY);
352 dd =
rand_range(&encrypt_key, limit1, limit2);
353 mat_determinant =
static_cast<int>(dd);
355 if (mat_determinant < 0)
356 mat_determinant = (mat_determinant % L);
357 }
while (std::abs(dd) > 1e3 ||
359 !std::isfinite(dd) ||
360 gcd(mat_determinant, L) != 1);
373 size_t size = encrypt_key.size();
374 int L = std::strlen(
STRKEY);
376 matrix<int> decrypt_key(size, std::valarray<int>(size));
379 int mat_determinant = det_encrypt < 0 ? det_encrypt % L : det_encrypt;
386 for (
int i = 0; i < L; i++) {
387 if (modulo(mat_determinant * i, L) == 1) {
394 std::cerr <<
"Could not find a co-prime for inversion\n";
395 std::exit(EXIT_FAILURE);
398 mat_determinant = det_inv * det_encrypt;
403#pragma parallel omp for private(i)
405 for (i = 0; i < size; i++) {
406 for (
int j = 0; j < size; j++) {
407 int temp = std::round(tmp_inverse[i][j] * mat_determinant);
408 decrypt_key[i][j] = modulo(temp, L);
431 while (std::abs(det2) < 0.1 || std::abs(det2) > 1e3) {
436 return std::make_pair(encrypt_key, decrypt_key);
448 return codec(text, encrypt_key);
460 return codec(text, decrypt_key);
471void test1(
const std::string &text) {
473 std::cout <<
"======Test 1 (3x3 key) ======\nOriginal text:\n\t" << text
484 std::cout <<
"Encrypted text:\n\t" << gibberish << std::endl;
489 std::cout <<
"Reconstruct text:\n\t" << txt_back << std::endl;
491 std::ofstream out_file(
"hill_cipher_test1.txt");
492 out_file <<
"Block size: " << ekey.size() <<
"\n";
493 out_file <<
"Encryption Key:\n" << ekey;
494 out_file <<
"\nDecryption Key:\n" << dkey;
497 assert(txt_back == text);
498 std::cout <<
"Passed :)\n";
506void test2(
const std::string &text) {
508 std::cout <<
"======Test 2 (8x8 key) ======\nOriginal text:\n\t" << text
517 std::cout <<
"Encrypted text:\n\t" << gibberish << std::endl;
520 std::cout <<
"Reconstruct text:\n\t" << txt_back << std::endl;
522 std::ofstream out_file(
"hill_cipher_test2.txt");
523 out_file <<
"Block size: " << ekey.size() <<
"\n";
524 out_file <<
"Encryption Key:\n" << ekey;
525 out_file <<
"\nDecryption Key:\n" << dkey;
528 assert(txt_back.compare(0, text.size(), text) == 0);
529 std::cout <<
"Passed :)\n";
534 std::srand(std::time(
nullptr));
535 std::cout <<
"Key dictionary: (" << std::strlen(
ciphers::STRKEY) <<
")\n\t"
538 std::string text =
"This is a simple text with numb3r5 and exclamat!0n.";
Implementation of Hill Cipher algorithm.
static char get_idx_char(const uint8_t idx)
Get the character at a given index in the STRKEY.
static matrix< double > get_inverse(matrix< T > const &A)
static std::pair< matrix< int >, matrix< int > > generate_keys(size_t size, int limit1=0, int limit2=10)
Generate encryption and decryption key pair.
static const std::string decrypt_text(const std::string &text, const matrix< int > &decrypt_key)
Decrypt a given text using a given key.
static const T2 rand_range(T1 a, T1 b)
Function to generate a random integer in a given interval.
static matrix< int > generate_encryption_key(size_t size, int limit1=0, int limit2=10)
Generate encryption matrix of a given size. Larger size matrices are difficult to generate but provid...
static double rand_range(matrix< T2 > *M, T1 a, T1 b)
Function overload to fill a matrix with random integers in a given interval.
static const T gcd(T a, T b)
Compute GCD of two integers using Euler's algorithm.
static const std::string encrypt_text(const std::string &text, const matrix< int > &encrypt_key)
Encrypt a given text using a given key.
static matrix< int > generate_decryption_key(matrix< int > const &encrypt_key)
Generate decryption matrix from an encryption matrix key.
static const std::valarray< uint8_t > mat_mul(const std::valarray< uint8_t > &vector, const matrix< int > &key)
helper function to perform vector multiplication with encryption or decryption matrix
static const std::string codec(const std::string &text, const matrix< int > &key)
Convenience function to perform block cipher operations. The operations are identical for both encryp...
static uint8_t get_char_idx(const char ch)
Get the index of a character in the STRKEY.
static void test2()
Self-implementations, 2nd test.
static void test1()
Self-test implementations, 1st test.
static std::ostream & operator<<(std::ostream &out, matrix< T > const &v)
Functions associated with LU Decomposition of a square matrix.
double determinant_lu(const matrix< T > &A)
std::vector< std::valarray< T > > matrix
Algorithms for encryption and decryption.
static const char * STRKEY