147 lines
4.8 KiB
C

/* Each character on a computer is assigned a unique code and the preferred standard is ASCII (American Standard Code for Information Interchange).
* For example, uppercase A = 65, asterisk (*) = 42, and lowercase k = 107.
*
* A modern encryption method is to take a text file, convert the bytes to ASCII, then XOR each byte with a given value, taken from a secret key.
* The advantage with the XOR function is that using the same encryption key on the cipher text, restores the plain text; for example, 65 XOR 42 = 107,
* then 107 XOR 42 = 65.
*
* For unbreakable encryption, the key is the same length as the plain text message, and the key is made up of random bytes. The user would keep
* the encrypted message and the encryption key in different locations, and without both "halves", it is impossible to decrypt the message.
*
* Unfortunately, this method is impractical for most users, so the modified method is to use a password as a key. If the password is shorter than
* the message, which is likely, the key is repeated cyclically throughout the message. The balance for this method is using a sufficiently long
* password key for security, but short enough to be memorable.
*
* Your task has been made easy, as the encryption key consists of three lower case characters. Using cipher.txt, a file containing the
* encrypted ASCII codes, and the knowledge that the plain text must contain common English words, decrypt the message and find the sum of the
* ASCII values in the original text.*/
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(int argc, char **argv)
{
int i, j, n, sum, found = 0;
double elapsed;
char c1, c2, c3, dummy;
char *enc_text, *plain_text;
FILE *fp;
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
if((fp = fopen("cipher.txt", "r")) == NULL)
{
fprintf(stderr, "Error while opening file %s\n", "cipher.txt");
return 1;
}
n = 0;
/* Count the number of characters (i.e. ASCII values) in the file.*/
while(fscanf(fp, "%d%c", &i, &dummy) != EOF)
{
n++;
}
fclose(fp);
if((enc_text = (char *)malloc(n*sizeof(char))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
if((plain_text = (char *)malloc((n+1)*sizeof(char))) == NULL)
{
fprintf(stderr, "Error while allocating memory\n");
return 1;
}
if((fp = fopen("cipher.txt", "r")) == NULL)
{
fprintf(stderr, "Error while opening file %s\n", "cipher.txt");
return 1;
}
/* Save the encrypted text.*/
for(i = 0; i < n; i++)
{
fscanf(fp, "%d%c", &j, &dummy);
enc_text[i] = (char)j;
}
fclose(fp);
/* Try every combination of three lowercase letters until the key is found.*/
for(c1 = 'a'; c1 <= 'z' && !found; c1++)
{
for(c2 = 'a'; c2 <= 'z' && !found; c2++)
{
for(c3 = 'a'; c3 <= 'z' && !found; c3++)
{
/* Try the current key.*/
for(i = 0; i < n - 2; i += 3)
{
plain_text[i] = enc_text[i] ^ c1;
plain_text[i+1] = enc_text[i+1] ^ c2;
plain_text[i+2] = enc_text[i+2] ^ c3;
}
/* Since a step of 3 was used, the last two characters or
* just the last one might not have been decrypted yet.*/
if(i == n - 2)
{
plain_text[i] = enc_text[i] ^ c1;
plain_text[i+1] = enc_text[i+1] ^ c2;
}
if(i == n - 1)
{
plain_text[i] = enc_text[i] ^ c1;
}
/* Set string terminator.*/
plain_text[n] = 0;
/* Check if the decrypted text contains the 8 most common English words. If it does,
* the right key was likely found.*/
if(strstr(plain_text, "the") != NULL && strstr(plain_text, "be") != NULL &&
strstr(plain_text, "to") != NULL && strstr(plain_text, "of") != NULL &&
strstr(plain_text, "and") != NULL && strstr(plain_text, "in") != NULL &&
strstr(plain_text, "that") != NULL && strstr(plain_text, "have") != NULL)
{
sum = 0;
/* Sum the ASCII values.*/
for(i = 0; i < n; i++)
{
sum += (int)plain_text[i];
}
found = 1;
// printf("%c%c%c\n", c1, c2, c3);
}
}
}
}
free(enc_text);
free(plain_text);
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed=(end.tv_sec-start.tv_sec)+(double)(end.tv_nsec-start.tv_nsec)/1000000000;
printf("Project Euler, Problem 59\n");
printf("Answer: %d\n", sum);
printf("Elapsed time: %.9lf seconds\n", elapsed);
return 0;
}