cryptopals

Cryptopals Challenges
git clone https://git.pngu.org/cryptopals
Log | Files | Refs

commit 30d2d1e5ed349664692276dcc538d85d35e30817
parent 03dd3fff2cc0dc99e3e1483b0da90134fe91d391
Author: Jack Sangdahl <jack@pngu.org>
Date:   Mon, 29 Sep 2025 10:19:01 -0600

challenge 3: single-byte xor cipher

Diffstat:
Alib/stats.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib/stats.h | 6++++++
Asrc/03_single_byte_xor_cipher.c | 38++++++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/lib/stats.c b/lib/stats.c @@ -0,0 +1,44 @@ +#include "stats.h" + +int frequency_score(const unsigned char *input, const size_t input_len) { + static int freqs[] = { + 8167, 1492, 2782, 4253, 12702, 2228, 2015, 6094, 6966, + 153, 772, 4025, 2406, 6749, 7507, 1929, 95, 5987, + 6327, 9056, 2758, 978, 2360, 150, 1974, 74, + }; + int score = 0; + for (size_t i = 0; i < input_len; i++) { + if (input[i] == 32) + score += 10000; + else if (input[i] >= 65 && input[i] <= 90) + score += freqs[input[i] - 65]; + else if (input[i] >= 97 && input[i] <= 122) + score += freqs[input[i] - 97]; + else if (input[i] >= 128) + score -= 5000; + else + score -= 1000; + } + return score; +} + +double index_coincidence(const char *input) { + int n = 0, values[26] = {0}, curr; + double sum = 0.0, total = 0.0; + for (size_t i = 0; i < strlen(input); i++) { + curr = input[i]; + if (curr >= 65 && curr <= 90) { + values[curr - 65]++; + n++; + } else if (curr >= 97 && curr <= 122) { + values[curr - 97]++; + n++; + } + } + for (size_t i = 0; i < 26; i++) { + curr = values[i]; + sum += curr * (curr - 1); + } + total = sum / (n * (n - 1)); + return total; +} diff --git a/lib/stats.h b/lib/stats.h @@ -0,0 +1,6 @@ +#pragma once + +#include <string.h> + +int frequency_score(const unsigned char *input, const size_t input_len); +double index_coincidence(const char *input); diff --git a/src/03_single_byte_xor_cipher.c b/src/03_single_byte_xor_cipher.c @@ -0,0 +1,38 @@ +// Single-byte XOR cipher +#include "hex.h" +#include "stats.h" +#include "xor.h" +#include <assert.h> +#include <stdio.h> + +/* + * the hex encoded string: + * 1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736 + * ... has been xor'd against single character. find the key, decrypt the + * message. you can do this by hand, but don't: wrrite code to do it for you. + * how? devise some method for "scoring" a piece of english plaintext. character + * frequency is a good metric. evaluate each output and chose the one with the + * best score. + */ + +int main(void) { + static char *input = + "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; + static char *output = "Cooking MC's like a pound of bacon"; + char *ascii; + unsigned char *xord = NULL; + size_t input_len = hex_to_ascii(input, &ascii); + unsigned char res = + find_single_byte_xor((unsigned char *)ascii, input_len, &frequency_score); + if (res > 0) { + unsigned char table[] = {res}; + xor((unsigned char *)ascii, input_len, table, sizeof(char), &xord); + printf("Input: %s\n", input); + printf("Key: %c\n", res); + printf("Output: %s\n", xord); + assert(!strcmp((char *)xord, output)); + } + free(ascii); + free(xord); + return 0; +}