$(CC) -o $@ $? $(CFLAGS)
set1p5.test: set1p5.test.c io.o util.o xor.o
$(CC) -o $@ $? $(CFLAGS)
-test: set1p1.test set1p2.test set1p3.test set1p4.test set1p5.test
+set1p6.test: set1p6.test.c io.o util.o xor.o xor_decrypt.o english.o hamming.o
+ $(CC) -o $@ $? $(CFLAGS)
+test: set1p1.test set1p2.test set1p3.test set1p4.test set1p5.test set1p6.test
./set1p1.test >/dev/null || echo "Set 1, Problem 1: Failed"
./set1p2.test >/dev/null || echo "Set 1, Problem 2: Failed"
./set1p3.test >/dev/null || echo "Set 1, Problem 3: Failed"
./set1p4.test >/dev/null || echo "Set 1, Problem 4: Failed"
./set1p5.test >/dev/null || echo "Set 1, Problem 5: Failed"
+ ./set1p6.test >/dev/null || echo "Set 1, Problem 6: Failed"
clean:
rm -f *.o a.out *.test
%.o: %.c
case 'e': case 't': case 'a': case 'o': case 'i': case 'n': return 3; break;
case 'S': case 'H': case 'R': case 'D': case 'L': case 'U': return 2; break;
case 's': case 'h': case 'r': case 'd': case 'l': case 'u': return 2; break;
- case 0: return 0; break;
+ case '\000': return 0; break;
default: break;
}
if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", c) != 0) return 1;
for (i=0;i<length;i++) {
score = score + score_english_character(possible_plaintext[i]);
}
- return score;
+ return score / length;
}
float score_english_str(char* possible_plaintext) {
return score_english_buf(possible_plaintext, strlen(possible_plaintext));
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+
+int bits_set(unsigned char b) {
+ int distance=0;
+ while (b != 0) {
+ if (b%2) { b--; distance++; }
+ b=b/2;
+ }
+ return distance;
+}
+
+int hamming_distance_buf(char* buf1, char* buf2, int length) {
+ int distance = 0;
+ int i;
+ for(i=0;i<length;i++) distance += bits_set(buf1[i] ^ buf2[i]);
+ return distance;
+}
+
+int hamming_distance(char* str1, char* str2) {
+ if (strlen(str1) != strlen(str2)) {
+ printf("hamming_distance: strings should be the same length\n");
+ return -1;
+ }
+ return hamming_distance_buf(str1, str2, strlen(str1));
+}
--- /dev/null
+int hamming_distance_buf(char* buf1, char* buf2, int length);
+int hamming_distance(char* str1, char* str2);
}
char* printable_buffer(char* buffer, int length) {
- char* output = malloc(length*3+2);
- encode_hex(buffer, length, output);
- memcpy(output + length * 2, ": ", 2);
- memcpy(output + length * 2 + 2, buffer, length);
+ char* hex = malloc(length*2+1);
+ char* output = malloc(length*3+7);
+ encode_hex(buffer, length, hex);
+ sprintf(output, "%s: >|%.*s|<", hex, length, buffer);
+ free(hex);
return output;
}
void print_buffer(char* buffer, int length) {
char* output = printable_buffer(buffer, length);
- printf("%s\n", output);
+ printf("%*s\n", length, output);
free(output);
}
+
+int read_base64_file(char* path, char** ciphertext, int *ciphertext_length) {
+ char* ciphertext_base64=NULL;
+ int ciphertext_base64_length=0;
+ FILE *f = fopen(path, "r");
+ int line_length=0;
+ int line_buf_length;
+ char* line=NULL;
+ while ((line_length = getline(&line, &line_buf_length, f))>0) {
+ if (line[line_length-1] == '\000') line_length--;
+ if (line[line_length-1] == '\n') line_length--;
+ ciphertext_base64 = realloc(ciphertext_base64, ciphertext_base64_length+line_length+1);
+ memcpy(ciphertext_base64+ciphertext_base64_length, line, line_length);
+ ciphertext_base64_length += line_length;
+ }
+ free(line);
+ fclose(f);
+ ciphertext_base64[ciphertext_base64_length]=0;
+ int succ = decode_base64(ciphertext_base64, *ciphertext, ciphertext_length);
+ free(ciphertext_base64);
+ return succ;
+}
+#define PRINT_BUFFER(BUF) { printf("Buffer BUF : ");print_buffer(BUF,sizeof(BUF)); }
+
int decode_hex(char* src, char* dest, int *destlen);
int decode_base64(char* src, char* dest, int* destlen);
void encode_base64(const char* src_bytes, int src_size, char* dest);
void encode_hex(const char* src_bytes, int src_size, char* dest);
char* printable_buffer(char* buffer, int length);
void print_buffer(char* buffer, int length);
-#define PRINT_BUFFER(BUF) { printf("Buffer BUF : ");print_buffer(BUF,sizeof(BUF)); }
+
+int read_base64_file(char* path, char** ciphertext, int *ciphertext_length);
<h3>Detect single-character XOR</h3>
<p>
One of the 60-character strings in
- <a href='/static/challenge-data/4.txt'>this file</a>
+ <a href='4.txt'>this file</a>
has been encrypted by single-character XOR.
</p>
<p>
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();*/
</script>
-</html>
\ No newline at end of file
+</html>
</div>
</div>
<p>
- <a href='/static/challenge-data/6.txt'>There's a file here.</a>
+ <a href='6.txt'>There's a file here.</a>
It's been base64'd after being encrypted with repeating-key XOR.
</p>
<p>
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();*/
</script>
-</html>
\ No newline at end of file
+</html>
<h3>AES in ECB mode</h3>
<p>
The Base64-encoded content
- <a href='/static/challenge-data/7.txt'>in this file</a>
+ <a href='7.txt'>in this file</a>
has been encrypted via AES-128 in ECB mode under the key
</p>
<pre>"YELLOW SUBMARINE".</pre>
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();*/
</script>
-</html>
\ No newline at end of file
+</html>
<div class='col-md-10'>
<h3>Detect AES in ECB mode</h3>
<p>
- <a href='/static/challenge-data/8.txt'>In this file</a>
+ <a href='8.txt'>In this file</a>
are a bunch of hex-encoded ciphertexts.
</p>
<p>
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();*/
</script>
-</html>
\ No newline at end of file
+</html>
break;
}
}
+ fclose(fd);
+ free(ciphertext_hex);
printf("Reading from file complete. Read %d lines.\n", candidate_ciphertext_count);
decryption[CIPHERTEXT_LENGTH] = '\0'; // Make it a string.
best_score = -1000;
for(i=0;i<candidate_ciphertext_count;i++) {
find_best_xor_candidate_buf(1, ciphertext_buffers[i], CIPHERTEXT_LENGTH, &xor_byte, decryption, &score);
+ free(ciphertext_buffers[i]);
if (score>best_score) {
best_score = score;
memcpy(best_decryption, decryption, CIPHERTEXT_LENGTH+1);
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "english.h"
+#include "hamming.h"
+#include "io.h"
+#include "util.h"
+#include "xor.h"
+#include "xor_decrypt.h"
+#define MIN_KEYSIZE 2
+#define MAX_KEYSIZE 40
+
+float score_repeating_xor_keysize(char* ciphertext, int ciphertext_size, int keysize) {
+ // The KEYSIZE with the smallest normalized edit distance is probably
+ // the key. You could proceed perhaps with the smallest 2-3 KEYSIZE
+ // values. Or take 4 KEYSIZE blocks instead of 2 and average the
+ // distances.
+ if (ciphertext_size < keysize*4) {
+ printf("in score_repeating_xor_keysize: attempted key size too large (%d vs %d)\n", keysize, ciphertext_size);
+ return -1;
+ }
+ float d01 = hamming_distance_buf(ciphertext+0*keysize, ciphertext+1*keysize, keysize) / keysize;
+ float d12 = hamming_distance_buf(ciphertext+1*keysize, ciphertext+2*keysize, keysize) / keysize;
+ float d23 = hamming_distance_buf(ciphertext+2*keysize, ciphertext+3*keysize, keysize) / keysize;
+ float d02 = hamming_distance_buf(ciphertext+0*keysize, ciphertext+2*keysize, keysize) / keysize;
+ float d13 = hamming_distance_buf(ciphertext+1*keysize, ciphertext+3*keysize, keysize) / keysize;
+ float d03 = hamming_distance_buf(ciphertext+0*keysize, ciphertext+3*keysize, keysize) / keysize;
+ return (d01+d12+d23+d02+d13+d03)/3;
+}
+
+void find_repeating_xor_keysize(char* ciphertext, int ciphertext_length,
+ int min_keysize, int max_keysize,
+ int *keysize, float *keysize_score) {
+ int best_keysize;
+ int keysize_guess;
+ float best_keysize_score=1000;
+ float keysize_guess_score;
+ for(keysize_guess=MIN_KEYSIZE; keysize_guess<=MAX_KEYSIZE; keysize_guess++) {
+ keysize_guess_score = score_repeating_xor_keysize(ciphertext, ciphertext_length, keysize_guess);
+ if (keysize_guess_score < best_keysize_score) {
+ best_keysize=keysize_guess;
+ best_keysize_score=keysize_guess_score;
+ }
+ }
+ *keysize=best_keysize;
+ if (keysize_score != NULL) *keysize_score=best_keysize_score;
+}
+
+int main() {
+ // Test hamming distance code
+ int hd = hamming_distance("this is a test", "wokka wokka!!!");
+ if (hd==37) {
+ printf("Hamming distance: correct\n");
+ } else {
+ printf("Hamming distance: incorrect\n");
+ printf(" Expected: 37\n");
+ printf(" Got: %d\n", hd);
+ }
+
+ // Read the file
+ char *ciphertext = malloc(20000);
+ int ciphertext_length;
+ if(read_base64_file("problems/set-1/6.txt", &ciphertext, &ciphertext_length)) {
+ printf("Base64 decode: complete. Size: %d\n", ciphertext_length);
+ } else {
+ printf("Base64 decode: failed\n");
+ exit(1);
+ }
+
+ // Guess the keysize using hamming distances of keysize-sized chunks
+ int keysize;
+ float keysize_score;
+ find_repeating_xor_keysize(ciphertext, ciphertext_length,
+ MIN_KEYSIZE, MAX_KEYSIZE,
+ &keysize, &keysize_score);
+ if (keysize == 29) {
+ printf("Keysize guess: correct\n");
+ } else {
+ printf("Guessed keysize: %d (%0.2f)\n", keysize, keysize_score);
+ printf("Keysize guess: incorrect\n");
+ exit(1);
+ }
+
+ // Check transpose code
+ char **transposed_simple = NULL;
+ int transpose_length_simple = 0;
+ transpose("ABCDEFG", 7, &transposed_simple, 3, &transpose_length_simple);
+ if (transpose_length_simple == 3) {
+ printf("Simple transpose: correct length\n");
+ } else {
+ printf("Simple transpose: incorrect length\n");
+ exit(1);
+ }
+ if (transposed_simple == NULL) { printf("no return\n"); exit(1); }
+ if (memcmp(transposed_simple[0], "ADG", 3)!=0) { printf("Transpose wrong %3s vs %3s.", "ADF", transposed_simple[0]); exit(1); }
+ if (memcmp(transposed_simple[1], "BE\000", 3)!=0) { printf("Transpose wrong %3s vs %4s.", "BE\\0", transposed_simple[1]); exit(1); }
+ if (memcmp(transposed_simple[2], "CF\000", 3)!=0) { printf("Transpose wrong %3s vs %4s.", "CF\\0", transposed_simple[2]); exit(1); }
+ printf("Simple transpose: correct\n");
+ free(transposed_simple[0]);
+ free(transposed_simple[1]);
+ free(transposed_simple[2]);
+ free(transposed_simple);
+
+ //Do transpose with sanity check
+ char **transposed = NULL;
+ int transpose_length = 0;
+ char *transpose_check = malloc(ciphertext_length);
+ transpose(ciphertext, ciphertext_length, &transposed, keysize, &transpose_length);
+ detranspose(transposed, keysize, transpose_check, ciphertext_length);
+ if (memcmp(ciphertext, transpose_check, ciphertext_length)==0) {
+ printf("Transpose: success\n");
+ } else {
+ printf("Transpose: failed\n");
+ exit(1);
+ }
+ free(transpose_check);
+
+ // Find each key bit
+ char *key = malloc(keysize+1);
+ int i;
+ key[keysize]=0;
+ char **best_decryptions = calloc(sizeof(char*), keysize);
+ float best_score=0;
+ printf("transpose length: %d\n", transpose_length);
+ for (i=0; i<keysize; i++) {
+ best_decryptions[i] = malloc(transpose_length+1);
+ best_decryptions[i][transpose_length]=0;
+ find_best_xor_candidate_buf(1,
+ transposed[i], transpose_length,
+ key + i, best_decryptions[i], &best_score);
+ /*
+ printf(" %d-th bytes begin : ", i);
+ print_buffer(transposed[i], 20);
+ printf("Key byte %d, choosing %hhx (with score %0.2f)\n", i, key[i], best_score);
+ printf("(Apparent) Decryption of %d-th bytes begins: ", i);
+ print_buffer(best_decryptions[i], 20);
+ */
+
+ /*
+ char *sanity_check = malloc(transpose_length+1);
+ sanity_check[transpose_length]=0;
+ repeating_xor(transposed[i], key+i, 1, sanity_check, transpose_length);
+ float sanity_score = score_english_buf(sanity_check, transpose_length);
+ printf("Verifying key byte %d, using %hhx (with score %0.2f)\n", i, key[i], sanity_score);
+ printf("(Sanity C) Decryption of %d-th bytes begins: ", i);
+ print_buffer(sanity_check, 20);
+ free(sanity_check);
+ */
+
+ free(transposed[i]);
+ }
+ free(transposed);
+ printf("Guessed key: %s\n", key);
+
+ // Print the results (Method 1)
+ char *plaintext1 = malloc(ciphertext_length+1);
+ detranspose(best_decryptions, keysize, plaintext1, ciphertext_length);
+ plaintext1[ciphertext_length]=0;
+ for (i=0; i<keysize; i++) {
+ free(best_decryptions[i]);
+ }
+ free(best_decryptions);
+ float score1 = score_english_buf(plaintext1, ciphertext_length);
+ printf("Guessed decryption [method 1] (%0.2f): %20s\n", score1, plaintext1);
+ print_buffer(plaintext1, 100);
+
+ // Print the results (Method 2)
+ char *plaintext2 = malloc(ciphertext_length+1);
+ repeating_xor(ciphertext, key, keysize, plaintext2, ciphertext_length);
+ plaintext2[ciphertext_length]=0;
+ free(ciphertext);
+ free(key);
+ float score = score_english_buf(plaintext2, ciphertext_length);
+ printf("Guessed decryption [method 2] (%0.2f): %20s\n", score, plaintext2);
+ //print_buffer(plaintext2, 20);
+ if (memcmp(plaintext1, plaintext2, ciphertext_length+1)!=0) {
+ printf("Two methods give different results.\n");
+ exit(1);
+ }
+ free(plaintext1);
+ free(plaintext2);
+}
+#include <stdlib.h>
+
void fill(char* buffer1, int buf1_length, char* buffer2, int buf2_length) {
int i;
for (i=0;i<buf2_length;i++)
buffer2[i] = buffer1[i%buf1_length];
}
+
+void transpose(char* buffer, int buffer_length,
+ char ***transposed, int streams, int *stream_length_out) {
+ int i,j, si, stream_length;
+ stream_length=(buffer_length/streams)+1;
+ if (stream_length_out != NULL) *stream_length_out = stream_length;
+ if (*transposed == NULL) {
+ *transposed=(char **) calloc(sizeof(char*), streams);
+ for (i=0;i<streams;i++) {
+ (*transposed)[i] = (char *) malloc(stream_length);
+ }
+ }
+ for (i=0; i<stream_length; i++) {
+ for (j=0;j<streams; j++) {
+ si = i*streams + j;
+ (*transposed)[j][i] = (si<buffer_length) ? buffer[si] : 0;
+ }
+ }
+}
+void detranspose(char **transposed, int streams,
+ char* buffer, int buffer_length) {
+ int i,j, si;
+ int stream_length=(buffer_length/streams)+1;
+ for (i=0; i<stream_length; i++) {
+ for (j=0;j<streams; j++) {
+ si = i*streams + j;
+ if (si < buffer_length) buffer[si] = transposed[j][i];
+ }
+ }
+}
void fill(char* buffer1, int buf1_length, char* buffer2, int buf2_length);
+void transpose(char* buffer, int buffer_length,
+ char ***transposed, int streams, int *stream_length_out);
+void detranspose(char **transpose, int streams,
+ char* buffer, int buffer_length);
#include <stdlib.h>
#include "util.h"
-void xor(char* buf1, char* buf2, char* buf3, int length) {
+void xor(char* in1, char* in2, char* out, int length) {
int i;
for(i=0; i<length; i++)
- buf3[i] = buf1[i] ^ buf2[i];
+ out[i] = in1[i] ^ in2[i];
}
-void repeating_xor(char* buf1, char* key, int keylength, char* buf3, int length) {
- char *buf2 = malloc(length);
- fill(key, keylength, buf2, length);
- xor(buf1, buf2, buf3, length);
- free(buf2);
+void repeating_xor(char* long_in, char* key, int keylength, char* out, int length) {
+ char *in2 = malloc(length);
+ fill(key, keylength, in2, length);
+ xor(long_in, in2, out, length);
+ free(in2);
}
-void xor(char* buf1, char* buf2, char* buf3, int length);
-void repeating_xor(char* buf1, char* key, int keylength, char* buf3, int length);
+void xor(char* in1, char* in2, char* out, int length);
+void repeating_xor(char* long_in, char* key, int keylength, char* out, int length);
#include "util.h"
#include "xor.h"
-void select_best_xor_candidate_buf(char** candidates, int num_candidates, int candidate_length, char* ciphertext_buffer, int ciphertext_length, char* best_candidate, char* best_decryption, float* best_score) {
+void select_best_xor_candidate_buf(char** candidates, int num_candidates, int candidate_length,
+ char* ciphertext_buffer, int ciphertext_length,
+ char* best_candidate, char* best_decryption, float* best_score) {
int candidate;
unsigned char xor_byte;
float score;
unsigned char best_xor_candidate;
char *xor_buffer = malloc(ciphertext_length);
char *plaintext_buffer = malloc(ciphertext_length);
+ float top_score=-100;
int ci;
- *best_score = -100;
for (ci=0; ci<num_candidates; ci++) {
fill(candidates[ci], candidate_length, xor_buffer, ciphertext_length);
xor(xor_buffer, ciphertext_buffer, plaintext_buffer, ciphertext_length);
score = score_english_buf(plaintext_buffer, ciphertext_length);
- if (score > *best_score) {
- *best_score = score;
- strncpy(best_decryption, plaintext_buffer, ciphertext_length);
- strncpy(best_candidate, candidates[ci], candidate_length);
+ if (score > top_score) {
+ top_score = score;
+ if (best_candidate != NULL) memcpy(best_candidate, candidates[ci], candidate_length);
+ if (best_decryption != NULL) memcpy(best_decryption, plaintext_buffer, ciphertext_length);
}
}
+ if (best_score != NULL) *best_score = top_score;
+ free(plaintext_buffer);
+ free(xor_buffer);
}
-void find_best_xor_candidate_buf(int length, char* ciphertext, int ciphertext_length, char* best_candidate, char* best_decryption, float* best_score) {
+void find_best_xor_candidate_buf(int length,
+ char* ciphertext, int ciphertext_length,
+ char* best_candidate, char* best_decryption, float* best_score) {
int num_candidates = 1;
unsigned int i, j, ci;
for (i=0; i<length; i++) num_candidates = num_candidates*256;
select_best_xor_candidate_buf(candidate_pointers, num_candidates, length,
ciphertext, ciphertext_length,
best_candidate, best_decryption, best_score);
+ free(candidates);
+ free(candidate_pointers);
}