aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@noconroy.net>2020-02-26 03:14:33 +1100
committerMatt Dunwoodie <ncon@noconroy.net>2020-02-26 03:14:33 +1100
commit679aaf4d1f0559febf9c992c26adbb625b4e1874 (patch)
tree7ea054b6969c934fb3e1fbf8e3b0e65e60df650c
parentHave unique errors for cookie_param_consume_payload (diff)
downloadwireguard-openbsd-679aaf4d1f0559febf9c992c26adbb625b4e1874.tar.xz
wireguard-openbsd-679aaf4d1f0559febf9c992c26adbb625b4e1874.zip
More tests
-rw-r--r--src/if_wg.c19
-rw-r--r--src/wgtest.c272
2 files changed, 280 insertions, 11 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index ce9a567..27459d3 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -2388,13 +2388,18 @@ wg_clone_destroy(struct ifnet *ifp)
void
wgattach(int nwg)
{
- if_clone_attach(&wg_cloner);
- cookieinit();
- pool_init(&wg_aip_pool, sizeof(struct wg_aip), 0, IPL_NET, 0, "wgaip", NULL);
- pool_init(&wg_peer_pool, sizeof(struct wg_peer), 0, IPL_NET, 0, "wgpeer", NULL);
- pool_init(&wg_keypair_pool, sizeof(struct wg_keypair), 0, IPL_NET, 0, "wgkeypair", NULL);
-
#ifdef WGTEST
- wgtest();
+ if (wgtest() == 1) {
+#endif
+ if_clone_attach(&wg_cloner);
+ cookieinit();
+ pool_init(&wg_aip_pool, sizeof(struct wg_aip), 0,
+ IPL_NET, 0, "wgaip", NULL);
+ pool_init(&wg_peer_pool, sizeof(struct wg_peer), 0,
+ IPL_NET, 0, "wgpeer", NULL);
+ pool_init(&wg_keypair_pool, sizeof(struct wg_keypair), 0,
+ IPL_NET, 0, "wgkeypair", NULL);
+#ifdef WGTEST
+ }
#endif
}
diff --git a/src/wgtest.c b/src/wgtest.c
index 2ac6618..5b8a127 100644
--- a/src/wgtest.c
+++ b/src/wgtest.c
@@ -24,8 +24,11 @@
#include <sys/mutex.h>
#include <crypto/noise.h>
+#include <crypto/cookie.h>
-/* proto */
+#define MESSAGE_SIZE 64
+
+/* proto (not exported from noise.c) */
int noise_keypair_counter_recv(struct noise_keypair *, uint64_t);
/* tests */
@@ -123,16 +126,277 @@ noise_counter_test()
#undef T_LIM
#undef T_INIT
- if (passed)
- printf("noise counter: pass\n");
+ printf("noise counter: %s\n", passed ? "passed" : "failed");
return passed;
}
void
+noise_handshake_init(struct noise_local *al, struct noise_local *bl,
+ struct noise_remote *ar, struct noise_remote *br)
+{
+ uint8_t apriv[NOISE_KEY_SIZE], bpriv[NOISE_KEY_SIZE];
+ uint8_t apub[NOISE_KEY_SIZE], bpub[NOISE_KEY_SIZE];
+ uint8_t psk[NOISE_PSK_SIZE];
+
+ noise_local_init(al, 0);
+ noise_local_init(bl, 0);
+
+ arc4random_buf(apriv, NOISE_KEY_SIZE);
+ arc4random_buf(bpriv, NOISE_KEY_SIZE);
+
+ noise_local_set_private(al, apriv);
+ noise_local_set_private(bl, bpriv);
+
+ noise_local_get_public(al, apub);
+ noise_local_get_public(bl, bpub);
+
+ noise_remote_init(ar, apub, 0);
+ noise_remote_init(br, bpub, 0);
+
+ arc4random_buf(psk, NOISE_PSK_SIZE);
+ noise_remote_set_psk(ar, psk);
+ noise_remote_set_psk(br, psk);
+}
+
+/* They live here, because stack frame too big */
+static struct noise_local al, bl; /* alice_local, bob_local */
+static struct noise_remote ar, br; /* alice_remote, bob_remote */
+static struct noise_handshake hs;
+static struct noise_keypair akp, bkp;
+
+int
+noise_handshake_test()
+{
+ uint64_t nonce;
+ int passed = 1;
+ size_t i;
+ uint8_t ue[NOISE_KEY_SIZE];
+ uint8_t es[NOISE_KEY_SIZE + NOISE_MAC_SIZE];
+ uint8_t ets[NOISE_TIMESTAMP_SIZE + NOISE_MAC_SIZE];
+ uint8_t en[0 + NOISE_MAC_SIZE];
+ uint8_t message[MESSAGE_SIZE + NOISE_MAC_SIZE];
+
+#define T_FAILED(n) do { \
+ passed = 0; \
+ printf("handshake test " n ": failed\n"); \
+} while (0)
+
+ noise_handshake_init(&al, &bl, &ar, &br);
+
+ /* Create initiation */
+ if (noise_create_initiation(&al, &br, ue, es, ets) != 0)
+ T_FAILED("create_initiation");
+
+ /* Check encrypted (es) validation */
+ for (i = 0; i < sizeof(es); i++) {
+ es[i] = ~es[i];
+ if (noise_consume_initiation(&bl, &hs, ue, es, ets) == 0)
+ T_FAILED("consume_initiation_es");
+ es[i] = ~es[i];
+ }
+
+ /* Check encrypted (ets) validation */
+ for (i = 0; i < sizeof(ets); i++) {
+ ets[i] = ~ets[i];
+ if (noise_consume_initiation(&bl, &hs, ue, es, ets) == 0)
+ T_FAILED("consume_initiation_ets");
+ ets[i] = ~ets[i];
+ }
+
+ /* Consume initiation properly */
+ if (noise_consume_initiation(&bl, &hs, ue, es, ets) != 0)
+ T_FAILED("consume_initiation");
+ if (noise_remote_set_handshake(&ar, &hs) != 0)
+ T_FAILED("set_handshake");
+
+ /* Replay initiation */
+ if (noise_consume_initiation(&bl, &hs, ue, es, ets) != 0)
+ T_FAILED("consume_initiation_replay");
+ if (noise_remote_set_handshake(&ar, &hs) == 0)
+ T_FAILED("set_handshake_replay");
+
+ /* Create response */
+ if (noise_create_response(&bl, &ar, es, en) != 0)
+ T_FAILED("create_response");
+
+ /* Check encrypted (en) validation */
+ for (i = 0; i < sizeof(en); i++) {
+ en[i] = ~en[i];
+ if (noise_consume_response(&al, &br, es, en) == 0)
+ T_FAILED("consume_response_en");
+ en[i] = ~en[i];
+ }
+
+ /* Consume response properly */
+ if (noise_consume_response(&al, &br, es, en) != 0)
+ T_FAILED("consume_response");
+
+ /* Derive keys on both sides */
+ if (noise_keypair_from_handshake(&akp, &br) != 0)
+ T_FAILED("keypair_akp");
+ if (noise_keypair_from_handshake(&bkp, &ar) != 0)
+ T_FAILED("keypair_bkp");
+
+ for (i = 0; i < MESSAGE_SIZE; i++)
+ message[i] = i;
+
+ /* Since bob is responder, he must not encrypt until confirmed */
+ if (noise_keypair_encrypt(&bkp, message, MESSAGE_SIZE, &nonce) == 0)
+ T_FAILED("encrypt_kci_wait");
+
+ /* Alice now encrypt and gets bob to decrypt */
+ if (noise_keypair_encrypt(&akp, message, MESSAGE_SIZE, &nonce) != 0)
+ T_FAILED("encrypt_akp");
+ if (noise_keypair_decrypt(&bkp, message, MESSAGE_SIZE + NOISE_MAC_SIZE, nonce) != 0)
+ T_FAILED("decrypt_bkp");
+
+ for (i = 0; i < MESSAGE_SIZE; i++)
+ if (message[i] != i)
+ T_FAILED("decrypt_message_akp_bkp");
+
+ /* Since bob is responder, he must not encrypt until confirmed */
+ if (noise_keypair_encrypt(&bkp, message, MESSAGE_SIZE, &nonce) != 0)
+ T_FAILED("encrypt_kci_ready");
+ if (noise_keypair_decrypt(&akp, message, MESSAGE_SIZE + NOISE_MAC_SIZE, nonce) != 0)
+ T_FAILED("decrypt_akp");
+
+ for (i = 0; i < MESSAGE_SIZE; i++)
+ if (message[i] != i)
+ T_FAILED("decrypt_message_bkp_akp");
+#undef T_FAILED
+
+ printf("handshake test: %s\n", passed ? "passed" : "failed");
+ return passed;
+}
+
+int
+cookie_ratelimit_test()
+{
+ printf("ratelimit test: TODO\n");
+ return 1;
+}
+
+int
+cookie_mac_test()
+{
+ size_t i;
+ int passed = 1;
+ struct cookie_checker cc;
+ struct cookie_param cp;
+ struct cookie_macs cm;
+ struct sockaddr_in sin;
+
+ uint8_t xnonce[COOKIE_XNONCE_SIZE];
+ uint8_t cookie[COOKIE_ENCRYPTED_SIZE];
+ uint8_t shared[COOKIE_INPUT_SIZE];
+ uint8_t message[MESSAGE_SIZE];
+
+#define T_FAILED(n) do { \
+ passed = 0; \
+ printf("cookie mac test " n ": failed\n"); \
+} while (0)
+
+ arc4random_buf(shared, COOKIE_INPUT_SIZE);
+ arc4random_buf(message, MESSAGE_SIZE);
+
+ /* Init both sides */
+ cookie_param_init(&cp, shared);
+ if (cookie_checker_init(&cc, 0) != 0)
+ T_FAILED("cookie_checker_allocate");
+ cookie_checker_update(&cc, shared);
+
+ /* Create dummy sockaddr */
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = 1;
+ sin.sin_port = 51820;
+
+ /* MAC message */
+ cookie_param_mac(&cp, &cm, message, MESSAGE_SIZE);
+
+ /* Check we have a null mac2 (currently fails as timing is too early in
+ * the kernel.
+ for (i = 0; i < sizeof(cm.mac2); i++)
+ if (cm.mac2[i] != 0)
+ T_FAILED("validate_macs_noload_mac2_zeroed");
+ */
+
+ /* Validate all bytes are checked in mac1 */
+ for (i = 0; i < sizeof(cm.mac1); i++) {
+ cm.mac1[i] = ~cm.mac1[i];
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 0, sintosa(&sin)) != EINVAL)
+ T_FAILED("validate_macs_noload_munge");
+ cm.mac1[i] = ~cm.mac1[i];
+ }
+
+ /* Check we can successfully validate the MAC */
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 0, sintosa(&sin)) != 0)
+ T_FAILED("validate_macs_noload_normal");
+
+ /* Check we get a EAGAIN if no mac2 and under load */
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 1, sintosa(&sin)) != EAGAIN)
+ T_FAILED("validate_macs_load_normal");
+
+ /* Simulate a cookie message */
+ cookie_checker_create_payload(&cc, &cm, xnonce, cookie, sintosa(&sin));
+
+ /* Validate all bytes are checked in cookie */
+ for (i = 0; i < sizeof(cookie); i++) {
+ cookie[i] = ~cookie[i];
+ if (cookie_param_consume_payload(&cp, xnonce, cookie) != EINVAL)
+ T_FAILED("consume_payload_munge");
+ cookie[i] = ~cookie[i];
+ }
+
+ /* Check we can actually consume the payload */
+ if (cookie_param_consume_payload(&cp, xnonce, cookie) != 0)
+ T_FAILED("consume_payload_normal");
+
+ /* Check replay isn't allowed */
+ if (cookie_param_consume_payload(&cp, xnonce, cookie) != ETIMEDOUT)
+ T_FAILED("consume_payload_normal_replay");
+
+ /* MAC message again, with MAC2 */
+ cookie_param_mac(&cp, &cm, message, MESSAGE_SIZE);
+
+ /* Check we get OK if mac2 and under load */
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 1, sintosa(&sin)) != 0)
+ T_FAILED("validate_macs_load_normal_mac2");
+
+ sin.sin_addr.s_addr = ~sin.sin_addr.s_addr;
+ /* Check we get EAGAIN if we munge the source IP */
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 1, sintosa(&sin)) != EAGAIN)
+ T_FAILED("validate_macs_load_spoofip_mac2");
+ sin.sin_addr.s_addr = ~sin.sin_addr.s_addr;
+
+ /* Check we get OK if mac2 and under load */
+ if (cookie_checker_validate_macs(&cc, &cm, message, MESSAGE_SIZE, 1, sintosa(&sin)) != 0)
+ T_FAILED("validate_macs_load_normal_mac2_retry");
+
+ cookie_checker_deinit(&cc);
+
+#undef T_FAILED
+ printf("handshake test: %s\n", passed ? "passed" : "failed");
+ return passed;
+}
+
+extern struct pool ratelimit_pool;
+
+int
wgtest()
{
int passed = 1;
printf("WireGuard tests: starting\n");
passed &= noise_counter_test();
- printf("WireGuard tests: %s!\n", passed ? "passed" : "failed");
+ passed &= noise_handshake_test();
+
+ /* We need to jerryrig the ratelimit pool here to successfully test */
+ pool_init(&ratelimit_pool, sizeof(struct ratelimit), 0,
+ IPL_NET, 0, "ratelimit", NULL);
+ passed &= cookie_ratelimit_test();
+ passed &= cookie_mac_test();
+ pool_destroy(&ratelimit_pool);
+
+ printf("WireGuard tests: %s!\n", passed ? "passed" : "failed, disabling");
+ return passed;
}