chibipub

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 2d18f6357c050bc811310b4ffeb97f10426e0283
parent fd582d92278a1714c0e1fe40b14e944a6229e73b
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  3 Dec 2020 19:57:59 -0800

signature progress

Diffstat:
MMakefile | 5++++-
Mdefault.nix | 2+-
Asrc/base64.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/base64.h | 24++++++++++++++++++++++++
Msrc/wolfsocks.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 367 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,11 +1,14 @@ CFLAGS = -Wall -Werror -std=c89 -Isrc -OBJS = src/cursor.o src/http.o +OBJS = src/cursor.o src/http.o src/base64.o wolfsocks: src/wolfsocks.c $(OBJS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ +clean: fake + rm -f $(OBJS) wolfsocks + tags: fake ctags src/*.c src/*.h diff --git a/default.nix b/default.nix @@ -2,6 +2,6 @@ with pkgs; stdenv.mkDerivation { name = "project"; - nativeBuildInputs = [ ]; + nativeBuildInputs = [ gdb ]; buildInputs = [ ]; } diff --git a/src/base64.c b/src/base64.c @@ -0,0 +1,169 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "base64.h" +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +static const unsigned char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const unsigned char base62_table[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ + +unsigned char * base_encode(const unsigned char *src, size_t len, + unsigned char *out, size_t out_capacity, + size_t *out_len, const unsigned char *base_table) +{ + unsigned char *pos; + const unsigned char *end, *in; + size_t olen; + + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen++; /* nul termination */ + if (olen < len) + return NULL; /* integer overflow */ + if (olen > out_capacity) + return NULL; /* buffer overflow */ + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + while (end - in >= 3) { + *pos++ = base_table[in[0] >> 2]; + *pos++ = base_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base_table[in[2] & 0x3f]; + in += 3; + } + + if (end - in) { + *pos++ = base_table[in[0] >> 2]; + if (end - in == 1) { + *pos++ = base_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } else { + *pos++ = base_table[((in[0] & 0x03) << 4) | + (in[1] >> 4)]; + *pos++ = base_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + } + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + +unsigned char * +base64_encode(const unsigned char *src, size_t len, unsigned char *out, + size_t out_capacity, size_t *out_len) { + return base_encode(src, len, out, out_capacity, out_len, base64_table); +} + +unsigned char * +base62_encode(const unsigned char *src, size_t len, unsigned char *out, + size_t out_capacity, size_t *out_len) { + return base_encode(src, len, out, out_capacity, out_len, base62_table); +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out: Pointer to output buffer + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + unsigned char *out, size_t out_capacity, + size_t *out_size) +{ + unsigned char dtable[256], *pos, block[4], tmp; + size_t i, count, olen; + int pad = 0; + + memset(dtable, 0x80, 256); + for (i = 0; i < sizeof(base64_table) - 1; i++) + dtable[base64_table[i]] = (unsigned char) i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0 || count % 4) + return NULL; + + olen = count / 4 * 3; + + if (out_capacity < olen) + return NULL; + + pos = out; + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len; i++) { + tmp = dtable[src[i]]; + if (tmp == 0x80) + continue; + + if (src[i] == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + assert((size_t)(pos - out) < out_capacity); + *pos++ = (block[1] << 4) | (block[2] >> 2); + assert((size_t)(pos - out) < out_capacity); + *pos++ = (block[2] << 6) | block[3]; + assert((size_t)(pos - out) < out_capacity); + count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + return NULL; + } + break; + } + } + } + + *out_size = pos - out; + return out; +} diff --git a/src/base64.h b/src/base64.h @@ -0,0 +1,24 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +#include <stddef.h> + +unsigned char * base62_encode(const unsigned char *src, size_t len, + unsigned char *out, size_t out_capacity, + size_t *out_len); +unsigned char * base64_encode(const unsigned char *src, size_t len, + unsigned char *out, size_t out_capacity, + size_t *out_len); +unsigned char * base64_decode(const unsigned char *src, size_t len, + unsigned char *out, size_t out_capacity, + size_t *out_size); + +#endif /* BASE64_H */ diff --git a/src/wolfsocks.c b/src/wolfsocks.c @@ -10,16 +10,26 @@ #include <arpa/inet.h> #include <ctype.h> #include <assert.h> +#include <base64.h> #include "http.h" #define BUF_SIZE 4096 #define ARENA_SIZE 8192 +#define note_error(a, msg) fprintf(stderr, "%s: %s\n", __FUNCTION__, msg) + struct request { struct http_req http_req; - struct -} + int client; +}; + +struct sig_header { + const char *key_id; + const char *headers; + unsigned char *signature; + int signature_len; +}; static void error(char *msg) { @@ -27,13 +37,164 @@ static void error(char *msg) exit(1); } -static void handle_request(struct http_req *req) +static int stricmp(const char *a, const char *b) { + int len, i; + + if ((len = strlen(a)) != strlen(b)) + return 0; + + for (i = 0; i < len; i++) { + if (tolower(a[i]) != tolower(b[i])) + return 0; + } + + return 1; +} + +static int parse_until(struct cursor *cur, struct cursor *arena, char match, + const char **out) +{ + unsigned char *start; + + start = cur->p; + + for (; cur->p < cur->end; cur->p++) { + if (*cur->p == match) { + *out = (char*)arena->p; + if (!push_data(arena, start, cur->p - start)) { + return 0; + } + if (!push_byte(arena, 0)) { + return 0; + } + cur->p++; + return 1; + } + } + + cur->p = start; + + return 0; +} + +static int parse_kv(struct cursor *cur, struct cursor *arena, + const char **key, const char **val) { - if (!strcmp(req->method, "POST") && - (!strcmp(req->path, "/inbox") || - !strcmp(req->path, "/inbox/"))) { - print_http_request(req); + if (!parse_until(cur, arena, '=', key)) { + note_error(cur, "= not found"); + return 0; } + + if (!parse_until(cur, arena, ',', val)) { + *val = (char*)cur->p; + assert(*cur->end == 0); + } + + return 1; +} + +static int parse_signature_header(struct cursor *arena, const char *value, + struct sig_header *out) +{ + const char *key; + const char *val; + struct cursor cur; + size_t out_len; + make_cursor((unsigned char*)value, (unsigned char*)value + strlen(value), &cur); + + while (1) { + if (!parse_kv(&cur, arena, &key, &val)) { + break; + } + + if (stricmp(key, "keyid")) { + out->key_id = val; + } else if (stricmp(key, "headers")) { + out->headers = val; + } else if (stricmp(key, "signature")) { + out->signature = arena->p; + + if (!base64_decode((const unsigned char*)val, strlen(val), + arena->p, arena->end - arena->p, + &out_len)) { + note_error(arena, "base64 decode signature"); + return 0; + } + + out->signature_len = out_len; + + arena->p += out_len; + if (!push_byte(arena, 0)) { + note_error(arena, "oom"); + return 0; + } + } + } + + if (out->key_id == NULL || out->headers == NULL || out->signature == NULL) { + return 0; + } + + return 1; +} + +static int verify_signature_header(struct sig_header *sig) +{ + int i; + + printf("verifying\nkeyid %s\nheaders %s\nsignature ", + sig->key_id, sig->headers); + + for (i = 0; i < sig->signature_len; i++) { + printf("%02x", sig->signature[i]); + } + printf("\n"); + + return 1; +} + +static int get_header(struct http_header *header, const char *match, + const char **result) +{ + for (; header; header = header->next) { + if (stricmp(header->name, match)) { + *result = header->value; + return 1; + } + } + + return 0; +} + +static int handle_request(struct http_req *req, struct cursor *arena) +{ + struct sig_header sig; + const char *signature; + memset(&sig, 0, sizeof(sig)); + + if (!strcmp(req->method, "POST") && + (!strcmp(req->path, "/inbox") || + !strcmp(req->path, "/inbox/"))) { + + if (!get_header(req->headers, "signature", &signature)) { + note_error(req, "signature"); + return 0; + } + + printf("signature: %s\n", signature); + + if (!parse_signature_header(arena, signature, &sig)) { + note_error(req, "parse signature header"); + return 0; + } + + if (!verify_signature_header(&sig)) { + note_error(req, "verify"); + return 0; + } + } + + return 1; } void run_http_server() @@ -114,7 +275,7 @@ void run_http_server() } if (parse_http_request(&req, &parser)) { - handle_request(&req); + handle_request(&req, &parser.arena); } if (len != BUF_SIZE) {