TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
hill_cipher.cpp
Go to the documentation of this file.
1
36#include <cassert>
37#include <cmath>
38#include <cstdint>
39#include <cstring>
40#include <ctime>
41#include <fstream>
42#include <iomanip>
43#include <iostream>
44#include <string>
45#ifdef _OPENMP
46#include <omp.h>
47#endif
48
50
54template <typename T>
55static std::ostream &operator<<(std::ostream &out, matrix<T> const &v) {
56 const int width = 15;
57 const char separator = ' ';
58
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)
62 << v[row][col];
63 out << std::endl;
64 }
65
66 return out;
67}
68
72namespace ciphers {
74static const char *STRKEY =
75 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&"
76 "*()_+`-=[]{}|;':\",./<>?\\\r\n \0";
77
83 private:
92 template <typename T1, typename T2>
93 static const T2 rand_range(T1 a, T1 b) {
94 // generate random number between 0 and 1
95 long double r = static_cast<long double>(std::rand()) / RAND_MAX;
96
97 // scale and return random number as integer
98 return static_cast<T2>(r * (b - a) + a);
99 }
100
118 template <typename T1, typename T2>
119 static double rand_range(matrix<T2> *M, T1 a, T1 b) {
120 for (size_t i = 0; i < M->size(); i++) {
121 for (size_t j = 0; j < M[0][0].size(); j++) {
122 M[0][i][j] = rand_range<T1, T2>(a, b);
123 }
124 }
125
126 return determinant_lu(*M);
127 }
128
138 template <typename T>
139 static const T gcd(T a, T b) {
140 if (b > a) // ensure always a < b
141 std::swap(a, b);
142
143 while (b != 0) {
144 T tmp = b;
145 b = a % b;
146 a = tmp;
147 }
148
149 return a;
150 }
151
160 static const std::valarray<uint8_t> mat_mul(
161 const std::valarray<uint8_t> &vector, const matrix<int> &key) {
162 std::valarray<uint8_t> out(vector); // make a copy
163
164 size_t L = std::strlen(STRKEY);
165
166 for (size_t i = 0; i < key.size(); i++) {
167 int tmp = 0;
168 for (size_t j = 0; j < vector.size(); j++) {
169 tmp += key[i][j] * vector[j];
170 }
171 out[i] = static_cast<uint8_t>(tmp % L);
172 }
173
174 return out;
175 }
176
183 static inline char get_idx_char(const uint8_t idx) { return STRKEY[idx]; }
184
191 static inline uint8_t get_char_idx(const char ch) {
192 size_t L = std::strlen(STRKEY);
193
194 for (size_t idx = 0; idx <= L; idx++)
195 if (STRKEY[idx] == ch)
196 return idx;
197
198 std::cerr << __func__ << ":" << __LINE__ << ": (" << ch
199 << ") Should not reach here!\n";
200 return 0;
201 }
202
211 static const std::string codec(const std::string &text,
212 const matrix<int> &key) {
213 size_t text_len = text.length();
214 size_t key_len = key.size();
215
216 // length of output string must be a multiple of key_len
217 // create output string and initialize with '\0' character
218 size_t L2 = text_len % key_len == 0
219 ? text_len
220 : text_len + key_len - (text_len % key_len);
221 std::string coded_text(L2, '\0');
222
223 // temporary array for batch processing
224 int i;
225#ifdef _OPENMP
226#pragma parallel omp for private(i)
227#endif
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++) {
231 batch_int[j] = get_char_idx(text[i + j]);
232 }
233
234 batch_int = mat_mul(batch_int, key);
235
236 for (size_t j = 0; j < key_len; j++) {
237 coded_text[i + j] =
238 STRKEY[batch_int[j]]; // get character at key
239 }
240 }
241
242 return coded_text;
243 }
244
250 template <typename T>
252 // Assuming A is square matrix
253 size_t N = A.size();
254
255 matrix<double> inverse(N, std::valarray<double>(N));
256 for (size_t row = 0; row < N; row++) {
257 for (size_t col = 0; col < N; col++) {
258 // create identity matrix
259 inverse[row][col] = (row == col) ? 1.f : 0.f;
260 }
261 }
262
263 if (A.size() != A[0].size()) {
264 std::cerr << "A must be a square matrix!" << std::endl;
265 return inverse;
266 }
267
268 // preallocate a temporary matrix identical to A
269 matrix<double> temp(N, std::valarray<double>(N));
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]);
273 }
274
275 // start transformations
276 for (size_t row = 0; row < N; row++) {
277 for (size_t row2 = row; row2 < N && temp[row][row] == 0; row2++) {
278 // this to ensure diagonal elements are not 0
279 temp[row] = temp[row] + temp[row2];
280 inverse[row] = inverse[row] + inverse[row2];
281 }
282
283 for (size_t col2 = row; col2 < N && temp[row][row] == 0; col2++) {
284 // this to further ensure diagonal elements are not 0
285 for (size_t row2 = 0; row2 < N; row2++) {
286 temp[row2][row] = temp[row2][row] + temp[row2][col2];
287 inverse[row2][row] =
288 inverse[row2][row] + inverse[row2][col2];
289 }
290 }
291
292 if (temp[row][row] == 0) {
293 // Probably a low-rank matrix and hence singular
294 std::cerr << "Low-rank matrix, no inverse!" << std::endl;
295 return inverse;
296 }
297
298 // set diagonal to 1
299 double divisor = temp[row][row];
300 temp[row] = temp[row] / divisor;
301 inverse[row] = inverse[row] / divisor;
302 // Row transformations
303 for (size_t row2 = 0; row2 < N; row2++) {
304 if (row2 == row)
305 continue;
306 double factor = temp[row2][row];
307 temp[row2] = temp[row2] - factor * temp[row];
308 inverse[row2] = inverse[row2] - factor * inverse[row];
309 }
310 }
311
312 return inverse;
313 }
314
315 static int modulo(int a, int b) {
316 int ret = a % b;
317 if (ret < 0)
318 ret += b;
319 return ret;
320 }
321
322 public:
340 static matrix<int> generate_encryption_key(size_t size, int limit1 = 0,
341 int limit2 = 10) {
342 matrix<int> encrypt_key(size, std::valarray<int>(size));
343 matrix<int> min_mat = encrypt_key;
344 int mat_determinant = -1; // because matrix has only ints, the
345 // determinant will also be an int
346 int L = std::strlen(STRKEY);
347
348 double dd;
349 do {
350 // keeping the random number range smaller generates better
351 // defined matrices with more ease of cracking
352 dd = rand_range(&encrypt_key, limit1, limit2);
353 mat_determinant = static_cast<int>(dd);
354
355 if (mat_determinant < 0)
356 mat_determinant = (mat_determinant % L);
357 } while (std::abs(dd) > 1e3 || // while ill-defined
358 dd < 0.1 || // while singular or negative determinant
359 !std::isfinite(dd) || // while determinant is not finite
360 gcd(mat_determinant, L) != 1); // while no common factors
361 // std::cout <<
362
363 return encrypt_key;
364 }
365
373 size_t size = encrypt_key.size();
374 int L = std::strlen(STRKEY);
375
376 matrix<int> decrypt_key(size, std::valarray<int>(size));
377 int det_encrypt = static_cast<int>(determinant_lu(encrypt_key));
378
379 int mat_determinant = det_encrypt < 0 ? det_encrypt % L : det_encrypt;
380
381 matrix<double> tmp_inverse = get_inverse(encrypt_key);
382 double d2 = determinant_lu(decrypt_key);
383
384 // find co-prime factor for inversion
385 int det_inv = -1;
386 for (int i = 0; i < L; i++) {
387 if (modulo(mat_determinant * i, L) == 1) {
388 det_inv = i;
389 break;
390 }
391 }
392
393 if (det_inv == -1) {
394 std::cerr << "Could not find a co-prime for inversion\n";
395 std::exit(EXIT_FAILURE);
396 }
397
398 mat_determinant = det_inv * det_encrypt;
399
400 // perform modular inverse of encryption matrix
401 int i;
402#ifdef _OPENMP
403#pragma parallel omp for private(i)
404#endif
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);
409 }
410 }
411 return decrypt_key;
412 }
413
425 static std::pair<matrix<int>, matrix<int>> generate_keys(size_t size,
426 int limit1 = 0,
427 int limit2 = 10) {
428 matrix<int> encrypt_key = generate_encryption_key(size);
429 matrix<int> decrypt_key = generate_decryption_key(encrypt_key);
430 double det2 = determinant_lu(decrypt_key);
431 while (std::abs(det2) < 0.1 || std::abs(det2) > 1e3) {
432 encrypt_key = generate_encryption_key(size, limit1, limit2);
433 decrypt_key = generate_decryption_key(encrypt_key);
434 det2 = determinant_lu(decrypt_key);
435 }
436 return std::make_pair(encrypt_key, decrypt_key);
437 }
438
446 static const std::string encrypt_text(const std::string &text,
447 const matrix<int> &encrypt_key) {
448 return codec(text, encrypt_key);
449 }
450
458 static const std::string decrypt_text(const std::string &text,
459 const matrix<int> &decrypt_key) {
460 return codec(text, decrypt_key);
461 }
462};
463
464} // namespace ciphers
465
471void test1(const std::string &text) {
472 // std::string text = "Hello world!";
473 std::cout << "======Test 1 (3x3 key) ======\nOriginal text:\n\t" << text
474 << std::endl;
475
476 std::pair<matrix<int>, matrix<int>> p =
478 matrix<int> ekey = p.first;
479 matrix<int> dkey = p.second;
480
481 // matrix<int> ekey = {{22, 28, 25}, {5, 26, 15}, {14, 18, 9}};
482 // std::cout << "Encryption key: \n" << ekey;
483 std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
484 std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
485
486 // matrix<int> dkey = ciphers::HillCipher::generate_decryption_key(ekey);
487 // std::cout << "Decryption key: \n" << dkey;
488 std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
489 std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
490
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;
495 out_file.close();
496
497 assert(txt_back == text);
498 std::cout << "Passed :)\n";
499}
500
506void test2(const std::string &text) {
507 // std::string text = "Hello world!";
508 std::cout << "======Test 2 (8x8 key) ======\nOriginal text:\n\t" << text
509 << std::endl;
510
511 std::pair<matrix<int>, matrix<int>> p =
513 matrix<int> ekey = p.first;
514 matrix<int> dkey = p.second;
515
516 std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
517 std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
518
519 std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
520 std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
521
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;
526 out_file.close();
527
528 assert(txt_back.compare(0, text.size(), text) == 0);
529 std::cout << "Passed :)\n";
530}
531
533int main() {
534 std::srand(std::time(nullptr));
535 std::cout << "Key dictionary: (" << std::strlen(ciphers::STRKEY) << ")\n\t"
536 << ciphers::STRKEY << "\n";
537
538 std::string text = "This is a simple text with numb3r5 and exclamat!0n.";
539
540 test1(text);
541 test2(text);
542
543 return 0;
544}
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)
int main()
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