|
- #include <stdio.h>
- #include <string.h>
- #include <assert.h>
- #include "utils/base64.h"
- #include "utils/hamming.h"
- #include "utils/xor.h"
- #include "set1/xor_char_finder.h"
- #include <ctype.h>
- #include <stdlib.h>
-
- void xor_char_finder(const unsigned char* const p_ciphertext_xor, struct frequency_t& o_frequency, unsigned ciphertext_len)
- {
- const char printable_ascii[] =" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"#$%&'()*+,-./:;<=>?@[\\]_{}|~`^\r\n";
- // const char printable_ascii[] =" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!()*+,-./:;<=>?@"; // '^' if I add this one then T3 doesn't work
- const unsigned printable_ascii_len = sizeof(printable_ascii)/sizeof(char);
-
-
- // -----------------------------------------------------------------------------
- //
- // Keeps xorable characters - those which produce valid ascii text after xoring
- // with ciphertext.
- //
- // -----------------------------------------------------------------------------
- int xorable = 0;
- unsigned char xorable_chars[printable_ascii_len];
-
-
- // -----------------------------------------------------------------------------
- //
- // 2. Check if Xorable. Get those characters for which XOR of ciphertext and
- // each byte from cipher text produces some valid text.
- //
- // -----------------------------------------------------------------------------
- for(int i=0; i<printable_ascii_len; ++i)
- {
- char tmp_hex[ciphertext_len];
- char aXorChar = printable_ascii[i];
- int whole_ciphertext_xorable=1; // whole ciphertext is xorable with aXorChar
- for(int j=0; j<ciphertext_len; ++j)
- {
- const char xor_chr = p_ciphertext_xor[j]^aXorChar;
- if( strchr(printable_ascii, xor_chr) == NULL)
- {
- whole_ciphertext_xorable=0;
- break;
- }
- }
- if(whole_ciphertext_xorable)
- {
- // printf("Xorable %c\n", aXorChar);
- xorable_chars[xorable] = aXorChar;
- xorable++;
- }
- }
- // -----------------------------------------------------------------------------
- //
- // 3. Frequency analysis: select xorable that xors to most probable result
- //
- // -----------------------------------------------------------------------------
- o_frequency.frequency=0;
- o_frequency.letter=0x00; // > is not in the set
- o_frequency.score = 0;
- double fq = 0;
-
- unsigned char* plaintext = new unsigned char[ciphertext_len];
- unsigned char ch[1];
- if( xorable > 1 )
- {
- for(int i=0; i<xorable; ++i)
- {
- xor_repeatedly(&xorable_chars[i], 1, p_ciphertext_xor, ciphertext_len, plaintext);
- unsigned long long score = frequency_analysis(plaintext, ciphertext_len);
-
- if(o_frequency.score > 0 && (score == o_frequency.score) )
- {
- if(tolower(o_frequency.letter) == tolower(xorable_chars[i]))
- {
- // in case two same letters are found always prefer small one (more probable)
- o_frequency.letter = tolower(xorable_chars[i]);
- }
- else
- {
- printf("Two results with equal scores.\n");
- delete [] plaintext;
- return;
- }
- }
- else if(score > o_frequency.score)
- {
- o_frequency.score = score;
- o_frequency.letter = xorable_chars[i];
- }
- }
- }
- else if(xorable == 1)
- {
- o_frequency.letter = xorable_chars[0];
- o_frequency.frequency=0;
- o_frequency.score = 0;
- }
- delete [] plaintext;
- }
-
- unsigned long long frequency_analysis(const unsigned char* i_text, const unsigned i_len)
- {
- unsigned long long score=0;
- double fq = 0;
- for(int j=0; j<i_len; ++j)
- {
- unsigned char ch = tolower(i_text[j]);
- if( (fq = get_frequency(ch)) )
- {
- unsigned long long tmp = score + fq;
- // crash if overflow
- assert(tmp>score);
- score =tmp;
- }
- }
- return score;
- }
-
- int crack_repeted_xor(const char* const i_hex_string, unsigned char* o_buf, const unsigned i_min_block, const unsigned i_max_block)
- {
- unsigned len = strlen(i_hex_string)/2;
- unsigned char* ciphertext_xor = new unsigned char[len];
-
- convert_string_to_hex(i_hex_string, strlen(i_hex_string), ciphertext_xor);
-
- unsigned char found_key[120];
- int keysize = choose_min_block_size(ciphertext_xor, len, i_min_block, i_max_block);
- /*
- fprintf(stdout, "Key Size: %d Hex Len: %d, Min key size: %d, SlideNb: %d\n",
- keysize,
- len,
- keysize,
- len/keysize);
- */
- const unsigned int block_nb = len / keysize;
- unsigned char* slide = new unsigned char[block_nb];
-
- for(int j=0; j<keysize; ++j)
- {
- memset(slide, '\0', block_nb);
- int i=0;
- for(; i<block_nb; ++i)
- {
- slide[i] = ciphertext_xor[i*keysize + j];
- }
-
- struct frequency_t max_score;
- xor_char_finder(slide, max_score, block_nb);
-
- // printf("> Score %d %c\n", max_score.score, max_score.letter);
- found_key[j]=max_score.letter;
- }
- xor_repeatedly(found_key, keysize, ciphertext_xor, len, o_buf);
-
- delete [] slide;
- delete [] ciphertext_xor;
-
- return keysize;
- }
-
- double get_frequency(char c)
- {
- // -----------------------------------------------------------------------------
- //
- // Array keeps letters that occure most frequently in english language. They are
- // ordered from most frequent to least frequent
- //
- // -----------------------------------------------------------------------------
- frequency_set frequency;
-
- for(int k=0; k<FREQUENT_LETTERS_AMOUNT; ++k)
- {
- if(frequency[k].letter == tolower(c))
- {
- return frequency[k].frequency;
- }
- }
- return 0;
- }
-
- int find_best_keysize(const char* i_hex_bytes, const unsigned i_len, const int i_min_keysize, const int i_max_keysize)
- {
- int score, best_score, block_size;
- score=best_score=block_size=0;
- int current_keysize = i_min_keysize;
-
- unsigned char* buf = (unsigned char*) malloc(i_len);
- while( current_keysize<i_max_keysize )
- {
- memset(buf, 0, i_len);
- current_keysize = crack_repeted_xor(i_hex_bytes, buf, current_keysize, i_max_keysize);
-
- // check again if keysize is in the range. Otherwise range may not be respected.
- if(current_keysize>i_max_keysize)
- break;
-
- score=frequency_analysis(buf, i_len);
- if(score>best_score)
- {
- best_score = score;
- block_size = current_keysize;
- }
- current_keysize++;
- }
- free(buf);
- return block_size;
- }
|