sigcheck.c (14722B)
1 2 #define SCRATCH_SIZE 134217728 3 #define MAX_PARALLEL 10 4 5 #include <sys/mman.h> 6 #include <sys/stat.h> 7 #include <sys/types.h> 8 #include <stdlib.h> 9 #include <assert.h> 10 #include <unistd.h> 11 #include <openssl/rsa.h> 12 #include <openssl/pem.h> 13 14 #include "json.h" 15 #include "debug.h" 16 #include "ubjson.h" 17 #include "hash.h" 18 #include "hex.h" 19 #include "base64.h" 20 #include "io.h" 21 #include "sigcheck.h" 22 #include "util.h" 23 #include "inbox.h" 24 #include "errors.h" 25 26 #include "sha256/sha256.h" 27 28 #include <ctype.h> 29 #include <curl/curl.h> 30 31 struct keyid_pubkey { 32 unsigned char *data; 33 int len; 34 }; 35 36 enum key_writer_flags { 37 KW_FAILED = 1 << 0, 38 KW_STARTED = 1 << 1, 39 }; 40 41 struct key_writer { 42 CURL *curl; 43 struct cursor *arena; 44 struct keyid_pubkey *pubkey; 45 const char *keyid; 46 struct cursor slice; 47 struct errors errs; 48 int flags; 49 }; 50 51 static int is_delete_activity(struct ubjson *ubjson) 52 { 53 struct json val; 54 static const char *type_path[] = {"type"}; 55 56 if (!ubjson_lookup(ubjson, type_path, ARRAY_SIZE(type_path), &val)) { 57 printf("unusual: couldn't determine object type\n"); 58 return 1; 59 } 60 61 return val.type == JSON_STRING && !memcmp(val.string, "Delete", val.len); 62 } 63 64 65 static int get_cached_pubkey(const char *keyid, int keyid_len, 66 struct cursor *arena, unsigned char **pubkey, int *pubkey_size) 67 { 68 unsigned char hash[32]; 69 char hash_str[64]; 70 char path[2048] = {0}; 71 int ok; 72 73 // no objects dir, definitely don't have any cached pubkeys 74 if (!dir_exists(".chibipub/objects")) { 75 return 0; 76 } 77 78 hashdata32((unsigned char *)keyid, keyid_len, hash, sizeof(hash)); 79 if (!hex_bytes(hash, 32, hash_str, 64)) { 80 assert(0); 81 } 82 83 sprintf(path, ".chibipub/objects/%c%c/%.*s", 84 hash_str[0], hash_str[1], 64, hash_str); 85 86 if (access(path, F_OK)) { 87 debug("key cache '%s' doesn't exist", path); 88 return 0; 89 } 90 91 *pubkey = arena->p; 92 ok = read_file(path, arena->p, arena->end - arena->p, pubkey_size); 93 94 arena->p += *pubkey_size; 95 return ok; 96 } 97 98 static int verify_signature_rsa(unsigned char *pubkey, int pubkey_len, 99 unsigned char *sig, int sig_len, 100 unsigned char *sigbuf, int sigbuf_len) 101 { 102 unsigned char hash[32]; 103 104 RSA *rsa = NULL; 105 BIO *keybio; 106 107 keybio = BIO_new_mem_buf((void*)pubkey, pubkey_len); 108 if (!keybio) { 109 return 0; 110 } 111 112 rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, 0, 0); 113 114 if (!rsa) { 115 fprintf(stderr, "verify_signature_rsa: PEM read failed\n"); 116 return 0; 117 } 118 119 sha256_hash(hash, sigbuf, sigbuf_len); 120 121 return RSA_verify(NID_sha256, hash, 32, sig, sig_len, rsa); 122 } 123 124 static int verify_signature(struct cursor cur, struct cursor arena) 125 { 126 struct ubjson ubjson; 127 struct json sigbuf, keyid, sig; 128 unsigned char *pubkey, *sig_data; 129 int pubkey_size; 130 size_t sig_len; 131 132 init_ubjson(&ubjson, cur.start, cur.p - cur.start); 133 ubjson.data_end = cur.p; 134 135 if (is_delete_activity(&ubjson)) 136 return 2; 137 138 static const char *sig_path[] = {"@wssig"}; 139 if (!ubjson_lookup(&ubjson, sig_path, ARRAY_SIZE(sig_path), &sig)) { 140 note_error(&ubjson.errs, "@wssig field not found"); 141 return 0; 142 } 143 144 static const char *sb_path[] = {"@wssigbuf"}; 145 if (!ubjson_lookup(&ubjson, sb_path, ARRAY_SIZE(sb_path), &sigbuf)) { 146 note_error(&ubjson.errs, "@wssigbuf field not found"); 147 return 0; 148 } 149 150 static const char *keyid_path[] = {"@wskeyid"}; 151 if (!ubjson_lookup(&ubjson, keyid_path, ARRAY_SIZE(keyid_path), &keyid)) { 152 note_error(&ubjson.errs, "keyid not fond"); 153 return 0; 154 } 155 156 if (!get_cached_pubkey(keyid.string, keyid.len, &arena, &pubkey, &pubkey_size)) { 157 note_error(&ubjson.errs, "no cached pubkey for '%.*s'", keyid.len, keyid.string); 158 return 0; 159 } 160 161 sig_data = arena.p; 162 if (!base64_decode((const unsigned char*)sig.string, 163 sig.len, arena.p, arena.end - arena.p, 164 &sig_len)) { 165 note_error(&ubjson.errs, "base64 decode signature"); 166 return 0; 167 } 168 169 return verify_signature_rsa( 170 pubkey, pubkey_size, 171 sig_data, sig_len, 172 (unsigned char*)sigbuf.string, sigbuf.len); 173 } 174 175 static inline int push_keyid(struct cursor *c, const char *keyid, int keyid_len) 176 { 177 struct keyid_pubkey pubkey = {0}; 178 return push_int(c, keyid_len) 179 && push_data(c, (unsigned char *)keyid, keyid_len) 180 && push_byte(c, 0) 181 && push_data(c, (unsigned char *)&pubkey, sizeof(pubkey)); // reserved for pubkey data index 182 } 183 184 static int pull_keyid(struct cursor *c, const char **keyid_str, int *keyid_len, 185 struct keyid_pubkey **pubkey) 186 { 187 if (!pull_int(c, keyid_len)) { 188 return 0; 189 } 190 191 *keyid_str = (const char*)c->p; 192 c->p += (*keyid_len) + 1; /* plus zero */ 193 *pubkey = (struct keyid_pubkey*)c->p; 194 c->p += sizeof(struct keyid_pubkey); 195 196 return 1; 197 } 198 199 static int has_keyid(struct cursor *keyids, const char *keyid, int keyid_len) 200 { 201 struct cursor scan; 202 int size; 203 const char *keyid_str; 204 struct keyid_pubkey *offset; 205 206 scan.start = keyids->start; 207 scan.p = keyids->start; 208 scan.end = keyids->p; 209 210 while (scan.p < scan.end) { 211 if (!pull_keyid(&scan, &keyid_str, &size, &offset)) { 212 return 0; 213 } 214 if (size != keyid_len) { 215 continue; 216 } 217 if (!memcmp(keyid, keyid_str, keyid_len)) { 218 return 1; 219 } 220 } 221 222 return 0; 223 } 224 225 /* 226 static int print_keyids(struct cursor keyids) 227 { 228 int size; 229 const char *keyid_str; 230 struct keyid_pubkey *pubkey; 231 232 keyids.end = keyids.p; 233 keyids.p = keyids.start; 234 235 printf("keyids\n"); 236 while (keyids.p < keyids.end) { 237 if (!pull_keyid(&keyids, &keyid_str, &size, &pubkey)) 238 return 0; 239 printf("%.*s\n", size, keyid_str); 240 } 241 242 return 1; 243 } 244 */ 245 246 static int gather_keyids(unsigned char *json, int json_len, 247 struct cursor *arena, struct cursor *keyids_cur) 248 { 249 static const char *path[] = {"@wskeyid"}; 250 const int ubjson_mem_size = 1024 * 256; 251 252 struct ubjson ubjson; 253 struct json val; 254 struct json_parser parser; 255 struct json_handlers handlers; 256 unsigned char *ubjson_mem; 257 258 ubjson_mem = cursor_alloc(arena, ubjson_mem_size); 259 make_cursor(arena->p, arena->end, keyids_cur); 260 init_ubjson(&ubjson, ubjson_mem, ubjson_mem_size); 261 make_ubjson_handlers(&handlers, &ubjson.cur); 262 init_json_parser(&parser, json, json_len, &handlers); 263 264 while (parse_json(&parser)) { 265 init_ubjson(&ubjson, ubjson_mem, ubjson_mem_size); 266 ubjson.data_end = ubjson.cur.p; 267 268 if (is_delete_activity(&ubjson)) 269 continue; 270 271 if (!ubjson_lookup(&ubjson, path, ARRAY_SIZE(path), &val)) { 272 note_error(&parser.errs, "@wskeyid not found"); 273 return 0; 274 } 275 276 if (has_keyid(keyids_cur, val.string, val.len)) { 277 continue; 278 } 279 280 if (!push_keyid(keyids_cur, val.string, val.len)) { 281 note_error(&parser.errs, "push_keyid"); 282 return 0; 283 } 284 } 285 286 keyids_cur->end = keyids_cur->p; 287 keyids_cur->p = keyids_cur->start; 288 arena->p = keyids_cur->end; 289 290 return 1; 291 } 292 293 static size_t write_cb(char *data, size_t n, size_t l, void *userp) 294 { 295 double dcl; 296 int cl; 297 int res; 298 size_t size = n*l; 299 struct key_writer *writer = (struct key_writer *)userp; 300 301 if (!(writer->flags & KW_STARTED)) { 302 res = curl_easy_getinfo(writer->curl, 303 CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dcl); 304 if (res) { 305 fprintf(stderr, "'%s' download failed, content-length unknown\n", 306 writer->keyid); 307 writer->flags |= KW_FAILED; 308 return 0; 309 } 310 311 cl = (int)dcl; 312 if (cl == -1) { 313 cl = 1024*1024; 314 } 315 316 writer->pubkey->data = cursor_alloc(writer->arena, cl); 317 if (!writer->pubkey->data) { 318 fprintf(stderr, "cursor_alloc failed, remaining (%ld) + %d (%ld total)\n", 319 writer->arena->end - writer->arena->p, 320 cl, writer->arena->end - writer->arena->start); 321 writer->flags |= KW_FAILED; 322 return 0; 323 } 324 325 writer->flags |= KW_STARTED; 326 writer->pubkey->len = 0; 327 make_cursor(writer->pubkey->data, writer->pubkey->data + cl, 328 &writer->slice); 329 } 330 331 if (!push_data(&writer->slice, (unsigned char*)data, size)) { 332 writer->flags |= KW_FAILED; 333 return 0; 334 } 335 336 writer->pubkey->len += size; 337 338 return size; 339 } 340 341 static int add_signature_transfer(CURLM *cm, struct key_writer *writer) 342 { 343 struct curl_slist *list = NULL; 344 345 CURL *eh = curl_easy_init(); 346 writer->curl = eh; 347 curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, write_cb); 348 curl_easy_setopt(eh, CURLOPT_WRITEDATA, writer); 349 curl_easy_setopt(eh, CURLOPT_URL, writer->keyid); 350 curl_easy_setopt(eh, CURLOPT_PRIVATE, writer); 351 352 list = curl_slist_append(list, "Accept: application/ld+json"); 353 curl_easy_setopt(eh, CURLOPT_HTTPHEADER, list); 354 355 curl_multi_add_handle(cm, eh); 356 357 return 1; 358 } 359 360 static void prepare_transfers(CURLM **cm) 361 { 362 curl_global_init(CURL_GLOBAL_ALL); 363 *cm = curl_multi_init(); 364 curl_multi_setopt(*cm, CURLMOPT_MAXCONNECTS, (long)10); 365 } 366 367 static int get_keyid_hash(const char *keyid, struct cursor *arena, 368 char **hash_str) 369 { 370 unsigned char *hash; 371 372 if (!(*hash_str = cursor_alloc(arena, 65))) { 373 return 0; 374 } 375 376 if (!(hash = cursor_alloc(arena, 32))) { 377 return 0; 378 } 379 380 hashdata32((unsigned char*)keyid, strlen(keyid), hash, 32); 381 if (!hex_bytes(hash, 32, *hash_str, 64)) { 382 return 0; 383 } 384 385 arena->p -= 32; 386 387 return 1; 388 } 389 390 static int write_pubkey_cache(struct key_writer *writer, struct cursor arena) 391 { 392 char *hash_str; 393 char *path; 394 395 if (!get_keyid_hash(writer->keyid, &arena, &hash_str)) { 396 note_error(&writer->errs, "get_keyid_hash"); 397 return 0; 398 } 399 400 path = (char*)arena.p; 401 402 if (!(push_str(&arena, ".chibipub/objects/") && 403 push_sized_str(&arena, hash_str, 2) && 404 push_byte(&arena, 0) && 405 mkdirp(path))) 406 { 407 note_error(&writer->errs, "mkdir -p '%s'", path); 408 return 0; 409 } 410 411 // continue path 412 arena.p--; 413 414 if (!(push_byte(&arena,'/') && 415 push_sized_str(&arena, hash_str, 64) && 416 push_byte(&arena, 0))) 417 { 418 note_error(&writer->errs, "push path oob"); 419 return 0; 420 } 421 422 if (!write_file(path, (unsigned char*)writer->pubkey->data, 423 writer->pubkey->len)) { 424 note_error(&writer->errs, "write_file '%s' pubkey failed", path); 425 return 0; 426 } 427 428 return 1; 429 } 430 431 static int handle_pubkey_response(struct cursor arena, 432 struct key_writer *writer, unsigned char *json, int json_len) 433 { 434 unsigned char *start; 435 struct ubjson ubjson; 436 struct json_handlers handler; 437 struct json_parser parser; 438 struct json val; 439 static const char *path[] = {"publicKey", "publicKeyPem"}; 440 441 start = arena.p; 442 443 make_ubjson_handlers(&handler, &arena); 444 init_json_parser(&parser, json, json_len, &handler); 445 if (!parse_json(&parser)) { 446 note_error(&writer->errs, "keyid '%s' activity json parse failed", 447 writer->keyid); 448 return 0; 449 } 450 451 init_ubjson(&ubjson, start, arena.p - start); 452 ubjson.data_end = arena.p; 453 454 if (!ubjson_lookup(&ubjson, path, ARRAY_SIZE(path), &val)) { 455 note_error(&writer->errs, 456 "keyid '%s' could not find 'publicKeyPem' field", 457 writer->keyid); 458 return 0; 459 } 460 461 writer->pubkey->data = (unsigned char*)val.string; 462 writer->pubkey->len = val.len; 463 464 if (!write_pubkey_cache(writer, arena)) { 465 note_error(&writer->errs, "failed to write pubkey"); 466 return 0; 467 } 468 469 return 1; 470 } 471 472 static int handle_transfer_msg(struct cursor *arena, CURLM *cm, CURLMsg *msg, int *transfers, 473 struct key_writer *to_fetch, int n_to_fetch) 474 { 475 struct key_writer *writer; 476 int ok = 1; 477 478 if (msg->msg == CURLMSG_DONE) { 479 CURL *e = msg->easy_handle; 480 481 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &writer); 482 483 fprintf(stderr, "R: %d - %s <%s>\n", 484 msg->data.result, 485 curl_easy_strerror(msg->data.result), 486 writer->keyid); 487 488 if (msg->data.result == CURLE_OK) { 489 ok = handle_pubkey_response(*arena, writer, 490 writer->pubkey->data, 491 writer->pubkey->len); 492 } 493 494 curl_multi_remove_handle(cm, e); 495 curl_easy_cleanup(e); 496 } else { 497 fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); 498 } 499 500 if (*transfers < n_to_fetch) { 501 add_signature_transfer(cm, &to_fetch[*transfers]); 502 *transfers = *transfers + 1; 503 } 504 505 return ok; 506 } 507 508 static int perform_transfers(struct cursor *arena, CURLM *cm, int transfers, 509 struct key_writer *to_fetch, int n_to_fetch) 510 { 511 CURLMsg *msg; 512 int still_alive = 1; 513 int msgs_left = -1; 514 515 do { 516 curl_multi_perform(cm, &still_alive); 517 518 while ((msg = curl_multi_info_read(cm, &msgs_left))) { 519 handle_transfer_msg(arena, 520 cm, msg, &transfers, to_fetch, n_to_fetch); 521 } 522 523 if (still_alive) { 524 curl_multi_wait(cm, NULL, 0, 1000, NULL); 525 } 526 } while (still_alive || (transfers < n_to_fetch)); 527 528 curl_multi_cleanup(cm); 529 curl_global_cleanup(); 530 531 return 1; 532 } 533 534 static int count_keyids(struct cursor scan, int *count) 535 { 536 int size; 537 const char *keyid_str; 538 struct keyid_pubkey *pubkey; 539 540 *count = 0; 541 542 while (scan.p < scan.end) { 543 if (!pull_keyid(&scan, &keyid_str, &size, &pubkey)) { 544 return 0; 545 } 546 *count = *count + 1; 547 } 548 549 return 1; 550 } 551 552 static int fetch_signatures(unsigned char *json, int json_len, 553 struct cursor *arena) 554 { 555 struct cursor keyids; 556 struct cursor scan; 557 int size, transfers; 558 int n_to_fetch, n_keyids; 559 const char *keyid_str; 560 struct key_writer *to_fetch, *kw; 561 struct keyid_pubkey *pubkey; 562 CURLM *cm; 563 n_to_fetch = 0; 564 565 if (!gather_keyids(json, json_len, arena, &keyids)) { 566 return 0; 567 } 568 569 prepare_transfers(&cm); 570 571 scan.p = scan.start = keyids.start; 572 scan.end = keyids.end; 573 574 if (!count_keyids(scan, &n_keyids)) { 575 return 0; 576 } 577 578 if (!(to_fetch = cursor_alloc(arena, n_keyids * sizeof(struct key_writer)))) { 579 return 0; 580 } 581 582 while (scan.p < scan.end) { 583 584 if (!pull_keyid(&scan, &keyid_str, &size, &pubkey)) { 585 return 0; 586 } 587 588 if (get_cached_pubkey(keyid_str, strlen(keyid_str), arena, 589 &pubkey->data, &pubkey->len)) { 590 /* 591 printf("got cached pubkey of size %d... do something\n", 592 pubkey->len); 593 */ 594 continue; 595 } 596 597 pubkey->len = 0; 598 599 kw = &to_fetch[n_to_fetch++]; 600 init_errors(&kw->errs); 601 kw->keyid = keyid_str; 602 kw->arena = arena; 603 kw->pubkey = pubkey; 604 kw->flags = 0; 605 } 606 607 for (transfers = 0; transfers < MAX_PARALLEL && transfers < n_to_fetch; transfers++) { 608 add_signature_transfer(cm, &to_fetch[transfers]); 609 } 610 611 if (!perform_transfers(arena, cm, transfers, to_fetch, n_to_fetch)) { 612 return 0; 613 } 614 615 return 1; 616 } 617 618 int sigcheck(struct sigcheck *check) 619 { 620 int count = 0, res; 621 unsigned char *p, *start, *scratch; 622 size_t flen; 623 struct json_parser jsonp; 624 struct json_handlers handlers; 625 struct cursor out_cur; 626 627 scratch = malloc(SCRATCH_SIZE); 628 map_file(check->activity_file, &p, &flen); 629 630 init_json_handlers(&handlers); 631 make_cursor(scratch, scratch + SCRATCH_SIZE, &out_cur); 632 make_ubjson_handlers(&handlers, &out_cur); 633 init_json_parser(&jsonp, p, flen, &handlers); 634 635 if (!fetch_signatures(p, flen, &out_cur)) { 636 note_error(&jsonp.errs, "fatal error fetching signature"); 637 return 0; 638 } 639 640 make_cursor(scratch, scratch + SCRATCH_SIZE, &out_cur); 641 make_ubjson_handlers(&handlers, &out_cur); 642 init_json_parser(&jsonp, p, flen, &handlers); 643 644 start = out_cur.p; 645 while (parse_json(&jsonp)) { 646 count++; 647 fprintf(stderr, "[%d] parse success\r", count); 648 649 res = verify_signature(out_cur, out_cur); 650 651 if (res == 0) { 652 printf("bad signature #%d\n", count); 653 } else if (res == 1) { 654 printf("good signature #%d\r", count); 655 } 656 657 // overwrite last ubjson 658 out_cur.p = start; 659 } 660 661 printf("\n"); 662 663 munmap(p, flen); 664 665 return 1; 666 }