Skip to content

Instantly share code, notes, and snippets.

@warm-ice0x00
Last active March 29, 2023 15:34
Show Gist options
  • Save warm-ice0x00/9e504e0cc22f0babbe630f6a47c6a932 to your computer and use it in GitHub Desktop.
Save warm-ice0x00/9e504e0cc22f0babbe630f6a47c6a932 to your computer and use it in GitHub Desktop.
Known plaintext attack XOR cipher.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const size_t kMaxKeyLen = 8;
unsigned char *hex_to_bytes(const char *hex, const size_t len) {
size_t final_len = len / 2 + (len % 2 != 0);
unsigned char *const result = (unsigned char *)malloc(final_len), *p = result;
if (result == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
if (len % 2 == 1) {
if (sscanf(hex++, "%1hhx", p++) != 1) {
perror("sscanf");
exit(EXIT_FAILURE);
}
final_len--;
}
for (; final_len--; hex += 2, p++) {
if (sscanf(hex, "%2hhx", p) != 1) {
perror("sscanf");
exit(EXIT_FAILURE);
}
}
return result;
}
int search(const unsigned char *const pat, const size_t pat_len,
const unsigned char *const arr, const size_t arr_len) {
if (pat_len > arr_len) {
return 0;
}
const unsigned char *p,
*q, *const arr_end = arr + arr_len, *const pat_end = pat + pat_len;
for (p = arr, q = pat; p < arr_end && q < pat_end; p++) {
if (*p == *q) {
q++;
} else {
p -= q - pat, q = pat;
}
}
return q == pat_end;
}
int increment(unsigned char *const arr, const size_t len) {
unsigned char *p;
for (p = arr + len - 1; p >= arr; p--) {
if (*p == 0xFF) {
*p = 0;
} else {
(*p)++;
break;
}
}
return p < arr;
}
void exclusive_or(unsigned char *arr, const size_t arr_len,
const unsigned char *const key, const size_t key_len) {
size_t i;
for (i = 0; i < arr_len; i++, arr++) {
*arr ^= *(key + (i % key_len));
}
}
void print_bytes(const unsigned char *arr, size_t len) {
const unsigned char *const end = arr + len - 1;
for (; arr < end; arr++) {
printf("%02hhx ", *arr);
}
printf("%02hhx\n", *arr);
}
int main(int argc, char **argv) {
if (argc != 3) {
printf("Usage: %s filename known_plaintext\n", argv[0]);
return 1;
}
const unsigned char *known;
size_t known_len = strlen(argv[2]);
known = hex_to_bytes(argv[2], known_len);
known_len = known_len / 2 + (known_len % 2 != 0);
FILE *const fp = fopen(argv[1], "rb");
if (fp == NULL) {
perror("fopen");
return 1;
}
if (fseek(fp, 0, SEEK_END) != 0) {
perror("fseek");
fclose(fp);
return 1;
}
const size_t file_size = ftello64(fp);
if (file_size == -1) {
perror("ftello64");
fclose(fp);
return 1;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
perror("fseek");
fclose(fp);
return 1;
}
unsigned char *const ciphertext = (unsigned char *)malloc(file_size);
if (ciphertext == NULL) {
perror("malloc");
fclose(fp);
exit(EXIT_FAILURE);
}
if (fread(ciphertext, 1, file_size, fp) != file_size) {
perror("fread");
fclose(fp);
free(ciphertext);
exit(EXIT_FAILURE);
}
fclose(fp);
printf("Filesize: %zu\n", file_size);
unsigned char *const temp = (unsigned char *)malloc(file_size);
if (temp == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
size_t key_len;
for (key_len = 1; key_len <= kMaxKeyLen; key_len++) {
printf("Trying key length %zu...\n", key_len);
unsigned char *const key = (unsigned char *)calloc(key_len, 1);
if (key == NULL) {
perror("calloc");
exit(EXIT_FAILURE);
}
do {
memcpy(temp, ciphertext, file_size);
exclusive_or(temp, file_size, key, key_len);
if (search(known, known_len, temp, file_size)) {
printf("Key found: ");
print_bytes(key, key_len);
}
} while (!increment(key, key_len));
free(key);
}
free(ciphertext);
free(temp);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment