cryptopals

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

commit 40037995c712bf6989e25d68354f45bf22d1173d
Author: Jack Sangdahl <jack@pngu.org>
Date:   Mon, 29 Sep 2025 09:24:52 -0600

challenge 1: convert hex to base64

Diffstat:
A.gitignore | 3+++
AMakefile | 33+++++++++++++++++++++++++++++++++
Alib/base64.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/base64.h | 7+++++++
Alib/hex.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/hex.h | 10++++++++++
Asrc/01_hex_to_base64.c | 32++++++++++++++++++++++++++++++++
7 files changed, 199 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +.cache +build +compile_commands.json diff --git a/Makefile b/Makefile @@ -0,0 +1,33 @@ +CFLAGS = -Wall -Wextra -Werror -Wno-unused -pedantic -g -Ilib +LDFLAGS = + +SRC_DIR = src +LIB_SRC = $(wildcard lib/*.c) +LIB_OBJ = $(LIB_SRC:%.c=build/%.o) +LIB = build/lib/libcryptoutils.a + +SOURCES = $(wildcard $(SRC_DIR)/*.c) +TARGETS = $(SOURCES:%.c=build/%) + +all: $(TARGETS) + +build/%: build/%.o $(LIB) + @mkdir -p $(dir $@) + $(CC) $(LDFLAGS) $^ -o $@ + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +$(LIB): $(LIB_OBJ) + @mkdir -p $(dir $@) + ar rcs $@ $^ + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf build + +.PHONY: all clean diff --git a/lib/base64.c b/lib/base64.c @@ -0,0 +1,63 @@ +#include "base64.h" + +static const unsigned char base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static int mod_table[] = {0, 2, 1}; + +int base64_encode(const char *base64, size_t base64_len, char **res) { + size_t res_len = 4 * ((base64_len + 2) / 3); + int padding = mod_table[base64_len % 3]; + *res = (char *)malloc((res_len + 1) * sizeof(char)); + for (size_t i = 0, j = 0; i < base64_len;) { + int octet_a = i < base64_len ? base64[i++] : 0; + int octet_b = i < base64_len ? base64[i++] : 0; + int octet_c = i < base64_len ? base64[i++] : 0; + int triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + (*res)[j++] = base64_table[(triple >> 3 * 6) & 0x3F]; + (*res)[j++] = base64_table[(triple >> 2 * 6) & 0x3F]; + (*res)[j++] = base64_table[(triple >> 1 * 6) & 0x3F]; + (*res)[j++] = base64_table[(triple >> 0 * 6) & 0x3F]; + } + for (int i = 0; i < padding; i++) + (*res)[res_len - 1 - i] = '='; + return res_len; +} + +int base64_decode(const char *base64, size_t base64_len, char **res) { + unsigned char dtable[256], in[4], block[4], tmp; + char *pos; + memset(dtable, 0x80, sizeof(dtable)); + for (size_t i = 0; i < sizeof(base64_table); i++) + dtable[base64_table[i]] = i; + dtable['='] = 0; + size_t cnt = 0; + for (size_t i = 0; i < base64_len; i++) + if (dtable[(int)base64[i]] != 0x80) + cnt++; + if (cnt % 4) + return -1; + size_t res_len = cnt / 4 * 3; + pos = *res = (char *)malloc(cnt * sizeof(char)); + cnt = 0; + for (size_t i = 0; i < base64_len; i++) { + tmp = dtable[(int)base64[i]]; + if (tmp == 0x80) + continue; + in[cnt] = base64[i]; + block[cnt] = tmp; + cnt++; + if (cnt == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + cnt = 0; + } + } + if (pos > *res) { + if (in[2] == '=') + res_len -= 2; + else if (in[3] == '=') + res_len--; + } + return res_len; +} diff --git a/lib/base64.h b/lib/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include <stdlib.h> +#include <string.h> + +int base64_encode(const char *base64, size_t base64_len, char **res); +int base64_decode(const char *base64, size_t base64_len, char **res); diff --git a/lib/hex.c b/lib/hex.c @@ -0,0 +1,51 @@ +#include "hex.h" + +int hex_to_int(char c) { + int first = c / 16 - 3; + int second = c % 16; + int res = first * 10 + second; + if (res > 9) + res--; + if (res > 29) + res -= 20; + return res; +} + +int hex_to_ascii(const char *hex, char **res) { + size_t hex_len = strlen(hex), res_len = hex_len / 2, i; + *res = (char *)malloc((res_len + 1) * sizeof(char)); + char buf = 0; + for (i = 0; i < hex_len; i++) + if (i % 2) { + int high = hex_to_int(buf) * 16; + int low = hex_to_int(hex[i]); + (*res)[i / 2] = high + low; + } else + buf = hex[i]; + (*res)[i] = 0; + return res_len; +} + +char hex_digit(unsigned int n) { return n < 10 ? n + '0' : (n - 10) + 'a'; } + +int ascii_to_hex(const char *ascii, char **res) { + size_t ascii_len = strlen((char *)ascii), res_len = ascii_len * 2, i; + *res = (char *)malloc((res_len + 1) * sizeof(char)); + for (i = 0; i < ascii_len; i++) { + (*res)[i * 2] = hex_digit(ascii[i] / 0x10); + (*res)[i * 2 + 1] = hex_digit(ascii[i] % 0x10); + } + (*res)[i * 2] = 0; + return res_len; +} + +int to_hex(const unsigned char *data, size_t data_len, char **res) { + size_t res_len = data_len * 2, i; + *res = (char *)malloc((res_len + 1) * sizeof(char)); + for (i = 0; i < data_len; i++) { + (*res)[i * 2] = hex_digit(data[i] / 0x10); + (*res)[i * 2 + 1] = hex_digit(data[i] % 0x10); + } + (*res)[i * 2] = 0; + return res_len; +} diff --git a/lib/hex.h b/lib/hex.h @@ -0,0 +1,10 @@ +#pragma once + +#include <stdlib.h> +#include <string.h> + +int hex_to_int(char c); +int hex_to_ascii(const char *hex, char **res); +char hex_digit(unsigned int n); +int ascii_to_hex(const char *ascii, char **res); +int to_hex(const unsigned char *data, size_t data_len, char **res); diff --git a/src/01_hex_to_base64.c b/src/01_hex_to_base64.c @@ -0,0 +1,32 @@ +// Convert hex to base64 +#include "base64.h" +#include "hex.h" +#include <assert.h> +#include <stdio.h> + +/* + * the string: + * 49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d + * should produce: + * SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t + * so go ahead and make that happen. you'll need to use this code for the rest + * of the exercises. + * + */ + +int main(void) { + static char *input = "49276d206b696c6c696e6720796f757220627261696e206c696b652" + "06120706f69736f6e6f7573206d757368726f6f6d"; + static char *output = + "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"; + char *ascii = NULL, *base64 = NULL; + size_t str_len = hex_to_ascii(input, &ascii); + base64_encode(ascii, str_len, &base64); + printf("Input: %s\n", input); + printf("Output: %s\n", base64); + printf("Expected: %s\n", output); + assert(!strcmp(base64, output)); + free(ascii); + free(base64); + return 0; +}