mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'ntor-resquashed'
Conflicts: src/or/cpuworker.c src/or/or.h src/test/bench.c
This commit is contained in:
+142
-1
@@ -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)
|
||||
@@ -105,6 +111,125 @@ bench_aes(void)
|
||||
crypto_cipher_free(c);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -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) {
|
||||
|
||||
+17
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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__
|
||||
+69
-8
@@ -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),
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user