From 2a1c57995f1e60a7ca3d5dd44b974aa2df9e9016 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 09:44:07 +0800 Subject: [PATCH 1/9] Use crypto_box_open_afternm() instead of crypto_box_afternm() when verifying vouch --- libcurvecpr/lib/server_recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcurvecpr/lib/server_recv.c b/libcurvecpr/lib/server_recv.c index 2bff2d7..8c84195 100644 --- a/libcurvecpr/lib/server_recv.c +++ b/libcurvecpr/lib/server_recv.c @@ -161,7 +161,7 @@ static int _handle_initiate (struct curvecpr_server *server, struct curvecpr_ses curvecpr_bytes_zero(vouch, 16); curvecpr_bytes_copy(vouch + 16, p_box->vouch, 48); - if (crypto_box_afternm(vouch, vouch, 64, nonce, s_new.my_global_their_global_key)) + if (crypto_box_open_afternm(vouch, vouch, 64, nonce, s_new.my_global_their_global_key)) return -EINVAL; if (!curvecpr_bytes_equal(vouch + 32, s_new.their_session_pk, 32)) From b1e74878fea6e79cbe8c840fc50a6f59418a7f54 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 09:45:32 +0800 Subject: [PATCH 2/9] Store client extension in session on server --- libcurvecpr/lib/server_recv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libcurvecpr/lib/server_recv.c b/libcurvecpr/lib/server_recv.c index 8c84195..25f1da7 100644 --- a/libcurvecpr/lib/server_recv.c +++ b/libcurvecpr/lib/server_recv.c @@ -171,6 +171,7 @@ static int _handle_initiate (struct curvecpr_server *server, struct curvecpr_ses /* All good, we can go ahead and submit the client for registration. */ s_new.their_session_nonce = curvecpr_bytes_unpack_uint64(p->nonce); curvecpr_bytes_copy(s_new.my_domain_name, p_box->server_domain_name, 256); + curvecpr_bytes_copy(s_new.their_extension, p->client_extension, 16); if (cf->ops.put_session(server, &s_new, priv, &s_new_stored)) return -EINVAL; /* This can fail for a variety of reasons that are up to From 1fbfcbae10859ffca2ee61e92bfc1b3280f51848 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 09:49:09 +0800 Subject: [PATCH 3/9] Return -EAGAIN if send hello packet fails --- libcurvecpr/lib/client.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libcurvecpr/lib/client.c b/libcurvecpr/lib/client.c index 39af1a1..282ffe5 100644 --- a/libcurvecpr/lib/client.c +++ b/libcurvecpr/lib/client.c @@ -9,6 +9,8 @@ #include +#include + static const unsigned char _zeros[128] = { 0 }; void curvecpr_client_new (struct curvecpr_client *client, const struct curvecpr_client_cf *cf) @@ -70,7 +72,8 @@ int curvecpr_client_connected (struct curvecpr_client *client) curvecpr_bytes_copy(p.box, data + 16, 80); } - cf->ops.send(client, (const unsigned char *)&p, sizeof(struct curvecpr_packet_hello)); + if (cf->ops.send(client, (const unsigned char *)&p, sizeof(struct curvecpr_packet_hello))) + return -EAGAIN; return 0; } From f52c134a16461c416d26cc024982782be4171be1 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 10:09:25 +0800 Subject: [PATCH 4/9] Added packet delivery tests --- libcurvecpr/test/Makefile.am | 3 + .../test/packet_delivery/packet_delivery.c | 1009 +++++++++++++++++ 2 files changed, 1012 insertions(+) create mode 100644 libcurvecpr/test/packet_delivery/packet_delivery.c diff --git a/libcurvecpr/test/Makefile.am b/libcurvecpr/test/Makefile.am index 3cd3509..b4696c0 100644 --- a/libcurvecpr/test/Makefile.am +++ b/libcurvecpr/test/Makefile.am @@ -21,4 +21,7 @@ messager_test_timeout_callback_fires_SOURCES = messager/test_timeout_callback_fi check_PROGRAMS += util/test_nanoseconds util_test_nanoseconds_SOURCES = util/test_nanoseconds.c +check_PROGRAMS += packet_delivery/packet_delivery +packet_delivery_packet_delivery_SOURCES = packet_delivery/packet_delivery.c + TESTS = $(check_PROGRAMS) diff --git a/libcurvecpr/test/packet_delivery/packet_delivery.c b/libcurvecpr/test/packet_delivery/packet_delivery.c new file mode 100644 index 0000000..739a290 --- /dev/null +++ b/libcurvecpr/test/packet_delivery/packet_delivery.c @@ -0,0 +1,1009 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + + +const unsigned char server_extension[16] = {16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19}; +const unsigned char client_extension[16] = {20,20,20,20,21,21,21,21,22,22,22,22,23,23,23,23}; +const char domain_name[] = "www.example.com"; + + + +/* Holds client, server, and data set and read by the ops callbacks in curvecpr_client_cf and curvecpr_server_cf */ +struct test_helper { + + struct curvecpr_server server; + struct curvecpr_client client; + + + /* Last decrypted message received by server and client, respectively. */ + unsigned char server_recv[1088]; + size_t server_recv_size; + int num_server_recv; + + unsigned char client_recv[1088]; + size_t client_recv_size; + int num_client_recv; + + + /* Encrypted packet on route between server and client or vice versa. */ + unsigned char transmission[1184]; + size_t transmission_size; + + /* Increased each time server or client sends a packet */ + int num_server_sent; + int num_client_sent; + + + struct curvecpr_session sessions[2]; + int num_sessions; +}; + + + +/********* Client callbacks ***********/ + + +static int client_send (struct curvecpr_client *client, const unsigned char *buf, size_t num) +{ + struct test_helper *t; + + fail_unless(client); + fail_unless(buf); + + t = client->cf.priv; + fail_unless(t); + + fail_if(num > sizeof(t->transmission)); + fail_if(num > 96 + 1088); + fail_if(num < 96 + 16); + fail_unless(num % 16 == 0); + + curvecpr_bytes_copy(t->transmission, buf, num); + t->transmission_size = num; + t->num_client_sent++; + + return 0; +} + +static int client_recv (struct curvecpr_client *client, const unsigned char *buf, size_t num) +{ + struct test_helper *t; + + fail_unless(client); + fail_unless(buf); + + t = client->cf.priv; + fail_if(num > sizeof(t->client_recv)); + + curvecpr_bytes_copy(t->client_recv, buf, num); + t->client_recv_size = num; + t->num_client_recv++; + + return 0; +} + +static int client_next_nonce (struct curvecpr_client *client, unsigned char *destination, size_t num) +{ + fail_if(num != 16); + randombytes(destination, num); + return 0; +} + + +/********* Server callbacks ***********/ + +static int server_put_session (struct curvecpr_server *server, const struct curvecpr_session *s, void *priv, struct curvecpr_session **s_stored) +{ + struct test_helper *t; + + fail_unless(server); + fail_unless(s); + fail_unless(s_stored); + + t = server->cf.priv; + fail_unless(t); + + for (int i = 0; i < t->num_sessions; i++) + { + fail_if(curvecpr_bytes_equal(t->sessions[i].their_session_pk, s->their_session_pk, 32)); + } + + fail_unless(t->num_sessions < (int)(sizeof(t->sessions)/sizeof(t->sessions[0]))); + + curvecpr_bytes_copy(&t->sessions[t->num_sessions], s, sizeof(struct curvecpr_session)); + *s_stored = &t->sessions[t->num_sessions]; + t->num_sessions++; + + return 0; +} + +static int server_get_session(struct curvecpr_server *server, const unsigned char their_session_pk[32], struct curvecpr_session **s_stored) +{ + struct test_helper *t; + + fail_unless(server); + fail_unless(s_stored); + + t = server->cf.priv; + fail_unless(t); + + for (int i = 0; i < t->num_sessions; i++) + { + if (curvecpr_bytes_equal(t->sessions[i].their_session_pk, their_session_pk, 32)) + { + *s_stored = &t->sessions[i]; + return 0; + } + } + + return 1; +} + +static int server_send (struct curvecpr_server *server, struct curvecpr_session *s, void *priv, const unsigned char *buf, size_t num) +{ + struct test_helper *t; + + fail_unless(server); + fail_unless(buf); + + t = server->cf.priv; + fail_unless(t); + + fail_if(num > sizeof(t->transmission)); + fail_if(num > 64 + 1088); + fail_if(num < 64 + 16); + fail_unless(num == 200 /* cookie packet is special case */ || num % 16 == 0); + + curvecpr_bytes_copy(t->transmission, buf, num); + t->transmission_size = num; + t->num_server_sent++; + + return 0; +} + +static int server_recv (struct curvecpr_server *server, struct curvecpr_session *s, void *priv, const unsigned char *buf, size_t num) +{ + struct test_helper *t; + + fail_unless(server); + fail_unless(s); + fail_unless(buf); + + t = server->cf.priv; + fail_if(num > sizeof(t->server_recv)); + + curvecpr_bytes_copy(t->server_recv, buf, num); + t->server_recv_size = num; + t->num_server_recv++; + + return 0; + +} + +static int server_next_nonce (struct curvecpr_server *server, unsigned char *destination, size_t num) +{ + fail_if(num != 16); + randombytes(destination, num); + return 0; +} + + + +/********* Initialization functions ************/ + +static void new_client (struct curvecpr_client *client, const unsigned char server_pk[32], void *priv) +{ + unsigned char client_pk[32]; + unsigned char client_sk[32]; + crypto_box_keypair(client_pk, client_sk); + + struct curvecpr_client_cf client_cf = { + .ops = { + .send = &client_send, + .recv = &client_recv, + .next_nonce = &client_next_nonce + }, + .priv = priv + }; + curvecpr_bytes_copy(client_cf.my_extension, client_extension, 16); + curvecpr_bytes_copy(client_cf.my_global_pk, client_pk, 32); + curvecpr_bytes_copy(client_cf.my_global_sk, client_sk, 32); + curvecpr_bytes_copy(client_cf.their_extension, server_extension, 16); + curvecpr_bytes_copy(client_cf.their_global_pk, server_pk, 32); + curvecpr_util_encode_domain_name(client_cf.their_domain_name, domain_name); + + curvecpr_client_new(client, &client_cf); +} + + +static void new_server (struct curvecpr_server *server, void *priv) +{ + unsigned char server_pk[32]; + unsigned char server_sk[32]; + crypto_box_keypair(server_pk, server_sk); + + struct curvecpr_server_cf server_cf = { + .ops = { + .put_session = &server_put_session, + .get_session = &server_get_session, + .send = &server_send, + .recv = &server_recv, + .next_nonce = &server_next_nonce + }, + .priv = priv + }; + curvecpr_bytes_copy(server_cf.my_extension, server_extension, 16); + curvecpr_bytes_copy(server_cf.my_global_pk, server_pk, 32); + curvecpr_bytes_copy(server_cf.my_global_sk, server_sk, 32); + + curvecpr_server_new(server, &server_cf); +} + + + +static void new_test_helper (struct test_helper *t) +{ + new_server(&t->server, t); + new_client(&t->client, t->server.cf.my_global_pk, t); + + curvecpr_bytes_zero(t->server_recv, sizeof(t->server_recv)); + t->server_recv_size = 0; + t->num_server_recv = 0; + + curvecpr_bytes_zero(t->client_recv, sizeof(t->client_recv)); + t->client_recv_size = 0; + t->num_client_recv = 0; + + curvecpr_bytes_zero(t->transmission, sizeof(t->transmission)); + t->transmission_size = 0; + t->num_server_sent = 0; + t->num_client_sent = 0; + + curvecpr_bytes_zero(t->sessions, sizeof(t->sessions)); + t->num_sessions = 0; +} + + + +/********** Tests functions *********/ + +static size_t client_create_hello (struct test_helper *helper, unsigned char packet[2000]) +{ + int num_client_sent = helper->num_client_sent; + + fail_unless(curvecpr_client_connected(&helper->client) == 0); + fail_unless(helper->num_client_sent == num_client_sent+1); + fail_unless(helper->transmission_size == 224); + + curvecpr_bytes_copy(packet, helper->transmission, helper->transmission_size); + + return helper->transmission_size; +} + +static size_t server_receive_hello_and_create_cookie (struct test_helper *helper, unsigned char packet[2000], size_t size) +{ + int num_server_sent = helper->num_server_sent; + int num_server_recv = helper->num_server_recv; + int num_sessions = helper->num_sessions; + + fail_unless(size == 224); + fail_unless(curvecpr_server_recv(&helper->server, 0, packet, size, 0) == 0); + fail_unless(helper->num_server_recv == num_server_recv); + fail_unless(helper->num_server_sent == num_server_sent+1); + fail_unless(helper->transmission_size == 200); + fail_unless(helper->num_sessions == num_sessions); + + curvecpr_bytes_copy(packet, helper->transmission, helper->transmission_size); + + return helper->transmission_size; +} + +static void client_receive_cookie (struct test_helper *helper, unsigned char packet[2000], size_t size) +{ + fail_unless(size == 200); + fail_unless(curvecpr_client_recv(&helper->client, packet, size) == 0); + fail_unless(helper->num_client_recv == 0); + fail_unless(helper->num_client_sent > 0); +} + +static size_t client_send_initiate (struct test_helper* helper, unsigned char packet[2000], const void *message, size_t message_size) +{ + int num_client_sent = helper->num_client_sent; + fail_unless(curvecpr_client_send(&helper->client, message, message_size) == 0); + fail_unless(helper->num_client_sent == num_client_sent+1); + fail_unless(helper->transmission_size == 544+message_size); + + curvecpr_bytes_copy(packet, helper->transmission, helper->transmission_size); + + return helper->transmission_size; +} + +static void server_receive_initiate (struct test_helper *helper, unsigned char packet[2000], size_t size, const void *expected_message, size_t message_size) +{ + int num_server_recv = helper->num_server_recv; + + fail_unless(curvecpr_server_recv(&helper->server, 0, packet, size, 0) == 0); + fail_unless(helper->num_server_recv == num_server_recv+1); + fail_unless(helper->server_recv_size == message_size); + fail_unless(curvecpr_bytes_equal(helper->server_recv, expected_message, message_size)); + fail_unless(helper->num_sessions == 1); +} + +static size_t server_send_message (struct test_helper *helper, unsigned char packet[2000], const void *message, size_t message_size) +{ + int num_server_sent = helper->num_server_sent; + fail_unless(num_server_sent >= 1); + + fail_unless(curvecpr_server_send(&helper->server, &helper->sessions[0], 0, (const unsigned char*)message, message_size) == 0); + fail_unless(helper->num_server_sent = num_server_sent+1); + fail_unless(helper->transmission_size == 64+message_size); + + curvecpr_bytes_copy(packet, helper->transmission, helper->transmission_size); + + return helper->transmission_size; +} + +static void client_receive_message(struct test_helper *helper, unsigned char packet[2000], size_t size, const void *expected_message, size_t message_size) { + fail_unless(curvecpr_client_recv(&helper->client, packet, size) == 0); + fail_unless(helper->num_client_recv == 1); + fail_unless(helper->client_recv_size == message_size); + fail_unless(curvecpr_bytes_equal(helper->client_recv, expected_message, message_size)); +} + +static size_t client_send_message (struct test_helper *helper, unsigned char packet[2000], const void *message, size_t message_size) +{ + int packets_sent_before = helper->num_client_sent; + + /* Test code must send hello and initiate before sending normal message */ + fail_unless(packets_sent_before >= 2); + + fail_unless(curvecpr_client_send(&helper->client, message, message_size) == 0); + + fail_unless(helper->num_client_sent == packets_sent_before+1); + fail_unless(helper->transmission_size == 96+message_size); + + curvecpr_bytes_copy(packet, helper->transmission, helper->transmission_size); + + return helper->transmission_size; +} + +static void server_receive_message (struct test_helper *helper, unsigned char packet[2000], size_t size, const void *expected_message, size_t message_size) +{ + int packets_recv_before = helper->num_server_recv; + + fail_unless(curvecpr_server_recv(&helper->server, 0, packet, size, 0) == 0); + fail_unless(helper->num_server_recv == packets_recv_before+1); + fail_unless(helper->server_recv_size+96 == size); +} + + +/* Because of lazyness I rely on the inner workings of libcurvecpr here. Right + now, nothing changes in the client nor server structs when receiving invalid + messages. The code wouldn't necessarily be broken just because something + changed in those structs though, one could imagein adding a counter + of invalid received packets or a last_error or something, which would + brake this code. + Also note that the comparison is shallow, which works fine right now, but + might brake if the structs get more complicated. */ +static void server_fail_receive (struct test_helper *helper, unsigned char packet[2000], size_t size) { + struct test_helper helper_copy; + curvecpr_bytes_copy(&helper_copy, helper, sizeof(struct test_helper)); + + fail_if(curvecpr_server_recv(&helper->server, 0, packet, size, 0) == 0); + fail_unless(curvecpr_bytes_equal(&helper_copy, helper, sizeof(struct test_helper))); +} + +static void client_fail_receive (struct test_helper *helper, unsigned char packet[2000], size_t size) +{ + struct test_helper helper_copy; + curvecpr_bytes_copy(&helper_copy, helper, sizeof(struct test_helper)); + + fail_if(curvecpr_client_recv(&helper->client, packet, size) == 0); + fail_unless(curvecpr_bytes_equal(&helper_copy, helper, sizeof(struct test_helper))); +} + + + +/********** Tests ************/ + + +#define TEST_SETUP \ + unsigned char packet[2000]; \ + size_t size; \ + struct test_helper helper; \ + new_test_helper(&helper); + +#define TEST_MODIFY_SETUP \ + unsigned char modified_packet[2000]; \ + TEST_SETUP + + +START_TEST (test_normal_run) +{ + TEST_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + + size = server_send_message(&helper, packet, "Hello world too", 16); + client_receive_message(&helper, packet, size, "Hello world too", 16); + + size = client_send_message(&helper, packet, "Hello world 2!!", 16); + server_receive_message(&helper, packet, size, "Hello world 2!!", 16); +} +END_TEST + + +START_TEST (test_all_initiate_message_sizes) +{ + unsigned char message[640]; + TEST_SETUP + + randombytes(message, sizeof(message)); + + for (size_t i = 16; i <= 640; i += 16) { + new_test_helper(&helper); + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + size = client_send_initiate(&helper, packet, message, i); + server_receive_initiate(&helper, packet, size, message, i); + } +} +END_TEST + + +START_TEST (test_all_server_message_sizes) +{ + unsigned char message[1088]; + TEST_SETUP + + randombytes(message, sizeof(message)); + + for (size_t i = 16; i <= 1088; i += 16) { + new_test_helper(&helper); + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + size = client_send_initiate(&helper, packet, message, 16); + server_receive_initiate(&helper, packet, size, message, 16); + + size = server_send_message(&helper, packet, message, i); + client_receive_message(&helper, packet, size, message, i); + } +} +END_TEST + + +START_TEST (test_all_client_message_sizes) +{ + unsigned char message[1088]; + TEST_SETUP + + randombytes(message, sizeof(message)); + + for (size_t i = 16; i <= 1088; i += 16) { + new_test_helper(&helper); + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + + size = server_send_message(&helper, packet, "Hello world too", 16); + client_receive_message(&helper, packet, size, "Hello world too", 16); + + size = client_send_message(&helper, packet, message, i); + server_receive_message(&helper, packet, size, message, i); + } +} +END_TEST + + +START_TEST (test_can_do_several_handshake_packets) +{ + /* Test lost packets during initial hand shaking */ + TEST_SETUP + + /* Hello gets lost */ + size = client_create_hello(&helper, packet); + + /* Hello gets through but cookie packet reply gets lost */ + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + + /* Hello and cookie get through */ + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + /* First message reply from server gets lost */ + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + size = server_send_message(&helper, packet, "abcdefghijklmno", 16); + + /* Second initiate from client gets lost */ + size = client_send_initiate(&helper, packet, "Hello world 2!!", 16); + + /* Third time all packets get through */ + size = client_send_initiate(&helper, packet, "Hello world too", 16); + server_receive_initiate(&helper, packet, size, "Hello world too", 16); + size = server_send_message(&helper, packet, "Something else.", 16); + client_receive_message(&helper, packet, size, "Something else.", 16); +} +END_TEST + + +START_TEST (test_reject_modified_hello_packet) +{ + TEST_MODIFY_SETUP + + size = client_create_hello(&helper, packet); + + /* Test modifying one byte */ + for (size_t i = 0; i < size; i++) + { + if (i >= 24 && i < 24+16) + /* Client extension */ + continue; + + if (i >= 72 && i < 72+64) + /* Non-verified zeros */ + continue; + + curvecpr_bytes_copy(modified_packet, packet, sizeof(modified_packet)); + modified_packet[i] ^= (i+1); + server_fail_receive(&helper, modified_packet, size); + } + + /* Test different message sizes */ + for (size_t i = 0; i < sizeof(modified_packet); i++) + { + if (i == 224) + continue; + + server_fail_receive(&helper, packet, i); + } + + + server_receive_hello_and_create_cookie(&helper, packet, size); +} +END_TEST + + +START_TEST (test_reject_modified_cookie_packet) +{ + TEST_MODIFY_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + + /* Test modify one byte */ + for (size_t i = 0; i < size; i++) + { + curvecpr_bytes_copy(modified_packet, packet, sizeof(modified_packet)); + modified_packet[i] ^= (i+1); + client_fail_receive(&helper, modified_packet, size); + } + + /* Test different message sizes */ + for (size_t i = 0; i < sizeof(modified_packet); i++) + { + if (i == size) + continue; + + client_fail_receive(&helper, packet, i); + } + + client_receive_cookie(&helper, packet, size); +} +END_TEST + + +START_TEST (test_reject_modified_init_packet) +{ + TEST_MODIFY_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + + /* Test modify one byte */ + for (size_t i = 0; i < 544+16; i++) + { + if (i >= 24 && i < 40) + /* Client extension */ + continue; + + curvecpr_bytes_copy(modified_packet, packet, sizeof(modified_packet)); + /* Do i/4 because i=255 or 511 would not change the packet otherwise. */ + modified_packet[i] ^= (i/4+1); + server_fail_receive(&helper, modified_packet, size); + } + + /* Test different message sizes */ + for (size_t i = 0; i < sizeof(modified_packet); i++) + { + if (i == 544+16) + continue; + + server_fail_receive(&helper, packet, i); + } + + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); +} +END_TEST + + +START_TEST (test_client_reject_modified_message_packet) +{ + TEST_MODIFY_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + size = server_send_message(&helper, packet, "Hello world too", 16); + + /* Test modify one byte */ + for (size_t i = 0; i < 64+16; i++) + { + curvecpr_bytes_copy(modified_packet, packet, sizeof(modified_packet)); + modified_packet[i] ^= (i+1); + client_fail_receive(&helper, modified_packet, size); + } + + /* Test different message sizes */ + for (size_t i = 0; i < sizeof(modified_packet); i++) + { + if (i == 64+16) + continue; + + client_fail_receive(&helper, packet, i); + } + + client_receive_message(&helper, packet, size, "Hello world too", 16); +} +END_TEST + + +START_TEST (test_server_reject_modified_message_packet) +{ + TEST_MODIFY_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + size = server_send_message(&helper, packet, "Hello world too", 16); + client_receive_message(&helper, packet, size, "Hello world too", 16); + size = client_send_message(&helper, packet, "Hello world 2!!", 16); + + /* Test modify one byte */ + for (size_t i = 0; i < size; i++) + { + if (i >= 24 && i < 24+16) + /* Client extension */ + continue; + + curvecpr_bytes_copy(modified_packet, packet, sizeof(modified_packet)); + modified_packet[i] ^= (i+1); + server_fail_receive(&helper, modified_packet, size); + } + + /* Test different message sizes */ + for (size_t i = 0; i < sizeof(modified_packet); i++) + { + if (i == size) + continue; + + server_fail_receive(&helper, packet, i); + } + + server_receive_message(&helper, packet, size, "Hello world 2!!", 16); +} +END_TEST + + +START_TEST (test_reject_modified_cookie) +{ + TEST_SETUP + + for (size_t i = 0; i < sizeof(helper.client.negotiated_cookie); i++) + { + new_test_helper(&helper); + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + helper.client.negotiated_cookie[i] ^= (unsigned char)(i+1); + + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_fail_receive(&helper, packet, size); + } +} +END_TEST + + +START_TEST (test_reject_randomly_modified_vouch) +{ + TEST_SETUP + + for (size_t i = 0; i < sizeof(helper.client.negotiated_vouch); i++) + { + new_test_helper(&helper); + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + helper.client.negotiated_vouch[i] ^= (unsigned char)(i+1); + + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_fail_receive(&helper, packet, size); + } +} +END_TEST + + +START_TEST (test_reject_vouch_containing_wrong_key) +{ + TEST_SETUP + + unsigned char nonce[24]; + + unsigned char other_pk[32]; + unsigned char other_sk[32]; + + unsigned char irrelevant_pk[32]; + unsigned char irrelevant_sk[32]; + + unsigned char my_irrelevant_their_global_key[32]; + + /* Generate another keypair and pretend we only know public key */ + crypto_box_keypair(other_pk, other_sk); + curvecpr_bytes_zero(other_sk, sizeof(other_sk)); + + /* This doesn't really matter, I just need a random secret key to do encryption */ + crypto_box_keypair(irrelevant_pk, irrelevant_sk); + + /* Do hello and cookie packets */ + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + + + /* Replace the vouch with the session key encrypted and authenticated to + server's global key from irrelevant private key. */ + + crypto_box_beforenm(my_irrelevant_their_global_key, helper.client.session.their_global_pk, irrelevant_sk); + + curvecpr_bytes_zero(helper.client.negotiated_vouch, 32); + + curvecpr_bytes_copy(helper.client.negotiated_vouch + 32, helper.client.session.my_session_pk, 32); + + curvecpr_bytes_copy(nonce, "CurveCPV", 8); + randombytes(nonce+8, 16); + + crypto_box_afternm(helper.client.negotiated_vouch, helper.client.negotiated_vouch, 64, nonce, my_irrelevant_their_global_key); + curvecpr_bytes_copy(helper.client.negotiated_vouch, nonce+8, 16); + + curvecpr_bytes_copy(helper.client.cf.my_global_pk, other_pk, 32); + + + /* Send the packet with modified vouch and verify that the server won't accept it. */ + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_fail_receive(&helper, packet, size); +} +END_TEST + + +START_TEST (test_reject_replays) +{ + TEST_SETUP + + size = client_create_hello(&helper, packet); + + size = server_receive_hello_and_create_cookie(&helper, packet, size); + + client_receive_cookie(&helper, packet, size); + client_fail_receive(&helper, packet, size); + + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); + server_fail_receive(&helper, packet, size); + + size = server_send_message(&helper, packet, "Hello world too", 16); + client_receive_message(&helper, packet, size, "Hello world too", 16); + client_fail_receive(&helper, packet, size); + + size = client_send_message(&helper, packet, "Hello world 2!!", 16); + server_receive_message(&helper, packet, size, "Hello world 2!!", 16); + server_fail_receive(&helper, packet, size); +} +END_TEST + + +START_TEST (test_1_minute_cookie_accepted) +{ + TEST_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + + /* "1 minute"... */ + curvecpr_server_refresh_temporal_keys(&helper.server); + + server_receive_initiate(&helper, packet, size, "Hello world!!!!", 16); +} +END_TEST + + +START_TEST (test_2_minute_cookie_rejected) +{ + TEST_SETUP + + size = client_create_hello(&helper, packet); + size = server_receive_hello_and_create_cookie(&helper, packet, size); + client_receive_cookie(&helper, packet, size); + size = client_send_initiate(&helper, packet, "Hello world!!!!", 16); + + /* "2 minutes"... */ + curvecpr_server_refresh_temporal_keys(&helper.server); + curvecpr_server_refresh_temporal_keys(&helper.server); + + server_fail_receive(&helper, packet, size); +} +END_TEST + + +/****************** Deterministic RNG ********************/ +/* Use a deterministic random number generator during tests so we always test + the same thing. UNDER NO CIRCUMSTANCES USE IN PRODUCTION */ + + +static uint32_t seed = 0; + +static const char *deterministic_rng_implementation_name (void) +{ + return "DeterministicTestRNG"; +} + +static void deterministic_rng_buf (void * const buf, const size_t size) +{ + unsigned char temp[64]; + size_t bytes_to_copy_this_iteration; + size_t bytes_left = size; + unsigned char *output = (unsigned char*)buf; + + + /* Hopefully generates compiler error if this function ever ends up outside test code. */ + fail_unless(1); + + + while (bytes_left > 0) + { + crypto_hash_sha512(temp, (const unsigned char*)&seed, sizeof(seed)); + seed++; + + bytes_to_copy_this_iteration = bytes_left < sizeof(temp) ? bytes_left : sizeof(temp); + curvecpr_bytes_copy(output, temp, bytes_to_copy_this_iteration); + bytes_left -= bytes_to_copy_this_iteration; + output += bytes_to_copy_this_iteration; + } +} + +static uint32_t deterministic_rng_random (void) +{ + uint32_t r; + deterministic_rng_buf(&r, sizeof(r)); + return r; +} + +static void deterministic_rng_stir (void) +{ +} + +/* Copied from libsodium randombytes_sysrandom_uniform, which in turn + is derived from OpenBSD */ +static uint32_t deterministic_rng_uniform (const uint32_t upper_bound) +{ + uint32_t min; + uint32_t r; + + if (upper_bound < 2) { + return 0; + } + min = (uint32_t) (-upper_bound % upper_bound); + for (;;) { + r = deterministic_rng_random(); + if (r >= min) { + break; + } + } + return r % upper_bound; +} + +static int deterministic_rng_close(void) { + return 0; +} + + + + +/******************* main *****************/ + +#define ADD_TEST(fn) \ + TCase *tc_ ## fn = tcase_create(#fn); \ + tcase_add_test(tc_ ## fn, (fn)); \ + suite_add_tcase(s, tc_ ## fn) + +int main (void) +{ + struct randombytes_implementation rand_impl = { + .implementation_name = &deterministic_rng_implementation_name, + .random = &deterministic_rng_random, + .stir = deterministic_rng_stir, + .uniform = deterministic_rng_uniform, + .buf = deterministic_rng_buf, + .close = deterministic_rng_close + }; + + randombytes_set_implementation(&rand_impl); + + + Suite *s = suite_create("packet_delivery"); + + ADD_TEST(test_normal_run); + + ADD_TEST(test_all_initiate_message_sizes); + ADD_TEST(test_all_server_message_sizes); + ADD_TEST(test_all_client_message_sizes); + + ADD_TEST(test_can_do_several_handshake_packets); + + ADD_TEST(test_reject_modified_hello_packet); + ADD_TEST(test_reject_modified_cookie_packet); + ADD_TEST(test_reject_modified_init_packet); + ADD_TEST(test_client_reject_modified_message_packet); + ADD_TEST(test_server_reject_modified_message_packet); + + ADD_TEST(test_reject_modified_cookie); + ADD_TEST(test_reject_randomly_modified_vouch); + ADD_TEST(test_reject_vouch_containing_wrong_key); + + ADD_TEST(test_reject_replays); + + ADD_TEST(test_1_minute_cookie_accepted); + ADD_TEST(test_2_minute_cookie_rejected); + + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + int failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return failed ? 1 : 0; +} From 06446ab7ba74a2697d817d8cc8a440a2194f9b4b Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 10:11:44 +0800 Subject: [PATCH 5/9] .gitignore for files generated during packet delivery testing --- libcurvecpr/test/packet_delivery/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 libcurvecpr/test/packet_delivery/.gitignore diff --git a/libcurvecpr/test/packet_delivery/.gitignore b/libcurvecpr/test/packet_delivery/.gitignore new file mode 100644 index 0000000..1d5d000 --- /dev/null +++ b/libcurvecpr/test/packet_delivery/.gitignore @@ -0,0 +1 @@ +/packet_delivery From c046e8781b8c375da8cfc249a9ac8f4dfe93a96b Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 11:14:13 +0800 Subject: [PATCH 6/9] Made chicago's get time function a callback in order to prepare for unit testing --- libcurvecpr/include/curvecpr/chicago.h | 19 +++++++++++- libcurvecpr/include/curvecpr/messager.h | 31 +++++++++++++++++++ libcurvecpr/include/curvecpr/util.h | 2 +- libcurvecpr/lib/chicago.c | 7 +++-- libcurvecpr/lib/messager.c | 12 +++++-- libcurvecpr/lib/util.c | 2 +- .../messager/test_new_configures_object.c | 4 ++- ...est_recv_requests_removal_from_sendmarkq.c | 4 ++- ..._with_1_failure_moves_message_from_sendq.c | 4 ++- .../messager/test_timeout_callback_fires.c | 4 ++- libcurvecpr/test/util/test_nanoseconds.c | 4 +-- 11 files changed, 79 insertions(+), 14 deletions(-) diff --git a/libcurvecpr/include/curvecpr/chicago.h b/libcurvecpr/include/curvecpr/chicago.h index 5a55c6c..b6e2748 100644 --- a/libcurvecpr/include/curvecpr/chicago.h +++ b/libcurvecpr/include/curvecpr/chicago.h @@ -1,6 +1,21 @@ #ifndef __CURVECPR_CHICAGO_H #define __CURVECPR_CHICAGO_H +struct curvecpr_chicago_ops { + /* Get current time in nanoseconds. The time 0 can be any time as long + as present time is not too close to it (>100 seconds should be ok) and + it doesn't change during the lifetime of this chicago instance (i.e. + it doesn't have to be for example 1970-01-01, but can be the number of + nanoseconds since the computer booted+100s or whatever). + You can pass in curvecpr_util_nanoseconds() here if you want. */ + long long (*get_nanoseconds)(void *priv); +}; + +struct curvecpr_chicago_cf { + struct curvecpr_chicago_ops ops; + void *priv; +}; + struct curvecpr_chicago { long long clock; @@ -24,9 +39,11 @@ struct curvecpr_chicago { long long ns_last_edge; long long ns_last_doubling; long long ns_last_panic; + + struct curvecpr_chicago_cf cf; }; -void curvecpr_chicago_new (struct curvecpr_chicago *chicago); +void curvecpr_chicago_new (struct curvecpr_chicago *chicago, const struct curvecpr_chicago_cf *cf); void curvecpr_chicago_refresh_clock (struct curvecpr_chicago *chicago); void curvecpr_chicago_on_timeout (struct curvecpr_chicago *chicago); void curvecpr_chicago_on_recv (struct curvecpr_chicago *chicago, long long ns_sent); diff --git a/libcurvecpr/include/curvecpr/messager.h b/libcurvecpr/include/curvecpr/messager.h index d651f8f..332e034 100644 --- a/libcurvecpr/include/curvecpr/messager.h +++ b/libcurvecpr/include/curvecpr/messager.h @@ -10,6 +10,28 @@ struct curvecpr_messager; + +/* The callbacks you need to implement to get a reliable stream transport. + You need to implement the three data structures mentioned below. + Terminology: + sendq (send queue): The data waiting to get sent. When you want to send + something, you divide it into curvecpr_block:s (each at most + messager->my_maximum_send_bytes in size) and put it in this + queue (you have to do this yourself). When you call + curvecpr_messager_process_sendq() it will check + this queue for data ready to be sent. It might not happen + immediately, but at a later invocation depending on the + decongestion algorithm and packets waiting to be resent etc. + sendmarkq (sent-to-be-marked queue): When a curvecpr_block is sent + (using the send() callback function), it is moved from sendq to + sendmarkq (if it wasn't moved earlier and this is just a resend). + It waits here until it has been acknowledged by the recipient. + recvmarkq (received-to-be-marked): Received curvecpr_block:s are stored + here until we have sent an ACK (which happens right after they are + stored actually). You need to assemble the stream data from this + data structure yourself. + +*/ struct curvecpr_messager_ops { int (*sendq_head)(struct curvecpr_messager *messager, struct curvecpr_block **block_stored); int (*sendq_move_to_sendmarkq)(struct curvecpr_messager *messager, const struct curvecpr_block *block, struct curvecpr_block **block_stored); @@ -19,10 +41,14 @@ struct curvecpr_messager_ops { the time at which they were last sent. */ int (*sendmarkq_head)(struct curvecpr_messager *messager, struct curvecpr_block **block_stored); int (*sendmarkq_get)(struct curvecpr_messager *messager, crypto_uint32 acknowledging_id, struct curvecpr_block **block_stored); + + /* This is called for all ranges in incoming messages's acknowledge structure */ int (*sendmarkq_remove_range)(struct curvecpr_messager *messager, unsigned long long start, unsigned long long end); unsigned char (*sendmarkq_is_full)(struct curvecpr_messager *messager); + /* This is called once for each message coming in that is not a pure acknowledgement */ int (*recvmarkq_put)(struct curvecpr_messager *messager, const struct curvecpr_block *block, struct curvecpr_block **block_stored); + int (*recvmarkq_get_nth_unacknowledged)(struct curvecpr_messager *messager, unsigned int n, struct curvecpr_block **block_stored); unsigned char (*recvmarkq_is_empty)(struct curvecpr_messager *messager); int (*recvmarkq_remove_range)(struct curvecpr_messager *messager, unsigned long long start, unsigned long long end); @@ -30,6 +56,7 @@ struct curvecpr_messager_ops { int (*send)(struct curvecpr_messager *messager, const unsigned char *buf, size_t num); void (*put_next_timeout)(struct curvecpr_messager *messager, const long long timeout_ns); + long long (*get_nanoseconds)(void *priv); }; struct curvecpr_messager_cf { @@ -50,6 +77,9 @@ struct curvecpr_messager { unsigned char my_eof; unsigned char my_final; + /* The client can only send 512 bytes/message until we know that an + initiation packet has reached the server. Then this variable is raised + to 1024 bytes. The server can send 1024 bytes/message from the start. */ size_t my_maximum_send_bytes; crypto_uint64 my_sent_bytes; @@ -68,6 +98,7 @@ struct curvecpr_messager { void curvecpr_messager_new (struct curvecpr_messager *messager, const struct curvecpr_messager_cf *cf, unsigned char client); int curvecpr_messager_recv (struct curvecpr_messager *messager, const unsigned char *buf, size_t num); +/* Call this function on timeout and when you have added things to sendq if it was empty. */ int curvecpr_messager_process_sendq (struct curvecpr_messager *messager); long long curvecpr_messager_next_timeout (struct curvecpr_messager *messager); diff --git a/libcurvecpr/include/curvecpr/util.h b/libcurvecpr/include/curvecpr/util.h index a96433b..9318846 100644 --- a/libcurvecpr/include/curvecpr/util.h +++ b/libcurvecpr/include/curvecpr/util.h @@ -2,7 +2,7 @@ #define __CURVECPR_UTIL_H long long curvecpr_util_random_mod_n (long long n); -long long curvecpr_util_nanoseconds (void); +long long curvecpr_util_nanoseconds (void *); int curvecpr_util_encode_domain_name (unsigned char *destination, const char *source); #endif diff --git a/libcurvecpr/lib/chicago.c b/libcurvecpr/lib/chicago.c index 5a73e6f..c11bf5d 100644 --- a/libcurvecpr/lib/chicago.c +++ b/libcurvecpr/lib/chicago.c @@ -123,10 +123,13 @@ static void _update_on_timeout (struct curvecpr_chicago *chicago) } } -void curvecpr_chicago_new (struct curvecpr_chicago *chicago) +void curvecpr_chicago_new (struct curvecpr_chicago *chicago, const struct curvecpr_chicago_cf *cf) { curvecpr_bytes_zero(chicago, sizeof(struct curvecpr_chicago)); + if (cf) + curvecpr_bytes_copy(&chicago->cf, cf, sizeof(struct curvecpr_chicago_cf)); + curvecpr_chicago_refresh_clock(chicago); chicago->rtt_latest = 0; @@ -153,7 +156,7 @@ void curvecpr_chicago_new (struct curvecpr_chicago *chicago) void curvecpr_chicago_refresh_clock (struct curvecpr_chicago *chicago) { - chicago->clock = curvecpr_util_nanoseconds(); + chicago->clock = chicago->cf.ops.get_nanoseconds(chicago->cf.priv); } void curvecpr_chicago_on_timeout (struct curvecpr_chicago *chicago) diff --git a/libcurvecpr/lib/messager.c b/libcurvecpr/lib/messager.c index 2c98253..312e23e 100644 --- a/libcurvecpr/lib/messager.c +++ b/libcurvecpr/lib/messager.c @@ -52,11 +52,17 @@ void curvecpr_messager_new (struct curvecpr_messager *messager, const struct cur curvecpr_bytes_zero(messager, sizeof(struct curvecpr_messager)); /* Initialize configuration. */ - if (cf) - curvecpr_bytes_copy(&messager->cf, cf, sizeof(struct curvecpr_messager_cf)); + curvecpr_bytes_copy(&messager->cf, cf, sizeof(struct curvecpr_messager_cf)); /* Initialize congestion handling. */ - curvecpr_chicago_new(&messager->chicago); + struct curvecpr_chicago_ops chicago_ops = { + .get_nanoseconds = cf->ops.get_nanoseconds + }; + struct curvecpr_chicago_cf chicago_cf = { + .ops = chicago_ops, + .priv = cf->priv + }; + curvecpr_chicago_new(&messager->chicago, &chicago_cf); /* If we're in client mode, initiate packets have a maximum size of 512 bytes. Otherwise, we're in server mode, and we can start at 1024. */ diff --git a/libcurvecpr/lib/util.c b/libcurvecpr/lib/util.c index 08fc8cc..0b3ee57 100644 --- a/libcurvecpr/lib/util.c +++ b/libcurvecpr/lib/util.c @@ -34,7 +34,7 @@ long long curvecpr_util_random_mod_n (long long n) /* XXX: Y2036 problems; should upgrade to a 128-bit type for this. */ /* XXX: Nanosecond granularity limits users to 1 terabyte per second. */ -long long curvecpr_util_nanoseconds (void) +long long curvecpr_util_nanoseconds (void *priv) { /* XXX: host_get_clock_service() has been officially deprecated for years; this may need to be updated in the future. */ diff --git a/libcurvecpr/test/messager/test_new_configures_object.c b/libcurvecpr/test/messager/test_new_configures_object.c index 2aa468b..abd4869 100644 --- a/libcurvecpr/test/messager/test_new_configures_object.c +++ b/libcurvecpr/test/messager/test_new_configures_object.c @@ -2,6 +2,7 @@ #include #include +#include static char static_priv[] = "Hello!"; @@ -27,7 +28,8 @@ START_TEST (test_new_configures_object) .ops = { .sendmarkq_is_full = t_sendmarkq_is_full, .sendq_is_empty = t_sendq_is_empty, - .sendmarkq_head = t_sendmarkq_head + .sendmarkq_head = t_sendmarkq_head, + .get_nanoseconds = curvecpr_util_nanoseconds }, .priv = static_priv }; diff --git a/libcurvecpr/test/messager/test_recv_requests_removal_from_sendmarkq.c b/libcurvecpr/test/messager/test_recv_requests_removal_from_sendmarkq.c index 9f004b0..800ce32 100644 --- a/libcurvecpr/test/messager/test_recv_requests_removal_from_sendmarkq.c +++ b/libcurvecpr/test/messager/test_recv_requests_removal_from_sendmarkq.c @@ -4,6 +4,7 @@ #include #include +#include static unsigned char t_sendmarkq_is_full (struct curvecpr_messager *messager) { @@ -36,7 +37,8 @@ START_TEST (test_recv_requests_removal_from_sendmarkq) .sendmarkq_is_full = t_sendmarkq_is_full, .sendq_is_empty = t_sendq_is_empty, .sendmarkq_head = t_sendmarkq_head, - .sendmarkq_remove_range = t_sendmarkq_remove_range + .sendmarkq_remove_range = t_sendmarkq_remove_range, + .get_nanoseconds = curvecpr_util_nanoseconds } }; unsigned char buf[1024] = { 0 }; diff --git a/libcurvecpr/test/messager/test_send_with_1_failure_moves_message_from_sendq.c b/libcurvecpr/test/messager/test_send_with_1_failure_moves_message_from_sendq.c index c0f5a5a..e329758 100644 --- a/libcurvecpr/test/messager/test_send_with_1_failure_moves_message_from_sendq.c +++ b/libcurvecpr/test/messager/test_send_with_1_failure_moves_message_from_sendq.c @@ -2,6 +2,7 @@ #include #include +#include static struct curvecpr_block static_block = { .id = 0, @@ -74,7 +75,8 @@ START_TEST (test_send_with_1_failure_moves_message_from_sendq) .sendmarkq_head = t_sendmarkq_head, .sendq_head = t_sendq_head, .send = t_send, - .sendq_move_to_sendmarkq = t_sendq_move_to_sendmarkq + .sendq_move_to_sendmarkq = t_sendq_move_to_sendmarkq, + .get_nanoseconds = curvecpr_util_nanoseconds }, .priv = &send_counter }; diff --git a/libcurvecpr/test/messager/test_timeout_callback_fires.c b/libcurvecpr/test/messager/test_timeout_callback_fires.c index 16cde7c..1ed0e4f 100644 --- a/libcurvecpr/test/messager/test_timeout_callback_fires.c +++ b/libcurvecpr/test/messager/test_timeout_callback_fires.c @@ -2,6 +2,7 @@ #include #include +#include long long test_timeout = -1LL; @@ -67,7 +68,8 @@ START_TEST (test_timeout_callback_fires) .sendq_head = t_sendq_head, .send = t_send, .sendq_move_to_sendmarkq = t_sendq_move_to_sendmarkq, - .put_next_timeout = t_put_next_timeout + .put_next_timeout = t_put_next_timeout, + .get_nanoseconds = curvecpr_util_nanoseconds } }; diff --git a/libcurvecpr/test/util/test_nanoseconds.c b/libcurvecpr/test/util/test_nanoseconds.c index 28fc414..54e6bc0 100644 --- a/libcurvecpr/test/util/test_nanoseconds.c +++ b/libcurvecpr/test/util/test_nanoseconds.c @@ -5,14 +5,14 @@ START_TEST (test_nanoseconds) { - long long nanoseconds = curvecpr_util_nanoseconds(); + long long nanoseconds = curvecpr_util_nanoseconds(0); fail_if(nanoseconds < 0); fail_unless(nanoseconds > 1374047000000000000LL); /* On OS X, we may cache the kernel clock reference. Make sure it still works. */ - nanoseconds = curvecpr_util_nanoseconds(); + nanoseconds = curvecpr_util_nanoseconds(0); fail_if(nanoseconds < 0); fail_unless(nanoseconds > 1374047000000000000LL); From dee9d29974bc19477d0835bda8898052a0b587d1 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 12:13:07 +0800 Subject: [PATCH 7/9] Complete test for reliable transfer with dropped and delayed packets etc --- libcurvecpr/test/Makefile.am | 3 + libcurvecpr/test/messager/.gitignore | 1 + libcurvecpr/test/messager/test_delivery.c | 516 ++++++++++++++++++++++ 3 files changed, 520 insertions(+) create mode 100644 libcurvecpr/test/messager/test_delivery.c diff --git a/libcurvecpr/test/Makefile.am b/libcurvecpr/test/Makefile.am index b4696c0..802eb96 100644 --- a/libcurvecpr/test/Makefile.am +++ b/libcurvecpr/test/Makefile.am @@ -18,6 +18,9 @@ messager_test_send_with_1_failure_moves_message_from_sendq_SOURCES = messager/te check_PROGRAMS += messager/test_timeout_callback_fires messager_test_timeout_callback_fires_SOURCES = messager/test_timeout_callback_fires.c +check_PROGRAMS += messager/test_delivery +messager_test_delivery_SOURCES = messager/test_delivery.c + check_PROGRAMS += util/test_nanoseconds util_test_nanoseconds_SOURCES = util/test_nanoseconds.c diff --git a/libcurvecpr/test/messager/.gitignore b/libcurvecpr/test/messager/.gitignore index d79bf98..15c6502 100644 --- a/libcurvecpr/test/messager/.gitignore +++ b/libcurvecpr/test/messager/.gitignore @@ -2,3 +2,4 @@ /test_recv_requests_removal_from_sendmarkq /test_send_with_1_failure_moves_message_from_sendq /test_timeout_callback_fires +/test_delivery diff --git a/libcurvecpr/test/messager/test_delivery.c b/libcurvecpr/test/messager/test_delivery.c new file mode 100644 index 0000000..3cf1e50 --- /dev/null +++ b/libcurvecpr/test/messager/test_delivery.c @@ -0,0 +1,516 @@ +#include +#include + +#include +#include + +#include + + +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) + + + +struct block_container { + size_t size; + struct curvecpr_block blocks[500]; +}; + +struct packet { + long long delivery_time; + char delivered; + size_t data_len; + unsigned char data[1088+96]; +}; + +struct packet_container { + size_t size; + struct packet packets[500]; +}; + +struct test_helper { + /* Sorted by offset */ + struct block_container sendq; + + /* These are not sorted. */ + struct block_container sendmarkq; + struct block_container recvmarkq; + + size_t send_offset; + + /* Used to assemble the stream, all received packets are stored in here */ + struct block_container allrecv; + + /* The data coming in through the send() callback are temporarily stored + here before being delivered to the other party. Sorted by the time they + should be delivered. */ + struct packet_container sent; + + /* When a packet is sent it is delayed by this many seconds. Wraps around + when reaching the last entry. Non-positive means dropped packet. */ + double delivery_latencies[10]; + size_t next_index; + size_t num_delivery_latencies; + + /* bool, is it client or server */ + char client; + + /* A pointer to the current time in milliseconds. */ + long long* now; +}; + + +/************ Comparator functions *********/ + +static int order_by_clock (const void *aa, const void *bb) +{ + const struct curvecpr_block *a = aa; + const struct curvecpr_block *b = bb; + if (a->clock < b->clock) + return -1; + else if (a->clock > b->clock) + return 1; + else + return 0; +} + +static int order_by_offset (const void *aa, const void *bb) +{ + const struct curvecpr_block *a = aa; + const struct curvecpr_block *b = bb; + if (a->offset < b->offset) + return -1; + else if (a->offset > b->offset) + return 1; + else + return 0; +} + +static int order_by_delivery_time (const void *aa, const void *bb) +{ + long long a = ((const struct packet *)aa)->delivery_time; + long long b = ((const struct packet *)bb)->delivery_time; + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +/************* Utility functions ***********/ + +static int remove_range (struct block_container *c, unsigned long long start, unsigned long long end) +{ + size_t i; + for (i = 0; i < c->size; i++) + { + struct curvecpr_block *b = &c->blocks[i]; + if (start <= b->offset && b->offset < end) + { + unsigned long long offset = b->offset; + size_t data_len = b->data_len; + + fail_if(end < start + data_len); + + *b = c->blocks[--c->size]; + + if (start < offset) { + remove_range(c, start, offset); + } + if (offset+data_len < end) { + remove_range(c, offset+data_len, end); + } + return 0; + } + } + return 1; +} + +static void add_to_send_queue (struct curvecpr_messager *messager, const unsigned char *buf, size_t num) +{ + size_t data_len; + size_t i; + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + + for (i = 0; i < num; i += messager->my_maximum_send_bytes) + { + fail_if(helper->sendq.size >= ARRAY_LENGTH(helper->sendq.blocks)); + + struct curvecpr_block *b = &helper->sendq.blocks[helper->sendq.size++]; + data_len = num-i <= messager->my_maximum_send_bytes ? num-i : messager->my_maximum_send_bytes; + helper->send_offset += data_len; + + b->offset = helper->send_offset; + b->data_len = data_len; + curvecpr_bytes_copy(b->data, buf+i, data_len); + } +} + +static size_t get_received_size (struct test_helper *helper) +{ + size_t i, size = 0; + crypto_uint64 offset = 0; + qsort(helper->allrecv.blocks, helper->allrecv.size, sizeof(struct curvecpr_block), &order_by_offset); + for (i = 0; i < helper->allrecv.size; i++) + { + struct curvecpr_block *b = &helper->allrecv.blocks[i]; + fail_if(offset > b->offset); + if (offset == b->offset) { + size += b->data_len; + offset += b->data_len; + } + else + break; + } + return size; +} + +/* buf must be able to hold size get_received_size() */ +static void get_received_data (struct test_helper *helper, unsigned char *buf) +{ + size_t i; + crypto_uint64 offset = 0; + qsort(helper->allrecv.blocks, helper->allrecv.size, sizeof(struct curvecpr_block), &order_by_offset); + for (i = 0; i < helper->allrecv.size; i++) + { + struct curvecpr_block *b = &helper->allrecv.blocks[i]; + fail_if(offset > b->offset); + if (offset == b->offset) + { + curvecpr_bytes_copy(buf + offset, b->data, b->data_len); + offset += b->data_len; + } + else + break; + } +} + +/*************** Init functions ************/ + +static void test_helper_new (struct test_helper *helper, char client, long long *time_variable) +{ + curvecpr_bytes_zero(helper, sizeof(struct test_helper)); + helper->client = client; + helper->now = time_variable; +} + + + + + +/*************** Callbacks *****************/ + +static int sendq_head (struct curvecpr_messager *messager, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + if (helper->sendq.size == 0) + return 1; + else + { + fail_if(block_stored == NULL); + *block_stored = &helper->sendq.blocks[0]; + return 0; + } +} + +static int sendq_move_to_sendmarkq (struct curvecpr_messager *messager, const struct curvecpr_block *block, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + size_t index; + size_t num_after; + + if (block < &helper->sendq.blocks[0] || block >= &helper->sendq.blocks[helper->sendq.size]) + return 1; + + fail_if(helper->sendmarkq.size >= ARRAY_LENGTH(helper->sendmarkq.blocks)); + + index = block - &helper->sendq.blocks[0]; + + memcpy(&helper->sendmarkq.blocks[helper->sendmarkq.size++], block, sizeof(struct curvecpr_block)); + + num_after = helper->sendq.size - index - 1; + + if (num_after > 0) + memcpy(&helper->sendq.blocks[index], &helper->sendq.blocks[index+1], sizeof(struct curvecpr_block)*num_after); + + helper->sendq.size--; + + return 0; +} + +static unsigned char sendq_is_empty (struct curvecpr_messager *messager) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + return helper->sendq.size == 0 ? 1 : 0; +} + +static int sendmarkq_head (struct curvecpr_messager *messager, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + if (helper->sendmarkq.size == 0) + return 1; + else { + qsort(helper->sendmarkq.blocks, helper->sendmarkq.size, sizeof(struct curvecpr_block), &order_by_clock); + fail_if(block_stored == NULL); + *block_stored = &helper->sendmarkq.blocks[0]; + return 0; + } +} + +static int sendmarkq_get (struct curvecpr_messager *messager, crypto_uint32 acknowledging_id, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + size_t i; + for (i = 0; i < helper->sendmarkq.size; i++) + { + struct curvecpr_block *b = &helper->sendmarkq.blocks[i]; + if (b->id == acknowledging_id) + { + fail_if(block_stored == NULL); + *block_stored = b; + return 0; + } + } + return 1; +} + +static int sendmarkq_remove_range (struct curvecpr_messager *messager, unsigned long long start, unsigned long long end) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + return remove_range(&helper->sendmarkq, start, end); +} + +static unsigned char sendmarkq_is_full (struct curvecpr_messager *messager) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + return helper->sendmarkq.size == ARRAY_LENGTH(helper->sendmarkq.blocks); +} + +static int recvmarkq_put (struct curvecpr_messager *messager, const struct curvecpr_block *block, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + if (helper->recvmarkq.size == ARRAY_LENGTH(helper->recvmarkq.blocks)) + return 1; + else + { + size_t i; + fail_if(helper->recvmarkq.size >= ARRAY_LENGTH(helper->recvmarkq.blocks)); + int already_received = 0; + helper->recvmarkq.blocks[helper->recvmarkq.size] = *block; + fail_if(block_stored == NULL); + *block_stored = &helper->recvmarkq.blocks[helper->recvmarkq.size++]; + + for (i = 0; i < helper->allrecv.size; i++) + { + if (helper->allrecv.blocks[i].offset == block->offset) + { + already_received = 1; + break; + } + } + if (!already_received) + { + fail_if(helper->allrecv.size >= ARRAY_LENGTH(helper->allrecv.blocks)); + helper->allrecv.blocks[helper->allrecv.size++] = *block; + } + + return 0; + } +} + +static int recvmarkq_get_nth_unacknowledged (struct curvecpr_messager *messager, unsigned int n, struct curvecpr_block **block_stored) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + if (n >= helper->recvmarkq.size) + return 1; + else + { + qsort(helper->recvmarkq.blocks, helper->recvmarkq.size, sizeof(struct curvecpr_block), &order_by_offset); + fail_if(block_stored == NULL); + *block_stored = &helper->recvmarkq.blocks[n]; + return 0; + } +} + +static unsigned char recvmarkq_is_empty (struct curvecpr_messager *messager) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + if (helper->recvmarkq.size == 0) + return 1; + else + return 0; +} + +static int recvmarkq_remove_range (struct curvecpr_messager *messager, unsigned long long start, unsigned long long end) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + return remove_range(&helper->recvmarkq, start, end); +} + +static int send (struct curvecpr_messager *messager, const unsigned char *buf, size_t num) +{ + struct test_helper *helper = (struct test_helper*)messager->cf.priv; + struct packet* p = &helper->sent.packets[helper->sent.size]; + double latency = helper->delivery_latencies[helper->next_index++]; + helper->next_index %= helper->num_delivery_latencies; + fail_if(helper->sent.size >= ARRAY_LENGTH(helper->sent.packets)); + + if (latency >= 0) + { + p->delivery_time = *helper->now + (long long)(latency * 1000*1000*1000); + p->data_len = num; + curvecpr_bytes_copy(p->data, buf, num); + helper->sent.size++; + + qsort(helper->sent.packets, helper->sent.size, sizeof(struct packet), order_by_delivery_time); + } + + return 0; +} + +static void put_next_timeout(struct curvecpr_messager *messager, const long long timeout_ns) +{ + // Not used +} + +static long long get_nanoseconds(void *priv) +{ + struct test_helper *helper = (struct test_helper*)priv; + return *helper->now; +} + + +/*************** test *****************/ + + +struct curvecpr_messager_ops ops = { + .sendq_head = &sendq_head, + .sendq_move_to_sendmarkq = &sendq_move_to_sendmarkq, + .sendq_is_empty = &sendq_is_empty, + .sendmarkq_head = &sendmarkq_head, + .sendmarkq_get = &sendmarkq_get, + .sendmarkq_remove_range = &sendmarkq_remove_range, + .sendmarkq_is_full = &sendmarkq_is_full, + .recvmarkq_put = &recvmarkq_put, + .recvmarkq_get_nth_unacknowledged = &recvmarkq_get_nth_unacknowledged, + .recvmarkq_is_empty = &recvmarkq_is_empty, + .recvmarkq_remove_range = &recvmarkq_remove_range, + .send = &send, + .put_next_timeout = &put_next_timeout, + .get_nanoseconds = &get_nanoseconds + }; + + + +static int are_all_queues_empty (const struct test_helper *helper) +{ + if (helper->sendq.size > 0 || helper->sendmarkq.size > 0 || helper->recvmarkq.size > 0 || helper->sent.size > 0) + return 0; + else + return 1; +} + +static long long min (long long a, long long b, long long c, long long d) +{ + long long ab = a < b ? a : b; + long long cd = c < d ? c : d; + return ab < cd ? ab : cd; +} + +static void deliver_packet (struct test_helper *from, struct curvecpr_messager *recipient) +{ + struct packet *p; + fail_if(from->sent.size == 0); + from->sent.size--; + p = &from->sent.packets[0]; + curvecpr_messager_recv(recipient, p->data, p->data_len); + if (from->sent.size > 0) + curvecpr_bytes_copy(&from->sent.packets[0], &from->sent.packets[0], from->sent.size * sizeof(struct packet)); +} + + +#define DECLARE(name) \ + struct test_helper *name ## _helper = malloc(sizeof(struct test_helper)); \ + struct curvecpr_messager_cf name ## _cf = { \ + .ops = ops, \ + .priv = name ## _helper \ + }; \ + struct curvecpr_messager name \ + +#define DELIVERY_LATENCY(name, values) \ + long long name ## _latencies[] = values; + +#define INITIALIZE(name, client, latencies) \ + test_helper_new(name ## _helper, client, &now); \ + fail_if(ARRAY_LENGTH(latencies) > ARRAY_LENGTH(name ## _helper->delivery_latencies)); \ + curvecpr_bytes_copy(name ## _helper->delivery_latencies, latencies, sizeof(latencies)); \ + name ## _helper->num_delivery_latencies = ARRAY_LENGTH(latencies); \ + curvecpr_messager_new(& name, & name ## _cf, client) \ + +#define INFINITY 999999999999999999LL + + +START_TEST (test_delivery) +{ + DECLARE(server); + DECLARE(client); + + long long now = 1000*1000*1000*1000LL; + + /* Non-positive means drop packet */ + double server_delivery_latencies[] = {0.1, 0.2, 0.4, 0.01, -1}; + double client_delivery_latencies[] = {0.5, 0.55, 0.1, -1, 0.1, 0.1, 0.2, 5}; + + const int SEND_SIZE = 75*1000; + unsigned char *send_data = malloc(SEND_SIZE); + unsigned char *recv_data = malloc(SEND_SIZE); + size_t i; + + INITIALIZE(server, 0, server_delivery_latencies); + INITIALIZE(client, 1, client_delivery_latencies); + + for (i = 0; i < SEND_SIZE; i++) + /* send_data = "aBcDeFgH..." */ + send_data[i] = 'a' + (char)(i%26) + (char)(('A'-'a') * (i%2)); + + curvecpr_bytes_zero(recv_data, SEND_SIZE); + + add_to_send_queue(&client, send_data, sizeof(send_data)); + + while (!(are_all_queues_empty(server_helper) && are_all_queues_empty(client_helper))) + { + long long server_timeout = now + curvecpr_messager_next_timeout(&server); + long long client_timeout = now + curvecpr_messager_next_timeout(&client); + long long server_delivery_timeout = server_helper->sent.size > 0 ? + server_helper->sent.packets[0].delivery_time : INFINITY; + long long client_delivery_timeout = client_helper->sent.size > 0 ? + client_helper->sent.packets[0].delivery_time : INFINITY; + + long long timeout = min(server_timeout, client_timeout, server_delivery_timeout, client_delivery_timeout); + fail_if(timeout < now); + now = timeout; + fail_if(now == INFINITY); + + if (now == server_timeout) + curvecpr_messager_process_sendq(&server); + else if (now == client_timeout) + curvecpr_messager_process_sendq(&client); + else if (now == server_delivery_timeout) + deliver_packet(server_helper, &client); + else if (now == client_delivery_timeout) + deliver_packet(client_helper, &server); + } + + fail_unless(get_received_size(server_helper) == sizeof(recv_data)); + get_received_data(server_helper, recv_data); + fail_unless(curvecpr_bytes_equal(send_data, recv_data, sizeof(send_data))); + + free(server_helper); + free(client_helper); + free(send_data); + free(recv_data); +} +END_TEST + +RUN_TEST(test_delivery) From c619948991a5df204be5536fcbeb6486927d8a8d Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 13:45:16 +0800 Subject: [PATCH 8/9] Fix from kostko's fork, commit 32a224ff639ea71b44ceb3750d0e3fe85b216f83 --- libcurvecpr/lib/messager.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libcurvecpr/lib/messager.c b/libcurvecpr/lib/messager.c index 312e23e..6b8b9c4 100644 --- a/libcurvecpr/lib/messager.c +++ b/libcurvecpr/lib/messager.c @@ -523,6 +523,11 @@ long long curvecpr_messager_next_timeout (struct curvecpr_messager *messager) if (at > block->clock + chicago->rtt_timeout) at = block->clock + chicago->rtt_timeout; + + /* Writing faster than wr_rate does not make sense and will cause spinning. BUT, if + there is something to acknowledge, block might still be resent. */ + if (cf->ops.recvmarkq_is_empty(messager) && at < messager->my_sent_clock + chicago->wr_rate) + at = messager->my_sent_clock + chicago->wr_rate; } if (chicago->clock > at) From 0920edbc64660e19067d37a8de4edd8f23e02198 Mon Sep 17 00:00:00 2001 From: Lars Magnusson Date: Fri, 2 May 2014 13:47:34 +0800 Subject: [PATCH 9/9] Bugfix in test_delivery --- libcurvecpr/test/messager/test_delivery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcurvecpr/test/messager/test_delivery.c b/libcurvecpr/test/messager/test_delivery.c index 3cf1e50..ce1e08c 100644 --- a/libcurvecpr/test/messager/test_delivery.c +++ b/libcurvecpr/test/messager/test_delivery.c @@ -471,12 +471,12 @@ START_TEST (test_delivery) INITIALIZE(client, 1, client_delivery_latencies); for (i = 0; i < SEND_SIZE; i++) - /* send_data = "aBcDeFgH..." */ + /* send_data = "aBcDeFgHiJ..." */ send_data[i] = 'a' + (char)(i%26) + (char)(('A'-'a') * (i%2)); curvecpr_bytes_zero(recv_data, SEND_SIZE); - add_to_send_queue(&client, send_data, sizeof(send_data)); + add_to_send_queue(&client, send_data, SEND_SIZE); while (!(are_all_queues_empty(server_helper) && are_all_queues_empty(client_helper))) { @@ -502,9 +502,9 @@ START_TEST (test_delivery) deliver_packet(client_helper, &server); } - fail_unless(get_received_size(server_helper) == sizeof(recv_data)); + fail_unless(get_received_size(server_helper) == SEND_SIZE); get_received_data(server_helper, recv_data); - fail_unless(curvecpr_bytes_equal(send_data, recv_data, sizeof(send_data))); + fail_unless(curvecpr_bytes_equal(send_data, recv_data, SEND_SIZE)); free(server_helper); free(client_helper);