diff --git a/formats.c b/formats.c index 43da7b1..dad6181 100644 --- a/formats.c +++ b/formats.c @@ -6,7 +6,7 @@ /* * Set print to true to print value, otherwise returns dynamic string with the number */ -char *print_hex(uint8_t *buf, int len, bool print) +char *print_hex(const uint8_t *buf, int len, bool print) { char *rv = NULL; @@ -36,6 +36,38 @@ char *print_hex(uint8_t *buf, int len, bool print) return rv; } -// TODO: -// buffer to base64 -// base64 to buffer +static bool is_hex_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +// from https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static uint32_t convert_hex_char(uint8_t c) { + return (c & 0xF) + 9 * (c >> 6); +} + +static uint8_t assemble_u8(const char *cs) +{ + uint8_t rv = 0; + for (int i = 0; i < 2; i++) { + int index = !i; + rv |= convert_hex_char(cs[i]) << (4 * index); + } + return rv; +} + +int decode_hex(const char *s, uint8_t *output, int len) +{ + for (int i = 0; i < len; i++) { + int j = i * 2; + int k = j + 1; + + if (is_hex_char(s[j]) && is_hex_char(s[k])) { + output[i] = assemble_u8(s + j); + } else { + return -1; + } + } + + return 0; +} diff --git a/purr.h b/purr.h index a3ebe1a..fb72e59 100644 --- a/purr.h +++ b/purr.h @@ -55,6 +55,7 @@ int socket_write(void *, const uint8_t *, size_t); /* urls.c */ int clean_up_link(const char *, char *, char *, char *); +int get_encryption_params(char *, uint8_t **, uint8_t **); int host_connect(const char *, const char *, bool); /* files.c */ @@ -68,7 +69,8 @@ size_t mmap_to_ssl(struct transmission_information); int send_and_receive(struct connection_information *); /* formats.c */ -char *print_hex(uint8_t *, int, bool); +char *print_hex(const uint8_t *, int, bool); +int decode_hex(const char *, uint8_t *, int); /* encrypt.c */ struct mmap_file encrypt_mmap(struct mmap_file, uint8_t **, uint8_t **); diff --git a/tests.c b/tests.c index f7e3373..3750620 100644 --- a/tests.c +++ b/tests.c @@ -20,6 +20,22 @@ static int compare_strings(const char *expected, const char *result, const char return rv; } +static int compare_arrays(const uint8_t *expected, const uint8_t *result, size_t len, const char *function) +{ + int rv = 0; + + printf("%s(): ", function); + if (memcmp(expected, result, len)) { + rv = 1; + puts("failure"); + printf("expected: %s\ngot: %s\n", print_hex(expected, len, false), print_hex(result, len, false)); + } else { + puts("success"); + } + + return rv; +} + int main() { int rv = 0; @@ -48,6 +64,21 @@ int main() rv = compare_strings("/", path, "clean_up_link") ? 1 : rv; rv = compare_strings("80", port, "clean_up_link") ? 1 : rv; assert(portn == HTTP_PORT); + + dirty = "https://bsd.ac/paste.html#sieqaqk_73fe_df51"; + portn = clean_up_link(dirty, clean, path, port); + rv = compare_strings("bsd.ac", clean, "clean_up_link") ? 1 : rv; + rv = compare_strings("/paste.html#sieqaqk_73fe_df51", path, "clean_up_link") ? 1 : rv; + rv = compare_strings("443", port, "clean_up_link") ? 1 : rv; + assert(portn == HTTPS_PORT); + uint8_t key_exc[KEY_LEN] = {0x73, 0xfe}; + uint8_t iv_exc[IV_LEN] = {0xdf, 0x51}; + uint8_t *key, *iv; + int err = get_encryption_params(path, &key, &iv); + rv = compare_strings("/sieqaqk", path, "get_encryption_params") ? 1 : rv; + rv = compare_arrays(key_exc, key, KEY_LEN, "get_encryption_params") ? 1 : rv; + rv = compare_arrays(iv_exc, iv, IV_LEN, "get_encryption_params") ? 1 : rv; + assert(err == 0); } return rv; diff --git a/urls.c b/urls.c index 2632e0c..7eb0eb1 100644 --- a/urls.c +++ b/urls.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -55,6 +56,58 @@ int clean_up_link(const char *dirty, char *clean, char *path, char *port) return portn; } +#define MALFORM_ERROR(p) do{if((p) == NULL || (p)[1] == 0) {fputs("get_encryption_params(): malformed URL\n", stderr); return rv;}}while(0); + +int get_encryption_params(char *path, uint8_t **keyp, uint8_t **ivp) +{ + int rv = -1; + // parse path in format: "/paste.html#_[_]" + // will update path to point to the proper piece + uint8_t *key = calloc(KEY_LEN, 1); + uint8_t *iv = calloc(IV_LEN, 1); + char *path_temp = calloc(strlen(path), 1); + if (key == NULL || iv == NULL || path_temp == NULL) { + perror("calloc()"); + return rv; + } + *keyp = key; + *ivp = iv; + + char *hash = strchr(path, '#'); + MALFORM_ERROR(hash); + char *underscore = strchr(hash + 1, '_'); + MALFORM_ERROR(underscore); + underscore[0] = 0; + + sprintf(path_temp, "/%s", hash + 1); + + char *key_start = underscore + 1; + underscore = strchr(key_start, '_'); + MALFORM_ERROR(underscore); + underscore[0] = 0; + char *iv_start = underscore + 1; + + size_t key_s_len = strlen(key_start), iv_s_len = strlen(iv_start); + // odd number of chars is an error, as well as being too big + if (key_s_len & 1 || iv_s_len & 1 || key_s_len / 2 > KEY_LEN || iv_s_len / 2 > IV_LEN) { + fputs("get_encryption_params(): malformed KEY and/or IV input\n", stderr); + return rv; + } + + int err = decode_hex(key_start, key, key_s_len / 2) + | decode_hex(iv_start, iv, iv_s_len / 2); + if (err) { + fputs("get_encryption_params(): malformed KEY and/or IV input\n", stderr); + return rv; + } + + strcpy(path, path_temp); + free(path_temp); + + rv = 0; + return rv; +} + int host_connect(const char *host, const char *port, bool debug) { struct addrinfo hints = { 0 }, *si = NULL;