static uint8_t find_key_byte(pool *p, const byteblock *b, size_t offs, size_t block) { byteblock cp = { p->alloc(p, b->len), 0 }; for (size_t x = 0, i = offs; i < b->len; i += block, x++) { cp.buf[x] = b->buf[i]; cp.len++; } int best_score = INT_MAX; uint8_t best_keybyte = 0; for (int keybyte = 0; keybyte < 256; keybyte++) { uint8_t kb = (uint8_t) keybyte; byteblock key = { &kb, 1 }; byteblock plain = byteblock_xor(p, &cp, &key); int score = score_english(&plain); if (score < best_score) { best_score = score; best_keybyte = keybyte; } } return best_keybyte; }
char break_ciphertext_single_byte_xor(const unsigned char *ciphertext, size_t length, unsigned char *plaintext) { unsigned char key; double high_score = 0.0; int i, j; for (i = 0; i < 256; ++i) { for (j = 0; j < length; ++j) { plaintext[j] = ciphertext[j] ^ i; } double score = score_english(plaintext, length); if (score > high_score) { high_score = score; key = i; } } for (j = 0; j < length; ++j) { plaintext[j] = ciphertext[j] ^ key; } return key; }
int main(int argc, char **argv) { pool p[1] = { pool_create() }; assert(argc == 2); byteblock cipher = from_base64(p, argv[1]); int best_score = INT_MAX; byteblock best_key; byteblock best_plaintext; // this is actually quick enough and easier to bruteforce: 40 * 256 * 256 tries for (size_t k = 2; k < MAX_KEYLEN; k++) { byteblock key = { p->alloc(p, k), k }; for (size_t i = 0; i < key.len; i++) { key.buf[i] = find_key_byte(p, &cipher, i, key.len); } byteblock plain = byteblock_xor(p, &cipher, &key); int score = score_english(&plain); if (score < best_score) { best_score = score; best_key = key; best_plaintext = plain; } } printf("key: %s, plain: %s\n", to_hex(p, &best_key), to_ascii(p, &best_plaintext)); p->finish(p); return 0; }