/* This header contains globals and helper functions used by all the * fuzz-connectd-handshake-act* fuzz targets. It also takes care of intercepting * io_read(), io_write(), and randombytes_buf(), so that the actual fuzz targets * only need to implement the test_read() and test_write() interceptors and the * run() function. */ #ifndef LIGHTNING_TESTS_FUZZ_CONNECTD_HANDSHAKE_H #define LIGHTNING_TESTS_FUZZ_CONNECTD_HANDSHAKE_H #include "config.h" #include #include #include #include #include #include #include #include /* No randomness please, we want to replicate test vectors. */ #include static void seeded_randombytes_buf(u8 *secret, size_t len); #define randombytes_buf(secret, len) seeded_randombytes_buf((secret), (len)) struct handshake; static struct io_plan * test_write(struct io_conn *conn, const void *data, size_t len, struct io_plan *(*next)(struct io_conn *, struct handshake *), struct handshake *h); static struct io_plan *test_read(struct io_conn *conn, void *data, size_t len, struct io_plan *(*next)(struct io_conn *, struct handshake *), struct handshake *h); #undef io_write #define io_write(conn, data, len, cb, cb_arg) \ test_write((conn), (data), (len), (cb), (cb_arg)) #undef io_read #define io_read(conn, data, len, cb, cb_arg) \ test_read((conn), (data), (len), (cb), (cb_arg)) /* Include handshake.c directly, so we can intercept io_write and io_read. */ #include "../../connectd/handshake.c" static struct pubkey init_pub, resp_pub; static struct privkey init_priv, resp_priv; static const u8 *bytes; static size_t bytes_remaining; static int read_count, write_count, ecdh_count; static u8 seed[randombytes_SEEDBYTES]; static struct pubkey pubkey(const char *str) { struct pubkey p; assert(pubkey_from_hexstr(str, strlen(str), &p)); return p; } static struct privkey privkey(const char *str) { struct privkey p; assert(hex_decode(str, strlen(str), &p, sizeof(p))); return p; } /* The init function used by all fuzz-connectd-handshake-act* targets. */ void init(int *argc, char ***argv) { int devnull = open("/dev/null", O_WRONLY); assert(devnull >= 0); status_setup_sync(devnull); common_setup("fuzzer"); /* These keys are copied from BOLT 8 test vectors, though we use them in * a different setting. * * BOLT #8: * * name: transport-responder successful handshake * ls.priv=2121212121212121212121212121212121212121212121212121212121212121 * ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7 * e.priv=0x2222222222222222222222222222222222222222222222222222222222222222 * e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27 */ init_priv = privkey( "2121212121212121212121212121212121212121212121212121212121212121"); init_pub = pubkey("028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa6" "12b469132ec7f7"); resp_priv = privkey( "2222222222222222222222222222222222222222222222222222222222222222"); resp_pub = pubkey("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf" "22285f1bae3f27"); } /* This function should be called at the start of each fuzz iteration to reset * globals to their initial values. */ static void init_globals(const u8 *data, size_t size) { bytes = data; bytes_remaining = size; read_count = 0; write_count = 0; ecdh_count = 0; /* Seed the RNG with fuzzer input. */ assert(bytes_remaining >= randombytes_SEEDBYTES); memcpy(seed, bytes, randombytes_SEEDBYTES); bytes += randombytes_SEEDBYTES; bytes_remaining -= randombytes_SEEDBYTES; } /* Replaces randombytes_buf with randomness generated deterministically from * the fuzzer-generated seed. */ static void seeded_randombytes_buf(u8 *secret, size_t len) { randombytes_buf_deterministic(secret, len, seed); /* Use the latest random bytes as seed for the next call. */ assert(len >= randombytes_SEEDBYTES); memcpy(seed, secret, randombytes_SEEDBYTES); } extern secp256k1_context *secp256k1_ctx; /* An interceptor that performs ECDH using the responder's private key. This is * expected to be called exactly once, by the responder, during Act 1. */ void ecdh(const struct pubkey *point, struct secret *ss) { ++ecdh_count; assert(ecdh_count == 1 && "too many calls to ecdh()"); assert(secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, resp_priv.secret.data, NULL, NULL) == 1); } /* A dummy function to call on handshake success. It should never be called * since the fuzzer should not be able to brute force a valid handshake. */ static struct io_plan * success(struct io_conn *conn UNUSED, const struct pubkey *them UNUSED, const struct wireaddr_internal *addr UNUSED, struct crypto_state *cs, struct oneshot *timeout UNUSED, enum is_websocket is_websocket UNUSED, void *unused UNUSED) { assert(false && "handshake unexpectedly succeeded"); } static struct io_plan *do_handshake(struct io_conn *conn, void *side) { struct wireaddr_internal dummy; dummy.itype = ADDR_INTERNAL_WIREADDR; dummy.u.wireaddr.wireaddr.addrlen = 0; if (side == (void *)RESPONDER) return responder_handshake(conn, &resp_pub, &dummy, NULL, NORMAL_SOCKET, success, NULL); return initiator_handshake(conn, &init_pub, &resp_pub, &dummy, NULL, NORMAL_SOCKET, success, NULL); } /* Attempts to do the indicated side of the handshake, using the test_read and * test_write interceptors implemented by the fuzz target. The handshake is * expected to always fail since the fuzzer should not be able to brute force a * valid handshake. */ static void handshake(enum bolt8_side side) { struct io_conn *conn = io_new_conn(tmpctx, -1, do_handshake, (void *)side); assert(!conn && "handshake unexpectedly succeeded"); } #endif /* LIGHTNING_TESTS_FUZZ_CONNECTD_HANDSHAKE_H */