mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-20 10:39:49 +01:00
2b629b2509
This header will be used by multiple fuzz targets to fuzz Acts 1, 2, and 3 of the BOLT 8 handshake. We could make this header into a full library, but considering its narrow use let's try not to over-engineer things.
179 lines
5.9 KiB
C
179 lines
5.9 KiB
C
/* 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 <assert.h>
|
|
#include <ccan/io/io.h>
|
|
#include <ccan/str/hex/hex.h>
|
|
#include <common/setup.h>
|
|
#include <common/status.h>
|
|
#include <common/wireaddr.h>
|
|
#include <fcntl.h>
|
|
#include <tests/fuzz/libfuzz.h>
|
|
|
|
/* No randomness please, we want to replicate test vectors. */
|
|
#include <sodium/randombytes.h>
|
|
|
|
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 */
|