diff options
author | Nick Mathewson <nickm@torproject.org> | 2013-01-03 11:52:41 -0500 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2013-01-03 11:52:41 -0500 |
commit | b1bdecd703879ca09bf63bf1453a70c4b80ac96d (patch) | |
tree | 9fd512361cae60d0aec849e52e349cc1a67f8055 /src/test | |
parent | ee4182612f7f06ae09531bf75e9b84ea30871278 (diff) | |
parent | d3de0b91fb322c00d11857d89a8420af0d466e39 (diff) | |
download | tor-b1bdecd703879ca09bf63bf1453a70c4b80ac96d.tar.gz tor-b1bdecd703879ca09bf63bf1453a70c4b80ac96d.zip |
Merge branch 'ntor-resquashed'
Conflicts:
src/or/cpuworker.c
src/or/or.h
src/test/bench.c
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/bench.c | 143 | ||||
-rw-r--r-- | src/test/include.am | 19 | ||||
-rw-r--r-- | src/test/ntor_ref.py | 387 | ||||
-rw-r--r-- | src/test/test.c | 77 | ||||
-rw-r--r-- | src/test/test_cell_formats.c | 502 | ||||
-rw-r--r-- | src/test/test_containers.c | 45 | ||||
-rw-r--r-- | src/test/test_crypto.c | 181 | ||||
-rw-r--r-- | src/test/test_ntor_cl.c | 166 | ||||
-rw-r--r-- | src/test/test_util.c | 10 |
9 files changed, 1519 insertions, 11 deletions
diff --git a/src/test/bench.c b/src/test/bench.c index da1ae9bc5d..8b91b07a47 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -15,17 +15,23 @@ const char tor_git_revision[] = ""; #include "orconfig.h" #define RELAY_PRIVATE +#define CONFIG_PRIVATE #include "or.h" +#include "onion_tap.h" #include "relay.h" #include <openssl/opensslv.h> #include <openssl/evp.h> -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) #ifndef OPENSSL_NO_EC #include <openssl/ec.h> #include <openssl/ecdh.h> #include <openssl/obj_mac.h> #endif + +#include "config.h" +#ifdef CURVE25519_ENABLED +#include "crypto_curve25519.h" +#include "onion_ntor.h" #endif #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) @@ -106,6 +112,125 @@ bench_aes(void) } static void +bench_onion_TAP(void) +{ + const int iters = 1<<9; + int i; + crypto_pk_t *key, *key2; + uint64_t start, end; + char os[TAP_ONIONSKIN_CHALLENGE_LEN]; + char or[TAP_ONIONSKIN_REPLY_LEN]; + crypto_dh_t *dh_out; + + key = crypto_pk_new(); + key2 = crypto_pk_new(); + crypto_pk_generate_key_with_bits(key, 1024); + crypto_pk_generate_key_with_bits(key2, 1024); + + reset_perftime(); + start = perftime(); + for (i = 0; i < iters; ++i) { + onion_skin_TAP_create(key, &dh_out, os); + crypto_dh_free(dh_out); + } + end = perftime(); + printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); + + onion_skin_TAP_create(key, &dh_out, os); + start = perftime(); + for (i = 0; i < iters; ++i) { + char key_out[CPATH_KEY_MATERIAL_LEN]; + onion_skin_TAP_server_handshake(os, key, NULL, or, + key_out, sizeof(key_out)); + } + end = perftime(); + printf("Server-side, key guessed right: %f usec\n", + NANOCOUNT(start, end, iters)/1e3); + + start = perftime(); + for (i = 0; i < iters; ++i) { + char key_out[CPATH_KEY_MATERIAL_LEN]; + onion_skin_TAP_server_handshake(os, key2, key, or, + key_out, sizeof(key_out)); + } + end = perftime(); + printf("Server-side, key guessed wrong: %f usec.\n", + NANOCOUNT(start, end, iters)/1e3); + + start = perftime(); + for (i = 0; i < iters; ++i) { + crypto_dh_t *dh; + char key_out[CPATH_KEY_MATERIAL_LEN]; + int s; + dh = crypto_dh_dup(dh_out); + s = onion_skin_TAP_client_handshake(dh, or, key_out, sizeof(key_out)); + tor_assert(s == 0); + } + end = perftime(); + printf("Client-side, part 2: %f usec.\n", + NANOCOUNT(start, end, iters)/1e3); + + crypto_pk_free(key); +} + +#ifdef CURVE25519_ENABLED +static void +bench_onion_ntor(void) +{ + const int iters = 1<<10; + int i; + curve25519_keypair_t keypair1, keypair2; + uint64_t start, end; + uint8_t os[NTOR_ONIONSKIN_LEN]; + uint8_t or[NTOR_REPLY_LEN]; + ntor_handshake_state_t *state = NULL; + uint8_t nodeid[DIGEST_LEN]; + di_digest256_map_t *keymap = NULL; + + curve25519_secret_key_generate(&keypair1.seckey, 0); + curve25519_public_key_generate(&keypair1.pubkey, &keypair1.seckey); + curve25519_secret_key_generate(&keypair2.seckey, 0); + curve25519_public_key_generate(&keypair2.pubkey, &keypair2.seckey); + dimap_add_entry(&keymap, keypair1.pubkey.public_key, &keypair1); + dimap_add_entry(&keymap, keypair2.pubkey.public_key, &keypair2); + + reset_perftime(); + start = perftime(); + for (i = 0; i < iters; ++i) { + onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); + ntor_handshake_state_free(state); + } + end = perftime(); + printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); + + onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); + start = perftime(); + for (i = 0; i < iters; ++i) { + uint8_t key_out[CPATH_KEY_MATERIAL_LEN]; + onion_skin_ntor_server_handshake(os, keymap, NULL, nodeid, or, + key_out, sizeof(key_out)); + } + end = perftime(); + printf("Server-side: %f usec\n", + NANOCOUNT(start, end, iters)/1e3); + + start = perftime(); + for (i = 0; i < iters; ++i) { + uint8_t key_out[CPATH_KEY_MATERIAL_LEN]; + int s; + s = onion_skin_ntor_client_handshake(state, or, key_out, sizeof(key_out)); + tor_assert(s == 0); + } + end = perftime(); + printf("Client-side, part 2: %f usec.\n", + NANOCOUNT(start, end, iters)/1e3); + + ntor_handshake_state_free(state); + dimap_free(keymap, NULL); +} +#endif + +static void bench_cell_aes(void) { uint64_t start, end; @@ -355,6 +480,10 @@ typedef struct benchmark_t { static struct benchmark_t benchmarks[] = { ENT(dmap), ENT(aes), + ENT(onion_TAP), +#ifdef CURVE25519_ENABLED + ENT(onion_ntor), +#endif ENT(cell_aes), ENT(cell_ops), ENT(dh), @@ -385,6 +514,8 @@ main(int argc, const char **argv) int i; int list=0, n_enabled=0; benchmark_t *b; + char *errmsg; + or_options_t *options; tor_threads_init(); @@ -405,6 +536,16 @@ main(int argc, const char **argv) reset_perftime(); crypto_seed_rng(1); + options = options_new(); + init_logging(); + options->command = CMD_RUN_UNITTESTS; + options->DataDirectory = tor_strdup(""); + options_init(options); + if (set_options(options, &errmsg) < 0) { + printf("Failed to set initial options: %s\n", errmsg); + tor_free(errmsg); + return 1; + } for (b = benchmarks; b->name; ++b) { if (b->enabled || n_enabled == 0) { diff --git a/src/test/include.am b/src/test/include.am index 075df36460..f625ab7f7d 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -36,14 +36,16 @@ src_test_bench_CPPFLAGS= $(src_test_AM_CPPFLAGS) src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ -src_test_test_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \ +src_test_test_LDADD = src/or/libtor.a src/common/libor.a \ + src/common/libor-crypto.a $(LIBDONNA) \ src/common/libor-event.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ -src_test_bench_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \ +src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ + src/common/libor-crypto.a $(LIBDONNA) \ src/common/libor-event.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @@ -51,3 +53,16 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypt noinst_HEADERS+= \ src/test/test.h +if CURVE25519_ENABLED +noinst_PROGRAMS+= src/test/test-ntor-cl +src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c +src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ + src/common/libor-crypto.a $(LIBDONNA) \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ +src_test_test_ntor_cl_AM_CPPFLAGS = \ + -I"$(top_srcdir)/src/or" + +endif + diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py new file mode 100644 index 0000000000..6133be1395 --- /dev/null +++ b/src/test/ntor_ref.py @@ -0,0 +1,387 @@ +# Copyright 2012 The Tor Project, Inc +# See LICENSE for licensing information + +""" +ntor_ref.py + + +This module is a reference implementation for the "ntor" protocol +s proposed by Goldberg, Stebila, and Ustaoglu and as instantiated in +Tor Proposal 216. + +It's meant to be used to validate Tor's ntor implementation. It +requirs the curve25519 python module from the curve25519-donna +package. + + *** DO NOT USE THIS IN PRODUCTION. *** + +commands: + + gen_kdf_vectors: Print out some test vectors for the RFC5869 KDF. + timing: Print a little timing information about this implementation's + handshake. + self-test: Try handshaking with ourself; make sure we can. + test-tor: Handshake with tor's ntor implementation via the program + src/test/test-ntor-cl; make sure we can. + +""" + +import binascii +import curve25519 +import hashlib +import hmac +import subprocess + +# ********************************************************************** +# Helpers and constants + +def HMAC(key,msg): + "Return the HMAC-SHA256 of 'msg' using the key 'key'." + H = hmac.new(key, "", hashlib.sha256) + H.update(msg) + return H.digest() + +def H(msg,tweak): + """Return the hash of 'msg' using tweak 'tweak'. (In this version of ntor, + the tweaked hash is just HMAC with the tweak as the key.)""" + return HMAC(key=tweak, + msg=msg) + +def keyid(k): + """Return the 32-byte key ID of a public key 'k'. (Since we're + using curve25519, we let k be its own keyid.) + """ + return k.serialize() + +NODE_ID_LENGTH = 20 +KEYID_LENGTH = 32 +G_LENGTH = 32 +H_LENGTH = 32 + +PROTOID = b"ntor-curve25519-sha256-1" +M_EXPAND = PROTOID + ":key_expand" +T_MAC = PROTOID + ":mac" +T_KEY = PROTOID + ":key_extract" +T_VERIFY = PROTOID + ":verify" + +def H_mac(msg): return H(msg, tweak=T_MAC) +def H_verify(msg): return H(msg, tweak=T_VERIFY) + +class PrivateKey(curve25519.keys.Private): + """As curve25519.keys.Private, but doesn't regenerate its public key + every time you ask for it. + """ + def __init__(self): + curve25519.keys.Private.__init__(self) + self._memo_public = None + + def get_public(self): + if self._memo_public is None: + self._memo_public = curve25519.keys.Private.get_public(self) + + return self._memo_public + +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +def kdf_rfc5869(key, salt, info, n): + + prk = HMAC(key=salt, msg=key) + + out = b"" + last = b"" + i = 1 + while len(out) < n: + m = last + info + chr(i) + last = h = HMAC(key=prk, msg=m) + out += h + i = i + 1 + return out[:n] + +def kdf_ntor(key, n): + return kdf_rfc5869(key, T_KEY, M_EXPAND, n) + +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +def client_part1(node_id, pubkey_B): + """Initial handshake, client side. + + From the specification: + + <<To send a create cell, the client generates a keypair x,X = + KEYGEN(), and sends a CREATE cell with contents: + + NODEID: ID -- ID_LENGTH bytes + KEYID: KEYID(B) -- H_LENGTH bytes + CLIENT_PK: X -- G_LENGTH bytes + >> + + Takes node_id -- a digest of the server's identity key, + pubkey_B -- a public key for the server. + Returns a tuple of (client secret key x, client->server message)""" + + assert len(node_id) == NODE_ID_LENGTH + + key_id = keyid(pubkey_B) + seckey_x = PrivateKey() + pubkey_X = seckey_x.get_public().serialize() + + message = node_id + key_id + pubkey_X + + assert len(message) == NODE_ID_LENGTH + H_LENGTH + H_LENGTH + return seckey_x , message + +def hash_nil(x): + """Identity function: if we don't pass a hash function that does nothing, + the curve25519 python lib will try to sha256 it for us.""" + return x + +def bad_result(r): + """Helper: given a result of multiplying a public key by a private key, + return True iff one of the inputs was broken""" + assert len(r) == 32 + return r == '\x00'*32 + +def server(seckey_b, my_node_id, message, keyBytes=72): + """Handshake step 2, server side. + + From the spec: + + << + The server generates a keypair of y,Y = KEYGEN(), and computes + + secret_input = EXP(X,y) | EXP(X,b) | ID | B | X | Y | PROTOID + KEY_SEED = H(secret_input, t_key) + verify = H(secret_input, t_verify) + auth_input = verify | ID | B | Y | X | PROTOID | "Server" + + The server sends a CREATED cell containing: + + SERVER_PK: Y -- G_LENGTH bytes + AUTH: H(auth_input, t_mac) -- H_LENGTH byets + >> + + Takes seckey_b -- the server's secret key + my_node_id -- the servers's public key digest, + message -- a message from a client + keybytes -- amount of key material to generate + + Returns a tuple of (key material, sever->client reply), or None on + error. + """ + + assert len(message) == NODE_ID_LENGTH + H_LENGTH + H_LENGTH + + if my_node_id != message[:NODE_ID_LENGTH]: + return None + + badness = (keyid(seckey_b.get_public()) != + message[NODE_ID_LENGTH:NODE_ID_LENGTH+H_LENGTH]) + + pubkey_X = curve25519.keys.Public(message[NODE_ID_LENGTH+H_LENGTH:]) + seckey_y = PrivateKey() + pubkey_Y = seckey_y.get_public() + pubkey_B = seckey_b.get_public() + xy = seckey_y.get_shared_key(pubkey_X, hash_nil) + xb = seckey_b.get_shared_key(pubkey_X, hash_nil) + + # secret_input = EXP(X,y) | EXP(X,b) | ID | B | X | Y | PROTOID + secret_input = (xy + xb + my_node_id + + pubkey_B.serialize() + + pubkey_X.serialize() + + pubkey_Y.serialize() + + PROTOID) + + verify = H_verify(secret_input) + + # auth_input = verify | ID | B | Y | X | PROTOID | "Server" + auth_input = (verify + + my_node_id + + pubkey_B.serialize() + + pubkey_Y.serialize() + + pubkey_X.serialize() + + PROTOID + + "Server") + + msg = pubkey_Y.serialize() + H_mac(auth_input) + + badness += bad_result(xb) + badness += bad_result(xy) + + if badness: + return None + + keys = kdf_ntor(secret_input, keyBytes) + + return keys, msg + +def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72): + """Handshake step 3: client side again. + + From the spec: + + << + The client then checks Y is in G^* [see NOTE below], and computes + + secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID + KEY_SEED = H(secret_input, t_key) + verify = H(secret_input, t_verify) + auth_input = verify | ID | B | Y | X | PROTOID | "Server" + + The client verifies that AUTH == H(auth_input, t_mac). + >> + + Takes seckey_x -- the secret key we generated in step 1. + msg -- the message from the server. + node_id -- the node_id we used in step 1. + server_key -- the same public key we used in step 1. + keyBytes -- the number of bytes we want to generate + Returns key material, or None on error + + """ + assert len(msg) == G_LENGTH + H_LENGTH + + pubkey_Y = curve25519.keys.Public(msg[:G_LENGTH]) + their_auth = msg[G_LENGTH:] + + pubkey_X = seckey_x.get_public() + + yx = seckey_x.get_shared_key(pubkey_Y, hash_nil) + bx = seckey_x.get_shared_key(pubkey_B, hash_nil) + + + # secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID + secret_input = (yx + bx + node_id + + pubkey_B.serialize() + + pubkey_X.serialize() + + pubkey_Y.serialize() + PROTOID) + + verify = H_verify(secret_input) + + # auth_input = verify | ID | B | Y | X | PROTOID | "Server" + auth_input = (verify + node_id + + pubkey_B.serialize() + + pubkey_Y.serialize() + + pubkey_X.serialize() + PROTOID + + "Server") + + my_auth = H_mac(auth_input) + + badness = my_auth != their_auth + badness = bad_result(yx) + bad_result(bx) + + if badness: + return None + + return kdf_ntor(secret_input, keyBytes) + +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +def demo(node_id="iToldYouAboutStairs.", server_key=PrivateKey()): + """ + Try to handshake with ourself. + """ + x, create = client_part1(node_id, server_key.get_public()) + skeys, created = server(server_key, node_id, create) + ckeys = client_part2(x, created, node_id, server_key.get_public()) + assert len(skeys) == 72 + assert len(ckeys) == 72 + assert skeys == ckeys + +# ====================================================================== +def timing(): + """ + Use Python's timeit module to see how fast this nonsense is + """ + import timeit + t = timeit.Timer(stmt="ntor_ref.demo(N,SK)", + setup="import ntor_ref,curve25519;N='ABCD'*5;SK=ntor_ref.PrivateKey()") + print t.timeit(number=1000) + +# ====================================================================== + +def kdf_vectors(): + """ + Generate some vectors to check our KDF. + """ + import binascii + def kdf_vec(inp): + k = kdf(inp, T_KEY, M_EXPAND, 100) + print repr(inp), "\n\""+ binascii.b2a_hex(k)+ "\"" + kdf_vec("") + kdf_vec("Tor") + kdf_vec("AN ALARMING ITEM TO FIND ON YOUR CREDIT-RATING STATEMENT") + +# ====================================================================== + + +def test_tor(): + """ + Call the test-ntor-cl command-line program to make sure we can + interoperate with Tor's ntor program + """ + enhex=binascii.b2a_hex + dehex=lambda s: binascii.a2b_hex(s.strip()) + + PROG = "./src/test/test-ntor-cl" + def tor_client1(node_id, pubkey_B): + " returns (msg, state) " + p = subprocess.Popen([PROG, "client1", enhex(node_id), + enhex(pubkey_B.serialize())], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + def tor_server1(seckey_b, node_id, msg, n): + " returns (msg, keys) " + p = subprocess.Popen([PROG, "server1", enhex(seckey_b.serialize()), + enhex(node_id), enhex(msg), str(n)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + def tor_client2(state, msg, n): + " returns (keys,) " + p = subprocess.Popen([PROG, "client2", enhex(state), + enhex(msg), str(n)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + + + node_id = "thisisatornodeid$#%^" + seckey_b = PrivateKey() + pubkey_B = seckey_b.get_public() + + # Do a pure-Tor handshake + c2s_msg, c_state = tor_client1(node_id, pubkey_B) + s2c_msg, s_keys = tor_server1(seckey_b, node_id, c2s_msg, 90) + c_keys, = tor_client2(c_state, s2c_msg, 90) + assert c_keys == s_keys + assert len(c_keys) == 90 + + # Try a mixed handshake with Tor as the client + c2s_msg, c_state = tor_client1(node_id, pubkey_B) + s_keys, s2c_msg = server(seckey_b, node_id, c2s_msg, 90) + c_keys, = tor_client2(c_state, s2c_msg, 90) + assert c_keys == s_keys + assert len(c_keys) == 90 + + # Now do a mixed handshake with Tor as the server + c_x, c2s_msg = client_part1(node_id, pubkey_B) + s2c_msg, s_keys = tor_server1(seckey_b, node_id, c2s_msg, 90) + c_keys = client_part2(c_x, s2c_msg, node_id, pubkey_B, 90) + assert c_keys == s_keys + assert len(c_keys) == 90 + + print "We just interoperated." + +# ====================================================================== + +if __name__ == '__main__': + import sys + if sys.argv[1] == 'gen_kdf_vectors': + kdf_vectors() + elif sys.argv[1] == 'timing': + timing() + elif sys.argv[1] == 'self-test': + demo() + elif sys.argv[1] == 'test-tor': + test_tor() + + else: + print __doc__ diff --git a/src/test/test.c b/src/test/test.c index c96aeb7053..32e060080b 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -53,10 +53,14 @@ double fabs(double x); #include "torgzip.h" #include "mempool.h" #include "memarea.h" -#include "onion.h" +#include "onion_tap.h" #include "policies.h" #include "rephist.h" #include "routerparse.h" +#ifdef CURVE25519_ENABLED +#include "crypto_curve25519.h" +#include "onion_ntor.h" +#endif #ifdef USE_DMALLOC #include <dmalloc.h> @@ -815,11 +819,11 @@ test_onion_handshake(void) { /* client-side */ crypto_dh_t *c_dh = NULL; - char c_buf[ONIONSKIN_CHALLENGE_LEN]; + char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; char c_keys[40]; /* server-side */ - char s_buf[ONIONSKIN_REPLY_LEN]; + char s_buf[TAP_ONIONSKIN_REPLY_LEN]; char s_keys[40]; /* shared */ @@ -828,18 +832,18 @@ test_onion_handshake(void) pk = pk_generate(0); /* client handshake 1. */ - memset(c_buf, 0, ONIONSKIN_CHALLENGE_LEN); - test_assert(! onion_skin_create(pk, &c_dh, c_buf)); + memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN); + test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf)); /* server handshake */ - memset(s_buf, 0, ONIONSKIN_REPLY_LEN); + memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN); memset(s_keys, 0, 40); - test_assert(! onion_skin_server_handshake(c_buf, pk, NULL, + test_assert(! onion_skin_TAP_server_handshake(c_buf, pk, NULL, s_buf, s_keys, 40)); /* client handshake 2 */ memset(c_keys, 0, 40); - test_assert(! onion_skin_client_handshake(c_dh, s_buf, c_keys, 40)); + test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); if (memcmp(c_keys, s_keys, 40)) { puts("Aiiiie"); @@ -856,6 +860,60 @@ test_onion_handshake(void) crypto_pk_free(pk); } +#ifdef CURVE25519_ENABLED +static void +test_ntor_handshake(void *arg) +{ + /* client-side */ + ntor_handshake_state_t *c_state = NULL; + uint8_t c_buf[NTOR_ONIONSKIN_LEN]; + uint8_t c_keys[400]; + + /* server-side */ + di_digest256_map_t *s_keymap=NULL; + curve25519_keypair_t s_keypair; + uint8_t s_buf[NTOR_REPLY_LEN]; + uint8_t s_keys[400]; + + /* shared */ + const curve25519_public_key_t *server_pubkey; + uint8_t node_id[20] = "abcdefghijklmnopqrst"; + + (void) arg; + + /* Make the server some keys */ + curve25519_secret_key_generate(&s_keypair.seckey, 0); + curve25519_public_key_generate(&s_keypair.pubkey, &s_keypair.seckey); + dimap_add_entry(&s_keymap, s_keypair.pubkey.public_key, &s_keypair); + server_pubkey = &s_keypair.pubkey; + + /* client handshake 1. */ + memset(c_buf, 0, NTOR_ONIONSKIN_LEN); + tt_int_op(0, ==, onion_skin_ntor_create(node_id, server_pubkey, + &c_state, c_buf)); + + /* server handshake */ + memset(s_buf, 0, NTOR_REPLY_LEN); + memset(s_keys, 0, 40); + tt_int_op(0, ==, onion_skin_ntor_server_handshake(c_buf, s_keymap, NULL, + node_id, + s_buf, s_keys, 400)); + + /* client handshake 2 */ + memset(c_keys, 0, 40); + tt_int_op(0, ==, onion_skin_ntor_client_handshake(c_state, s_buf, + c_keys, 400)); + + test_memeq(c_keys, s_keys, 400); + memset(s_buf, 0, 40); + test_memneq(c_keys, s_buf, 40); + + done: + ntor_handshake_state_free(c_state); + dimap_free(s_keymap, NULL); +} +#endif + static void test_circuit_timeout(void) { @@ -1947,6 +2005,9 @@ static struct testcase_t test_array[] = { ENT(buffers), { "buffer_copy", test_buffer_copy, 0, NULL, NULL }, ENT(onion_handshake), +#ifdef CURVE25519_ENABLED + { "ntor_handshake", test_ntor_handshake, 0, NULL, NULL }, +#endif ENT(circuit_timeout), ENT(policies), ENT(rend_fns), diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 4222c79d92..932124c100 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -9,6 +9,10 @@ #define RELAY_PRIVATE #include "or.h" #include "connection_edge.h" +#include "onion.h" +#include "onion_tap.h" +#include "onion_fast.h" +#include "onion_ntor.h" #include "relay.h" #include "test.h" @@ -374,6 +378,500 @@ test_cfmt_connected_cells(void *arg) tor_free(mem_op_hex_tmp); } +static void +test_cfmt_create_cells(void *arg) +{ + uint8_t b[MAX_ONIONSKIN_CHALLENGE_LEN]; + create_cell_t cc; + cell_t cell; + cell_t cell2; + + (void)arg; + + /* === Let's try parsing some good cells! */ + + /* A valid create cell. */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, TAP_ONIONSKIN_CHALLENGE_LEN); + cell.command = CELL_CREATE; + memcpy(cell.payload, b, TAP_ONIONSKIN_CHALLENGE_LEN); + tt_int_op(0, ==, create_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATE, ==, cc.cell_type); + tt_int_op(ONION_HANDSHAKE_TYPE_TAP, ==, cc.handshake_type); + tt_int_op(TAP_ONIONSKIN_CHALLENGE_LEN, ==, cc.handshake_len); + test_memeq(cc.onionskin, b, TAP_ONIONSKIN_CHALLENGE_LEN + 10); + tt_int_op(0, ==, create_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A valid create_fast cell. */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, CREATE_FAST_LEN); + cell.command = CELL_CREATE_FAST; + memcpy(cell.payload, b, CREATE_FAST_LEN); + tt_int_op(0, ==, create_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATE_FAST, ==, cc.cell_type); + tt_int_op(ONION_HANDSHAKE_TYPE_FAST, ==, cc.handshake_type); + tt_int_op(CREATE_FAST_LEN, ==, cc.handshake_len); + test_memeq(cc.onionskin, b, CREATE_FAST_LEN + 10); + tt_int_op(0, ==, create_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A valid create2 cell with a TAP payload */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, TAP_ONIONSKIN_CHALLENGE_LEN); + cell.command = CELL_CREATE2; + memcpy(cell.payload, "\x00\x00\x00\xBA", 4); /* TAP, 186 bytes long */ + memcpy(cell.payload+4, b, TAP_ONIONSKIN_CHALLENGE_LEN); + tt_int_op(0, ==, create_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATE2, ==, cc.cell_type); + tt_int_op(ONION_HANDSHAKE_TYPE_TAP, ==, cc.handshake_type); + tt_int_op(TAP_ONIONSKIN_CHALLENGE_LEN, ==, cc.handshake_len); + test_memeq(cc.onionskin, b, TAP_ONIONSKIN_CHALLENGE_LEN + 10); + tt_int_op(0, ==, create_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A valid create2 cell with an ntor payload */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, NTOR_ONIONSKIN_LEN); + cell.command = CELL_CREATE2; + memcpy(cell.payload, "\x00\x02\x00\x54", 4); /* ntor, 84 bytes long */ + memcpy(cell.payload+4, b, NTOR_ONIONSKIN_LEN); +#ifdef CURVE25519_ENABLED + tt_int_op(0, ==, create_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATE2, ==, cc.cell_type); + tt_int_op(ONION_HANDSHAKE_TYPE_NTOR, ==, cc.handshake_type); + tt_int_op(NTOR_ONIONSKIN_LEN, ==, cc.handshake_len); + test_memeq(cc.onionskin, b, NTOR_ONIONSKIN_LEN + 10); + tt_int_op(0, ==, create_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); +#else + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); +#endif + + /* A valid create cell with an ntor payload, in legacy format. */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, NTOR_ONIONSKIN_LEN); + cell.command = CELL_CREATE; + memcpy(cell.payload, "ntorNTORntorNTOR", 16); + memcpy(cell.payload+16, b, NTOR_ONIONSKIN_LEN); +#ifdef CURVE25519_ENABLED + tt_int_op(0, ==, create_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATE, ==, cc.cell_type); + tt_int_op(ONION_HANDSHAKE_TYPE_NTOR, ==, cc.handshake_type); + tt_int_op(NTOR_ONIONSKIN_LEN, ==, cc.handshake_len); + test_memeq(cc.onionskin, b, NTOR_ONIONSKIN_LEN + 10); + tt_int_op(0, ==, create_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); +#else + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); +#endif + + /* == Okay, now let's try to parse some impossible stuff. */ + + /* It has to be some kind of a create cell! */ + cell.command = CELL_CREATED; + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); + + /* You can't acutally make an unparseable CREATE or CREATE_FAST cell. */ + + /* Try some CREATE2 cells. First with a bad type. */ + cell.command = CELL_CREATE2; + memcpy(cell.payload, "\x00\x50\x00\x99", 4); /* Type 0x50???? */ + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); + /* Now a good type with an incorrect length. */ + memcpy(cell.payload, "\x00\x00\x00\xBC", 4); /* TAP, 187 bytes.*/ + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); + /* Now a good type with a ridiculous length. */ + memcpy(cell.payload, "\x00\x00\x02\x00", 4); /* TAP, 512 bytes.*/ + tt_int_op(-1, ==, create_cell_parse(&cc, &cell)); + + /* == Time to try formatting bad cells. The important thing is that + we reject big lengths, so just check that for now. */ + cc.handshake_len = 512; + tt_int_op(-1, ==, create_cell_format(&cell2, &cc)); + + /* == Try formatting a create2 cell we don't understand. XXXX */ + + done: + ; +} + +static void +test_cfmt_created_cells(void *arg) +{ + uint8_t b[512]; + created_cell_t cc; + cell_t cell; + cell_t cell2; + + (void)arg; + + /* A good CREATED cell */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, TAP_ONIONSKIN_REPLY_LEN); + cell.command = CELL_CREATED; + memcpy(cell.payload, b, TAP_ONIONSKIN_REPLY_LEN); + tt_int_op(0, ==, created_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATED, ==, cc.cell_type); + tt_int_op(TAP_ONIONSKIN_REPLY_LEN, ==, cc.handshake_len); + test_memeq(cc.reply, b, TAP_ONIONSKIN_REPLY_LEN + 10); + tt_int_op(0, ==, created_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A good CREATED_FAST cell */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, CREATED_FAST_LEN); + cell.command = CELL_CREATED_FAST; + memcpy(cell.payload, b, CREATED_FAST_LEN); + tt_int_op(0, ==, created_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATED_FAST, ==, cc.cell_type); + tt_int_op(CREATED_FAST_LEN, ==, cc.handshake_len); + test_memeq(cc.reply, b, CREATED_FAST_LEN + 10); + tt_int_op(0, ==, created_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A good CREATED2 cell with short reply */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, 64); + cell.command = CELL_CREATED2; + memcpy(cell.payload, "\x00\x40", 2); + memcpy(cell.payload+2, b, 64); + tt_int_op(0, ==, created_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATED2, ==, cc.cell_type); + tt_int_op(64, ==, cc.handshake_len); + test_memeq(cc.reply, b, 80); + tt_int_op(0, ==, created_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* A good CREATED2 cell with maximal reply */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, 496); + cell.command = CELL_CREATED2; + memcpy(cell.payload, "\x01\xF0", 2); + memcpy(cell.payload+2, b, 496); + tt_int_op(0, ==, created_cell_parse(&cc, &cell)); + tt_int_op(CELL_CREATED2, ==, cc.cell_type); + tt_int_op(496, ==, cc.handshake_len); + test_memeq(cc.reply, b, 496); + tt_int_op(0, ==, created_cell_format(&cell2, &cc)); + tt_int_op(cell.command, ==, cell2.command); + test_memeq(cell.payload, cell2.payload, CELL_PAYLOAD_SIZE); + + /* Bogus CREATED2 cell: too long! */ + memset(&cell, 0, sizeof(cell)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, 496); + cell.command = CELL_CREATED2; + memcpy(cell.payload, "\x01\xF1", 2); + tt_int_op(-1, ==, created_cell_parse(&cc, &cell)); + + /* Unformattable CREATED2 cell: too long! */ + cc.handshake_len = 497; + tt_int_op(-1, ==, created_cell_format(&cell2, &cc)); + + done: + ; +} + +static void +test_cfmt_extend_cells(void *arg) +{ + cell_t cell; + uint8_t b[512]; + extend_cell_t ec; + create_cell_t *cc = &ec.create_cell; + uint8_t p[RELAY_PAYLOAD_SIZE]; + uint8_t p2[RELAY_PAYLOAD_SIZE]; + uint8_t p2_cmd; + uint16_t p2_len; + char *mem_op_hex_tmp = NULL; + + (void) arg; + + /* Let's start with a simple EXTEND cell. */ + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, TAP_ONIONSKIN_CHALLENGE_LEN); + memcpy(p, "\x12\xf4\x00\x01\x01\x02", 6); /* 18 244 0 1 : 258 */ + memcpy(p+6,b,TAP_ONIONSKIN_CHALLENGE_LEN); + memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, "electroencephalogram", 20); + tt_int_op(0, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND, + p, 26+TAP_ONIONSKIN_CHALLENGE_LEN)); + tt_int_op(RELAY_COMMAND_EXTEND, ==, ec.cell_type); + tt_str_op("18.244.0.1", ==, fmt_addr(&ec.orport_ipv4.addr)); + tt_int_op(258, ==, ec.orport_ipv4.port); + tt_int_op(AF_UNSPEC, ==, tor_addr_family(&ec.orport_ipv6.addr)); + test_memeq(ec.node_id, "electroencephalogram", 20); + tt_int_op(cc->cell_type, ==, CELL_CREATE); + tt_int_op(cc->handshake_type, ==, ONION_HANDSHAKE_TYPE_TAP); + tt_int_op(cc->handshake_len, ==, TAP_ONIONSKIN_CHALLENGE_LEN); + test_memeq(cc->onionskin, b, TAP_ONIONSKIN_CHALLENGE_LEN+20); + tt_int_op(0, ==, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, ==, RELAY_COMMAND_EXTEND); + tt_int_op(p2_len, ==, 26+TAP_ONIONSKIN_CHALLENGE_LEN); + test_memeq(p2, p, RELAY_PAYLOAD_SIZE); + + /* Let's do an ntor stuffed in a legacy EXTEND cell */ + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, NTOR_ONIONSKIN_LEN); + memcpy(p, "\x12\xf4\x00\x01\x01\x02", 6); /* 18 244 0 1 : 258 */ + memcpy(p+6,"ntorNTORntorNTOR", 16); + memcpy(p+22, b, NTOR_ONIONSKIN_LEN); + memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, "electroencephalogram", 20); + tt_int_op(0, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND, + p, 26+TAP_ONIONSKIN_CHALLENGE_LEN)); + tt_int_op(RELAY_COMMAND_EXTEND, ==, ec.cell_type); + tt_str_op("18.244.0.1", ==, fmt_addr(&ec.orport_ipv4.addr)); + tt_int_op(258, ==, ec.orport_ipv4.port); + tt_int_op(AF_UNSPEC, ==, tor_addr_family(&ec.orport_ipv6.addr)); + test_memeq(ec.node_id, "electroencephalogram", 20); + tt_int_op(cc->cell_type, ==, CELL_CREATE2); + tt_int_op(cc->handshake_type, ==, ONION_HANDSHAKE_TYPE_NTOR); + tt_int_op(cc->handshake_len, ==, NTOR_ONIONSKIN_LEN); + test_memeq(cc->onionskin, b, NTOR_ONIONSKIN_LEN+20); + tt_int_op(0, ==, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, ==, RELAY_COMMAND_EXTEND); + tt_int_op(p2_len, ==, 26+TAP_ONIONSKIN_CHALLENGE_LEN); + test_memeq(p2, p, RELAY_PAYLOAD_SIZE); + tt_int_op(0, ==, create_cell_format_relayed(&cell, cc)); + + /* Now let's do a minimal ntor EXTEND2 cell. */ + memset(&ec, 0xff, sizeof(ec)); + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, NTOR_ONIONSKIN_LEN); + /* 2 items; one 18.244.0.1:61681 */ + memcpy(p, "\x02\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + /* The other is a digest. */ + memcpy(p+9, "\x02\x14" "anarchoindividualist", 22); + /* Prep for the handshake: type and length */ + memcpy(p+31, "\x00\x02\x00\x54", 4); + memcpy(p+35, b, NTOR_ONIONSKIN_LEN); + tt_int_op(0, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, 35+NTOR_ONIONSKIN_LEN)); + tt_int_op(RELAY_COMMAND_EXTEND2, ==, ec.cell_type); + tt_str_op("18.244.0.1", ==, fmt_addr(&ec.orport_ipv4.addr)); + tt_int_op(61681, ==, ec.orport_ipv4.port); + tt_int_op(AF_UNSPEC, ==, tor_addr_family(&ec.orport_ipv6.addr)); + test_memeq(ec.node_id, "anarchoindividualist", 20); + tt_int_op(cc->cell_type, ==, CELL_CREATE2); + tt_int_op(cc->handshake_type, ==, ONION_HANDSHAKE_TYPE_NTOR); + tt_int_op(cc->handshake_len, ==, NTOR_ONIONSKIN_LEN); + test_memeq(cc->onionskin, b, NTOR_ONIONSKIN_LEN+20); + tt_int_op(0, ==, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, ==, RELAY_COMMAND_EXTEND2); + tt_int_op(p2_len, ==, 35+NTOR_ONIONSKIN_LEN); + test_memeq(p2, p, RELAY_PAYLOAD_SIZE); + + /* Now let's do a fanciful EXTEND2 cell. */ + memset(&ec, 0xff, sizeof(ec)); + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, 99); + /* 4 items; one 18 244 0 1 61681 */ + memcpy(p, "\x04\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + /* One is a digest. */ + memcpy(p+9, "\x02\x14" "anthropomorphization", 22); + /* One is an ipv6 address */ + memcpy(p+31, "\x01\x12\x20\x02\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\xf0\xc5\x1e\x11\x12", 20); + /* One is the Konami code. */ + memcpy(p+51, "\xf0\x20upupdowndownleftrightleftrightba", 34); + /* Prep for the handshake: weird type and length */ + memcpy(p+85, "\x01\x05\x00\x63", 4); + memcpy(p+89, b, 99); + tt_int_op(0, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, p, 89+99)); + tt_int_op(RELAY_COMMAND_EXTEND2, ==, ec.cell_type); + tt_str_op("18.244.0.1", ==, fmt_addr(&ec.orport_ipv4.addr)); + tt_int_op(61681, ==, ec.orport_ipv4.port); + tt_str_op("2002::f0:c51e", ==, fmt_addr(&ec.orport_ipv6.addr)); + tt_int_op(4370, ==, ec.orport_ipv6.port); + test_memeq(ec.node_id, "anthropomorphization", 20); + tt_int_op(cc->cell_type, ==, CELL_CREATE2); + tt_int_op(cc->handshake_type, ==, 0x105); + tt_int_op(cc->handshake_len, ==, 99); + test_memeq(cc->onionskin, b, 99+20); + tt_int_op(0, ==, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, ==, RELAY_COMMAND_EXTEND2); + /* We'll generate it minus the IPv6 address and minus the konami code */ + tt_int_op(p2_len, ==, 89+99-34-20); + test_memeq_hex(p2, + /* Two items: one that same darn IP address. */ + "02000612F40001F0F1" + /* The next is a digest : anthropomorphization */ + "0214616e7468726f706f6d6f727068697a6174696f6e" + /* Now the handshake prologue */ + "01050063"); + test_memeq(p2+1+8+22+4, b, 99+20); + tt_int_op(0, ==, create_cell_format_relayed(&cell, cc)); + + /* == Now try parsing some junk */ + + /* Try a too-long handshake */ + memset(p, 0, sizeof(p)); + memcpy(p, "\x02\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + memcpy(p+9, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+31, "\xff\xff\x01\xd0", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* Try two identities. */ + memset(p, 0, sizeof(p)); + memcpy(p, "\x03\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + memcpy(p+9, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+31, "\x02\x14" "autodepolymerization", 22); + memcpy(p+53, "\xff\xff\x00\x10", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* No identities. */ + memset(p, 0, sizeof(p)); + memcpy(p, "\x01\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + memcpy(p+53, "\xff\xff\x00\x10", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* Try a bad IPv4 address (too long, too short)*/ + memset(p, 0, sizeof(p)); + memcpy(p, "\x02\x00\x07\x12\xf4\x00\x01\xf0\xf1\xff", 10); + memcpy(p+10, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+32, "\xff\xff\x00\x10", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + memset(p, 0, sizeof(p)); + memcpy(p, "\x02\x00\x05\x12\xf4\x00\x01\xf0", 8); + memcpy(p+8, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+30, "\xff\xff\x00\x10", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* IPv6 address (too long, too short, no IPv4)*/ + memset(p, 0, sizeof(p)); + memcpy(p, "\x03\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + memcpy(p+9, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+31, "\x01\x13" "xxxxxxxxxxxxxxxxYYZ", 19); + memcpy(p+50, "\xff\xff\x00\x20", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + memset(p, 0, sizeof(p)); + memcpy(p, "\x03\x00\x06\x12\xf4\x00\x01\xf0\xf1", 9); + memcpy(p+9, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+31, "\x01\x11" "xxxxxxxxxxxxxxxxY", 17); + memcpy(p+48, "\xff\xff\x00\x20", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + memset(p, 0, sizeof(p)); + memcpy(p, "\x02", 1); + memcpy(p+1, "\x02\x14" "anarchoindividualist", 22); + memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 18); + memcpy(p+41, "\xff\xff\x00\x20", 4); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* Running out of space in specifiers */ + memset(p,0,sizeof(p)); + memcpy(p, "\x05\x0a\xff", 3); + memcpy(p+3+255, "\x0a\xff", 2); + tt_int_op(-1, ==, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + + /* Fuzz, because why not. */ + memset(&ec, 0xff, sizeof(ec)); + { + int i; + memset(p, 0, sizeof(p)); + for (i = 0; i < 10000; ++i) { + int n = crypto_rand_int(sizeof(p)); + crypto_rand((char *)p, n); + extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, p, n); + } + } + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_cfmt_extended_cells(void *arg) +{ + uint8_t b[512]; + extended_cell_t ec; + created_cell_t *cc = &ec.created_cell; + uint8_t p[RELAY_PAYLOAD_SIZE]; + uint8_t p2[RELAY_PAYLOAD_SIZE]; + uint8_t p2_cmd; + uint16_t p2_len; + char *mem_op_hex_tmp = NULL; + + (void) arg; + + /* Try a regular EXTENDED cell. */ + memset(&ec, 0xff, sizeof(ec)); + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, TAP_ONIONSKIN_REPLY_LEN); + memcpy(p,b,TAP_ONIONSKIN_REPLY_LEN); + tt_int_op(0, ==, extended_cell_parse(&ec, RELAY_COMMAND_EXTENDED, p, + TAP_ONIONSKIN_REPLY_LEN)); + tt_int_op(RELAY_COMMAND_EXTENDED, ==, ec.cell_type); + tt_int_op(cc->cell_type, ==, CELL_CREATED); + tt_int_op(cc->handshake_len, ==, TAP_ONIONSKIN_REPLY_LEN); + test_memeq(cc->reply, b, TAP_ONIONSKIN_REPLY_LEN); + tt_int_op(0, ==, extended_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(RELAY_COMMAND_EXTENDED, ==, p2_cmd); + tt_int_op(TAP_ONIONSKIN_REPLY_LEN, ==, p2_len); + test_memeq(p2, p, sizeof(p2)); + + /* Try an EXTENDED2 cell */ + memset(&ec, 0xff, sizeof(ec)); + memset(p, 0, sizeof(p)); + memset(b, 0, sizeof(b)); + crypto_rand((char*)b, 42); + memcpy(p,"\x00\x2a",2); + memcpy(p+2,b,42); + tt_int_op(0, ==, extended_cell_parse(&ec, RELAY_COMMAND_EXTENDED2, p, 2+42)); + tt_int_op(RELAY_COMMAND_EXTENDED2, ==, ec.cell_type); + tt_int_op(cc->cell_type, ==, CELL_CREATED2); + tt_int_op(cc->handshake_len, ==, 42); + test_memeq(cc->reply, b, 42+10); + tt_int_op(0, ==, extended_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(RELAY_COMMAND_EXTENDED2, ==, p2_cmd); + tt_int_op(2+42, ==, p2_len); + test_memeq(p2, p, sizeof(p2)); + + /* Try an almost-too-long EXTENDED2 cell */ + memcpy(p, "\x01\xf0", 2); + tt_int_op(0, ==, + extended_cell_parse(&ec, RELAY_COMMAND_EXTENDED2, p, sizeof(p))); + + /* Now try a too-long extended2 cell. That's the only misparse I can think + * of. */ + memcpy(p, "\x01\xf1", 2); + tt_int_op(-1, ==, + extended_cell_parse(&ec, RELAY_COMMAND_EXTENDED2, p, sizeof(p))); + + done: + tor_free(mem_op_hex_tmp); +} + #define TEST(name, flags) \ { #name, test_cfmt_ ## name, flags, 0, NULL } @@ -381,6 +879,10 @@ struct testcase_t cell_format_tests[] = { TEST(relay_header, 0), TEST(begin_cells, 0), TEST(connected_cells, 0), + TEST(create_cells, 0), + TEST(created_cells, 0), + TEST(extend_cells, 0), + TEST(extended_cells, 0), END_OF_TESTCASES }; diff --git a/src/test/test_containers.c b/src/test/test_containers.c index 580c5779f0..d6fc5f7812 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -782,6 +782,50 @@ test_container_order_functions(void) ; } +static void +test_di_map(void *arg) +{ + di_digest256_map_t *map = NULL; + const uint8_t key1[] = "In view of the fact that it was "; + const uint8_t key2[] = "superficially convincing, being "; + const uint8_t key3[] = "properly enciphered in a one-tim"; + const uint8_t key4[] = "e cipher scheduled for use today"; + char *v1 = tor_strdup(", it came close to causing a disaster..."); + char *v2 = tor_strdup("I regret to have to advise you that the mission"); + char *v3 = tor_strdup("was actually initiated..."); + /* -- John Brunner, _The Shockwave Rider_ */ + + (void)arg; + + /* Try searching on an empty map. */ + tt_ptr_op(NULL, ==, dimap_search(map, key1, NULL)); + tt_ptr_op(NULL, ==, dimap_search(map, key2, NULL)); + tt_ptr_op(v3, ==, dimap_search(map, key2, v3)); + dimap_free(map, NULL); + map = NULL; + + /* Add a single entry. */ + dimap_add_entry(&map, key1, v1); + tt_ptr_op(NULL, ==, dimap_search(map, key2, NULL)); + tt_ptr_op(v3, ==, dimap_search(map, key2, v3)); + tt_ptr_op(v1, ==, dimap_search(map, key1, NULL)); + + /* Now try it with three entries in the map. */ + dimap_add_entry(&map, key2, v2); + dimap_add_entry(&map, key3, v3); + tt_ptr_op(v1, ==, dimap_search(map, key1, NULL)); + tt_ptr_op(v2, ==, dimap_search(map, key2, NULL)); + tt_ptr_op(v3, ==, dimap_search(map, key3, NULL)); + tt_ptr_op(NULL, ==, dimap_search(map, key4, NULL)); + tt_ptr_op(v1, ==, dimap_search(map, key4, v1)); + + done: + tor_free(v1); + tor_free(v2); + tor_free(v3); + dimap_free(map, NULL); +} + #define CONTAINER_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_container_ ## name } @@ -796,6 +840,7 @@ struct testcase_t container_tests[] = { CONTAINER_LEGACY(strmap), CONTAINER_LEGACY(pqueue), CONTAINER_LEGACY(order_functions), + { "di_map", test_di_map, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index ed1da39670..1cd09e280b 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -5,9 +5,13 @@ #include "orconfig.h" #define CRYPTO_PRIVATE +#define CRYPTO_CURVE25519_PRIVATE #include "or.h" #include "test.h" #include "aes.h" +#ifdef CURVE25519_ENABLED +#include "crypto_curve25519.h" +#endif /** Run unit tests for Diffie-Hellman functionality. */ static void @@ -832,6 +836,177 @@ test_crypto_base32_decode(void) ; } +static void +test_crypto_kdf_TAP(void *arg) +{ + uint8_t key_material[100]; + int r; + char *mem_op_hex_tmp = NULL; + + (void)arg; +#define EXPAND(s) \ + r = crypto_expand_key_material_TAP( \ + (const uint8_t*)(s), strlen(s), \ + key_material, 100) + + /* Test vectors generated with a little python script; feel free to write + * your own. */ + memset(key_material, 0, sizeof(key_material)); + EXPAND(""); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "5ba93c9db0cff93f52b521d7420e43f6eda2784fbf8b4530d8" + "d246dd74ac53a13471bba17941dff7c4ea21bb365bbeeaf5f2" + "c654883e56d11e43c44e9842926af7ca0a8cca12604f945414" + "f07b01e13da42c6cf1de3abfdea9b95f34687cbbe92b9a7383"); + + EXPAND("Tor"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "776c6214fc647aaa5f683c737ee66ec44f03d0372e1cce6922" + "7950f236ddf1e329a7ce7c227903303f525a8c6662426e8034" + "870642a6dabbd41b5d97ec9bf2312ea729992f48f8ea2d0ba8" + "3f45dfda1a80bdc8b80de01b23e3e0ffae099b3e4ccf28dc28"); + + EXPAND("AN ALARMING ITEM TO FIND ON A MONTHLY AUTO-DEBIT NOTICE"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "a340b5d126086c3ab29c2af4179196dbf95e1c72431419d331" + "4844bf8f6afb6098db952b95581fb6c33625709d6f4400b8e7" + "ace18a70579fad83c0982ef73f89395bcc39493ad53a685854" + "daf2ba9b78733b805d9a6824c907ee1dba5ac27a1e466d4d10"); + + done: + tor_free(mem_op_hex_tmp); + +#undef EXPAND +} + +static void +test_crypto_hkdf_sha256(void *arg) +{ + uint8_t key_material[100]; + const uint8_t salt[] = "ntor-curve25519-sha256-1:key_extract"; + const size_t salt_len = strlen((char*)salt); + const uint8_t m_expand[] = "ntor-curve25519-sha256-1:key_expand"; + const size_t m_expand_len = strlen((char*)m_expand); + int r; + char *mem_op_hex_tmp = NULL; + + (void)arg; + +#define EXPAND(s) \ + r = crypto_expand_key_material_rfc5869_sha256( \ + (const uint8_t*)(s), strlen(s), \ + salt, salt_len, \ + m_expand, m_expand_len, \ + key_material, 100) + + /* Test vectors generated with ntor_ref.py */ + memset(key_material, 0, sizeof(key_material)); + EXPAND(""); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "d3490ed48b12a48f9547861583573fe3f19aafe3f81dc7fc75" + "eeed96d741b3290f941576c1f9f0b2d463d1ec7ab2c6bf71cd" + "d7f826c6298c00dbfe6711635d7005f0269493edf6046cc7e7" + "dcf6abe0d20c77cf363e8ffe358927817a3d3e73712cee28d8"); + + EXPAND("Tor"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "5521492a85139a8d9107a2d5c0d9c91610d0f95989975ebee6" + "c02a4f8d622a6cfdf9b7c7edd3832e2760ded1eac309b76f8d" + "66c4a3c4d6225429b3a016e3c3d45911152fc87bc2de9630c3" + "961be9fdb9f93197ea8e5977180801926d3321fa21513e59ac"); + + EXPAND("AN ALARMING ITEM TO FIND ON YOUR CREDIT-RATING STATEMENT"); + tt_int_op(r, ==, 0); + test_memeq_hex(key_material, + "a2aa9b50da7e481d30463adb8f233ff06e9571a0ca6ab6df0f" + "b206fa34e5bc78d063fc291501beec53b36e5a0e434561200c" + "5f8bd13e0f88b3459600b4dc21d69363e2895321c06184879d" + "94b18f078411be70b767c7fc40679a9440a0c95ea83a23efbf"); + + done: + tor_free(mem_op_hex_tmp); +#undef EXPAND +} + +#ifdef CURVE25519_ENABLED +static void +test_crypto_curve25519_impl(void *arg) +{ + /* adapted from curve25519_donna, which adapted it from test-curve25519 + version 20050915, by D. J. Bernstein, Public domain. */ + + unsigned char e1k[32]; + unsigned char e2k[32]; + unsigned char e1e2k[32]; + unsigned char e2e1k[32]; + unsigned char e1[32] = {3}; + unsigned char e2[32] = {5}; + unsigned char k[32] = {9}; + int loop, i; + const int loop_max=10000; + char *mem_op_hex_tmp = NULL; + + (void)arg; + + for (loop = 0; loop < loop_max; ++loop) { + curve25519_impl(e1k,e1,k); + curve25519_impl(e2e1k,e2,e1k); + curve25519_impl(e2k,e2,k); + curve25519_impl(e1e2k,e1,e2k); + test_memeq(e1e2k, e2e1k, 32); + if (loop == loop_max-1) { + break; + } + for (i = 0;i < 32;++i) e1[i] ^= e2k[i]; + for (i = 0;i < 32;++i) e2[i] ^= e1k[i]; + for (i = 0;i < 32;++i) k[i] ^= e1e2k[i]; + } + + test_memeq_hex(e1, + "4faf81190869fd742a33691b0e0824d5" + "7e0329f4dd2819f5f32d130f1296b500"); + test_memeq_hex(e2k, + "05aec13f92286f3a781ccae98995a3b9" + "e0544770bc7de853b38f9100489e3e79"); + test_memeq_hex(e1e2k, + "cd6e8269104eb5aaee886bd2071fba88" + "bd13861475516bc2cd2b6e005e805064"); + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_crypto_curve25519_wrappers(void *arg) +{ + curve25519_public_key_t pubkey1, pubkey2; + curve25519_secret_key_t seckey1, seckey2; + + uint8_t output1[CURVE25519_OUTPUT_LEN]; + uint8_t output2[CURVE25519_OUTPUT_LEN]; + (void)arg; + + /* Test a simple handshake, serializing and deserializing some stuff. */ + curve25519_secret_key_generate(&seckey1, 0); + curve25519_secret_key_generate(&seckey2, 1); + curve25519_public_key_generate(&pubkey1, &seckey1); + curve25519_public_key_generate(&pubkey2, &seckey2); + test_assert(curve25519_public_key_is_ok(&pubkey1)); + test_assert(curve25519_public_key_is_ok(&pubkey2)); + curve25519_handshake(output1, &seckey1, &pubkey2); + curve25519_handshake(output2, &seckey2, &pubkey1); + test_memeq(output1, output2, sizeof(output1)); + + done: + ; +} +#endif + static void * pass_data_setup_fn(const struct testcase_t *testcase) { @@ -863,6 +1038,12 @@ struct testcase_t crypto_tests[] = { { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode), + { "kdf_TAP", test_crypto_kdf_TAP, 0, NULL, NULL }, + { "hkdf_sha256", test_crypto_hkdf_sha256, 0, NULL, NULL }, +#ifdef CURVE25519_ENABLED + { "curve25519_impl", test_crypto_curve25519_impl, 0, NULL, NULL }, + { "curve25519_wrappers", test_crypto_curve25519_wrappers, 0, NULL, NULL }, +#endif END_OF_TESTCASES }; diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c new file mode 100644 index 0000000000..6e6bd2139b --- /dev/null +++ b/src/test/test_ntor_cl.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include <stdio.h> +#include <stdlib.h> + +#define ONION_NTOR_PRIVATE +#include "or.h" +#include "util.h" +#include "compat.h" +#include "crypto.h" +#include "crypto_curve25519.h" +#include "onion_ntor.h" + +#ifndef CURVE25519_ENABLED +#error "This isn't going to work without curve25519." +#endif + +#define N_ARGS(n) STMT_BEGIN { \ + if (argc < (n)) { \ + fprintf(stderr, "%s needs %d arguments.\n",argv[1],n); \ + return 1; \ + } \ + } STMT_END +#define BASE16(idx, var, n) STMT_BEGIN { \ + const char *s = argv[(idx)]; \ + if (base16_decode((char*)var, n, s, strlen(s)) < 0 ) { \ + fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \ + return 1; \ + } \ + } STMT_END +#define INT(idx, var) STMT_BEGIN { \ + var = atoi(argv[(idx)]); \ + if (var <= 0) { \ + fprintf(stderr, "bad integer argument %d (%s)\n",idx,argv[(idx)]); \ + } \ + } STMT_END + +static int +client1(int argc, char **argv) +{ + /* client1 nodeID B -> msg state */ + curve25519_public_key_t B; + uint8_t node_id[DIGEST_LEN]; + ntor_handshake_state_t *state; + uint8_t msg[NTOR_ONIONSKIN_LEN]; + + char buf[1024]; + + memset(&state, 0, sizeof(state)); + + N_ARGS(4); + BASE16(2, node_id, DIGEST_LEN); + BASE16(3, B.public_key, CURVE25519_PUBKEY_LEN); + + if (onion_skin_ntor_create(node_id, &B, &state, msg)<0) { + fprintf(stderr, "handshake failed"); + return 2; + } + + base16_encode(buf, sizeof(buf), (const char*)msg, sizeof(msg)); + printf("%s\n", buf); + base16_encode(buf, sizeof(buf), (void*)state, sizeof(*state)); + printf("%s\n", buf); + ntor_handshake_state_free(state); + return 0; +} + +static int +server1(int argc, char **argv) +{ + uint8_t msg_in[NTOR_ONIONSKIN_LEN]; + curve25519_keypair_t kp; + di_digest256_map_t *keymap=NULL; + uint8_t node_id[DIGEST_LEN]; + int keybytes; + + uint8_t msg_out[NTOR_REPLY_LEN]; + uint8_t *keys; + char *hexkeys; + + char buf[256]; + + /* server1: b nodeID msg N -> msg keys */ + N_ARGS(6); + BASE16(2, kp.seckey.secret_key, CURVE25519_SECKEY_LEN); + BASE16(3, node_id, DIGEST_LEN); + BASE16(4, msg_in, NTOR_ONIONSKIN_LEN); + INT(5, keybytes); + + curve25519_public_key_generate(&kp.pubkey, &kp.seckey); + dimap_add_entry(&keymap, kp.pubkey.public_key, &kp); + + keys = tor_malloc(keybytes); + hexkeys = tor_malloc(keybytes*2+1); + if (onion_skin_ntor_server_handshake( + msg_in, keymap, NULL, node_id, msg_out, keys, + (size_t)keybytes)<0) { + fprintf(stderr, "handshake failed"); + return 2; + } + + base16_encode(buf, sizeof(buf), (const char*)msg_out, sizeof(msg_out)); + printf("%s\n", buf); + base16_encode(hexkeys, keybytes*2+1, (const char*)keys, keybytes); + printf("%s\n", hexkeys); + + tor_free(keys); + tor_free(hexkeys); + return 0; +} + +static int +client2(int argc, char **argv) +{ + struct ntor_handshake_state_t state; + uint8_t msg[NTOR_REPLY_LEN]; + int keybytes; + uint8_t *keys; + char *hexkeys; + + N_ARGS(5); + BASE16(2, (&state), sizeof(state)); + BASE16(3, msg, sizeof(msg)); + INT(4, keybytes); + + keys = tor_malloc(keybytes); + hexkeys = tor_malloc(keybytes*2+1); + if (onion_skin_ntor_client_handshake(&state, msg, keys, keybytes)<0) { + fprintf(stderr, "handshake failed"); + return 2; + } + + base16_encode(hexkeys, keybytes*2+1, (const char*)keys, keybytes); + printf("%s\n", hexkeys); + + tor_free(keys); + tor_free(hexkeys); + + return 0; +} + +int +main(int argc, char **argv) +{ + /* + client1: nodeID B -> msg state + server1: b nodeID msg N -> msg keys + client2: state msg N -> keys + */ + if (argc < 2) { + fprintf(stderr, "I need arguments. Read source for more info.\n"); + return 1; + } else if (!strcmp(argv[1], "client1")) { + return client1(argc, argv); + } else if (!strcmp(argv[1], "server1")) { + return server1(argc, argv); + } else if (!strcmp(argv[1], "client2")) { + return client2(argc, argv); + } else { + fprintf(stderr, "What's a %s?\n", argv[1]); + return 1; + } +} + diff --git a/src/test/test_util.c b/src/test/test_util.c index 3ed2cbb54d..1cd68eb00e 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -2843,6 +2843,16 @@ test_util_di_ops(void) test_eq(neq1, !eq1); } + tt_int_op(1, ==, safe_mem_is_zero("", 0)); + tt_int_op(1, ==, safe_mem_is_zero("", 1)); + tt_int_op(0, ==, safe_mem_is_zero("a", 1)); + tt_int_op(0, ==, safe_mem_is_zero("a", 2)); + tt_int_op(0, ==, safe_mem_is_zero("\0a", 2)); + tt_int_op(1, ==, safe_mem_is_zero("\0\0a", 2)); + tt_int_op(1, ==, safe_mem_is_zero("\0\0\0\0\0\0\0\0", 8)); + tt_int_op(1, ==, safe_mem_is_zero("\0\0\0\0\0\0\0\0a", 8)); + tt_int_op(0, ==, safe_mem_is_zero("\0\0\0\0\0\0\0\0a", 9)); + done: ; } |