chibipub

experimental activitypub node in C
git clone git://jb55.com/chibipub
Log | Files | Refs | README | LICENSE

commit cb03022e9287060b9495fe9c45844e271f4c9df8
parent deca2e241bc1d924cc914e6c8e995f42f7e31bd7
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 15 Feb 2021 18:40:06 -0800

sigcheck rsa verification working

Diffstat:
Msrc/ap_json.c | 6++++++
Msrc/chibipub.c | 6------
Msrc/io.c | 4+++-
Msrc/sigcheck.c | 234++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
4 files changed, 168 insertions(+), 82 deletions(-)

diff --git a/src/ap_json.c b/src/ap_json.c @@ -197,6 +197,8 @@ static int handle_wssigbuf(struct ap_json *a) return 0; } + fprintf(stderr, "headers: %s\n", a->sig->headers); + if (!split_headers((unsigned char*)a->sig->headers, strlen(a->sig->headers), strs, sizeof(strs)/sizeof(strs[0]), &n_headers)) { @@ -230,6 +232,10 @@ static int handle_wssigbuf(struct ap_json *a) str->size, str->text); return 0; } + + if (i != n_headers-1) { + push_str(c, "\\n"); + } } if (!push_str(c, "\",")) { diff --git a/src/chibipub.c b/src/chibipub.c @@ -178,12 +178,6 @@ static int handle_inbox_request(struct http_req *req, struct cursor *arena) apjson.sig = &sig; apjson.req = req; - // TODO: defer this check - if (!verify_signature_header(&sig)) { - note_error(&req->errs, "verify"); - return 0; - } - start = arena->p; init_json_pusher_with(&push, arena); diff --git a/src/io.c b/src/io.c @@ -29,8 +29,10 @@ int write_file(const char *filename, unsigned char *out, size_t len) size_t written; file = fopen(filename, "wb"); - if (file == NULL) + if (file == NULL) { + fprintf(stderr, "fopen wb '%s' failed\n", filename); return 0; + } written = fwrite(out, 1, len, file); diff --git a/src/sigcheck.c b/src/sigcheck.c @@ -8,19 +8,26 @@ #include <stdlib.h> #include <assert.h> #include <unistd.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> #include "json.h" #include "ubjson.h" #include "hash.h" +#include "base64.h" #include "io.h" #include "sigcheck.h" #include "util.h" #include "inbox.h" #include "errors.h" +#include "sha256/sha256.h" + #include <ctype.h> #include <curl/curl.h> +#define debug_info(...) + struct keyid_pubkey { unsigned char *data; int len; @@ -41,21 +48,132 @@ struct key_writer { int flags; }; -static int verify_signature(struct cursor cur, struct cursor *arena) +static int hex_bytes(unsigned char *bytes, int n_bytes, char *buf, + int buf_size) +{ + static const char *hex = "0123456789abcdef"; + int b; + + if (n_bytes * 2 < buf_size) + return 0; + + for (int i = 0; i < buf_size; i++) { + b = bytes[i/2]; + b = i % 2 ? b & 0x0F : (b & 0xF0) >> 4; + buf[i] = hex[b]; + } + + return 1; +} + + +static int get_cached_pubkey(const char *keyid, int keyid_len, + struct cursor *arena, unsigned char **pubkey, int *pubkey_size) +{ + unsigned char hash[32]; + char hash_str[64]; + char path[2048] = {0}; + int ok; + + // no objects dir, definitely don't have any cached pubkeys + if (!dir_exists(".chibipub/objects")) { + return 0; + } + + blake3_hash((unsigned char *)keyid, keyid_len, hash); + if (!hex_bytes(hash, 32, hash_str, 64)) { + assert(0); + } + + sprintf(path, ".chibipub/objects/%c%c/%.*s", + hash_str[0], hash_str[1], 64, hash_str); + + debug_info(stderr, "key cache exists? '%s' ", path); + if (access(path, F_OK)) { + debug_info(stderr, "no.\n"); + return 0; + } + debug_info(stderr, "yes!\n"); + + *pubkey = arena->p; + ok = read_file(path, arena->p, arena->end - arena->p, pubkey_size); + + arena->p += *pubkey_size; + return ok; +} + +static int verify_signature_rsa(unsigned char *pubkey, int pubkey_len, + unsigned char *sig, int sig_len, + unsigned char *sigbuf, int sigbuf_len) +{ + unsigned char hash[32]; + + RSA *rsa = NULL; + BIO *keybio; + + keybio = BIO_new_mem_buf((void*)pubkey, pubkey_len); + if (!keybio) { + return 0; + } + + rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, 0, 0); + + if (!rsa) { + fprintf(stderr, "verify_signature_rsa: PEM read failed\n"); + return 0; + } + + sha256_hash(hash, sigbuf, sigbuf_len); + + return RSA_verify(NID_sha256, hash, 32, sig, sig_len, rsa); +} + +static int verify_signature(struct cursor cur, struct cursor arena) { struct ubjson ubjson; - struct json val; + struct json sigbuf, keyid, sig; + unsigned char *pubkey, *sig_data; + int pubkey_size; + size_t sig_len; init_ubjson(&ubjson, cur.start, cur.p - cur.start); ubjson.data_end = cur.p; - static const char *path[] = {"@wssigbuf"}; - if (!ubjson_lookup(&ubjson, path, ARRAY_SIZE(path), &val)) { - note_error(&ubjson.errs, "Signature header not found"); + static const char *sig_path[] = {"@wssig"}; + if (!ubjson_lookup(&ubjson, sig_path, ARRAY_SIZE(sig_path), &sig)) { + note_error(&ubjson.errs, "@wssig field not found"); return 0; } - return 1; + static const char *sb_path[] = {"@wssigbuf"}; + if (!ubjson_lookup(&ubjson, sb_path, ARRAY_SIZE(sb_path), &sigbuf)) { + note_error(&ubjson.errs, "@wssigbuf field not found"); + return 0; + } + + static const char *keyid_path[] = {"@wskeyid"}; + if (!ubjson_lookup(&ubjson, keyid_path, ARRAY_SIZE(keyid_path), &keyid)) { + note_error(&ubjson.errs, "keyid not fond"); + return 0; + } + + if (!get_cached_pubkey(keyid.string, keyid.len, &arena, &pubkey, &pubkey_size)) { + note_error(&ubjson.errs, "no cached pubkey for '%s'", keyid.string); + return 0; + } + + sig_data = arena.p; + if (!base64_decode((const unsigned char*)sig.string, + sig.len, arena.p, arena.end - arena.p, + &sig_len)) { + note_error(&ubjson.errs, "base64 decode signature"); + return 0; + } + + return verify_signature_rsa( + pubkey, pubkey_size, + sig_data, sig_len, + (unsigned char*)sigbuf.string, sigbuf.len); } static inline int push_keyid(struct cursor *c, const char *keyid, int keyid_len) @@ -108,6 +226,7 @@ static int has_keyid(struct cursor *keyids, const char *keyid, int keyid_len) return 0; } +/* static int print_keyids(struct cursor keyids) { int size; @@ -126,6 +245,7 @@ static int print_keyids(struct cursor keyids) return 1; } +*/ static int gather_keyids(unsigned char *json, int json_len, struct cursor *arena, struct cursor *keyids_cur) @@ -168,58 +288,9 @@ static int gather_keyids(unsigned char *json, int json_len, keyids_cur->p = keyids_cur->start; arena->p = keyids_cur->end; - print_keyids(*keyids_cur); - - return 1; -} - -static int hex_bytes(unsigned char *bytes, int n_bytes, char *buf, - int buf_size) -{ - static const char *hex = "0123456789abcdef"; - int b; - - if (n_bytes * 2 < buf_size) - return 0; - - for (int i = 0; i < buf_size; i++) { - b = bytes[i/2]; - b = i % 2 ? b & 0x0F : (b & 0xF0) >> 4; - buf[i] = hex[b]; - } - return 1; } -static int get_cached_pubkey(const char *keyid, unsigned char *buf, int buflen, int *pubkey_size) -{ - unsigned char hash[32]; - char hash_str[64]; - char path[2048] = {0}; - - // no objects dir, definitely don't have any cached pubkeys - if (!dir_exists(".chibipub/objects")) { - return 0; - } - - blake3_hash((unsigned char *)keyid, strlen(keyid), hash); - if (!hex_bytes(hash, 32, hash_str, 64)) { - assert(0); - } - - sprintf(path, ".chibipub/objects/%c%c/%.*s", - hash_str[0], hash_str[1], 64, hash_str); - - fprintf(stderr, "key cache exists? '%s' ", path); - if (access(path, F_OK)) { - fprintf(stderr, "no.\n"); - return 0; - } - fprintf(stderr, "yes!\n"); - - return read_file(path, buf, buflen, pubkey_size); -} - static size_t write_cb(char *data, size_t n, size_t l, void *userp) { double dcl; @@ -294,25 +365,36 @@ static void prepare_transfers(CURLM **cm) curl_multi_setopt(*cm, CURLMOPT_MAXCONNECTS, (long)10); } -static int write_pubkey_cache(struct key_writer *writer, struct cursor arena) +static int get_keyid_hash(const char *keyid, struct cursor *arena, + char **hash_str) { unsigned char *hash; - char *hash_str; - char *path; - if (!(hash = cursor_alloc(&arena, 32))) { + if (!(*hash_str = cursor_alloc(arena, 65))) { return 0; } - blake3_hash((unsigned char*)writer->keyid, strlen(writer->keyid), hash); + if (!(hash = cursor_alloc(arena, 32))) { + return 0; + } - if (!(hash_str = cursor_alloc(&arena, 64))) { - note_error(&writer->errs, "alloc hash_str"); + blake3_hash((unsigned char*)keyid, strlen(keyid), hash); + if (!hex_bytes(hash, 32, *hash_str, 64)) { return 0; } - if (!hex_bytes(hash, 32, hash_str, 64)) { - note_error(&writer->errs, "hex_bytes"); + arena->p -= 32; + + return 1; +} + +static int write_pubkey_cache(struct key_writer *writer, struct cursor arena) +{ + char *hash_str; + char *path; + + if (!get_keyid_hash(writer->keyid, &arena, &hash_str)) { + note_error(&writer->errs, "get_keyid_hash"); return 0; } @@ -338,7 +420,8 @@ static int write_pubkey_cache(struct key_writer *writer, struct cursor arena) return 0; } - if (!write_file(path, writer->pubkey->data, writer->pubkey->len)) { + if (!write_file(path, (unsigned char*)writer->pubkey->data, + writer->pubkey->len)) { note_error(&writer->errs, "write_file '%s' pubkey failed", path); return 0; } @@ -503,14 +586,12 @@ static int fetch_signatures(unsigned char *json, int json_len, return 0; } - if (get_cached_pubkey(keyid_str, arena->p, - arena->end - arena->p, - &pubkey->len)) { - pubkey->data = arena->p; - arena->p += pubkey->len; + if (get_cached_pubkey(keyid_str, strlen(keyid_str), arena, + &pubkey->data, &pubkey->len)) { + /* printf("got cached pubkey of size %d... do something\n", pubkey->len); - + */ continue; } @@ -542,13 +623,12 @@ int sigcheck(struct sigcheck *check) size_t flen; struct json_parser jsonp; struct json_handlers handlers; - struct cursor in_cur, out_cur; + struct cursor out_cur; scratch = malloc(SCRATCH_SIZE); map_file(check->activity_file, &p, &flen); init_json_handlers(&handlers); - make_cursor(p, p + flen, &in_cur); make_cursor(scratch, scratch + SCRATCH_SIZE, &out_cur); make_ubjson_handlers(&handlers, &out_cur); init_json_parser(&jsonp, p, flen, &handlers); @@ -558,12 +638,16 @@ int sigcheck(struct sigcheck *check) return 0; } + make_cursor(scratch, scratch + SCRATCH_SIZE, &out_cur); + make_ubjson_handlers(&handlers, &out_cur); + init_json_parser(&jsonp, p, flen, &handlers); + start = out_cur.p; while (parse_json(&jsonp)) { count++; - printf("[%d] parse success\n", count); + debug_info("[%d] parse success\n", count); - if (!verify_signature(out_cur, &out_cur)) { + if (!verify_signature(out_cur, out_cur)) { printf("bad signature #%d\n", count); }