mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 05:03:43 +01:00
prop224: Add Python integration tests for HS ntor.
This test is identical to the ./src/test/test_ntor.sh integration test.
This commit is contained in:
parent
18ee145cda
commit
ea5901bf1c
2
.gitignore
vendored
2
.gitignore
vendored
@ -179,6 +179,7 @@ uptime-*.json
|
|||||||
/src/test/test-child
|
/src/test/test-child
|
||||||
/src/test/test-memwipe
|
/src/test/test-memwipe
|
||||||
/src/test/test-ntor-cl
|
/src/test/test-ntor-cl
|
||||||
|
/src/test/test-hs-ntor-cl
|
||||||
/src/test/test-switch-id
|
/src/test/test-switch-id
|
||||||
/src/test/test-timers
|
/src/test/test-timers
|
||||||
/src/test/test_workqueue
|
/src/test/test_workqueue
|
||||||
@ -187,6 +188,7 @@ uptime-*.json
|
|||||||
/src/test/test-bt-cl.exe
|
/src/test/test-bt-cl.exe
|
||||||
/src/test/test-child.exe
|
/src/test/test-child.exe
|
||||||
/src/test/test-ntor-cl.exe
|
/src/test/test-ntor-cl.exe
|
||||||
|
/src/test/test-hs-ntor-cl.exe
|
||||||
/src/test/test-memwipe.exe
|
/src/test/test-memwipe.exe
|
||||||
/src/test/test-switch-id.exe
|
/src/test/test-switch-id.exe
|
||||||
/src/test/test-timers.exe
|
/src/test/test-timers.exe
|
||||||
|
408
src/test/hs_ntor_ref.py
Normal file
408
src/test/hs_ntor_ref.py
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright 2017, The Tor Project, Inc
|
||||||
|
# See LICENSE for licensing information
|
||||||
|
|
||||||
|
"""
|
||||||
|
hs_ntor_ref.py
|
||||||
|
|
||||||
|
This module is a reference implementation of the modified ntor protocol
|
||||||
|
proposed for Tor hidden services in proposal 224 (Next Generation Hidden
|
||||||
|
Services) in section [NTOR-WITH-EXTRA-DATA].
|
||||||
|
|
||||||
|
The modified ntor protocol is a single-round protocol, with three steps in total:
|
||||||
|
|
||||||
|
1: Client generates keys and sends them to service via INTRODUCE cell
|
||||||
|
|
||||||
|
2: Service computes key material based on client's keys, and sends its own
|
||||||
|
keys to client via RENDEZVOUS cell
|
||||||
|
|
||||||
|
3: Client computes key material as well.
|
||||||
|
|
||||||
|
It's meant to be used to validate Tor's HS ntor implementation by conducting
|
||||||
|
various integration tests. Specifically it conducts the following three tests:
|
||||||
|
|
||||||
|
- Tests our Python implementation by running the whole protocol in Python and
|
||||||
|
making sure that results are consistent.
|
||||||
|
|
||||||
|
- Tests little-t-tor ntor implementation. We use this Python code to instrument
|
||||||
|
little-t-tor and carry out the handshake by using little-t-tor code. The
|
||||||
|
small C wrapper at src/test/test-hs-ntor-cl is used for this Python module to
|
||||||
|
interface with little-t-tor.
|
||||||
|
|
||||||
|
- Cross-tests Python and little-t-tor implementation by running half of the
|
||||||
|
protocol in Python code and the other in little-t-tor. This is actually two
|
||||||
|
tests so that all parts of the protocol are run both by little-t-tor and
|
||||||
|
Python.
|
||||||
|
|
||||||
|
It requires the curve25519 python module from the curve25519-donna package.
|
||||||
|
|
||||||
|
The whole logic and concept for this test suite was taken from ntor_ref.py.
|
||||||
|
|
||||||
|
*** DO NOT USE THIS IN PRODUCTION. ***
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import os, sys
|
||||||
|
import binascii
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
try:
|
||||||
|
import curve25519
|
||||||
|
curve25519mod = curve25519.keys
|
||||||
|
except ImportError:
|
||||||
|
curve25519 = None
|
||||||
|
import slownacl_curve25519
|
||||||
|
curve25519mod = slownacl_curve25519
|
||||||
|
|
||||||
|
try:
|
||||||
|
import sha3
|
||||||
|
except ImportError:
|
||||||
|
# error code 77 tells automake to skip this test
|
||||||
|
sys.exit(77)
|
||||||
|
|
||||||
|
# Import Nick's ntor reference implementation in Python
|
||||||
|
# We are gonna use a few of its utilities.
|
||||||
|
from ntor_ref import hash_nil
|
||||||
|
from ntor_ref import PrivateKey
|
||||||
|
|
||||||
|
# String constants used in this protocol
|
||||||
|
PROTOID = "tor-hs-ntor-curve25519-sha3-256-1"
|
||||||
|
T_HSENC = PROTOID + ":hs_key_extract"
|
||||||
|
T_HSVERIFY = PROTOID + ":hs_verify"
|
||||||
|
T_HSMAC = PROTOID + ":hs_mac"
|
||||||
|
M_HSEXPAND = PROTOID + ":hs_key_expand"
|
||||||
|
|
||||||
|
INTRO_SECRET_LEN = 161
|
||||||
|
REND_SECRET_LEN = 225
|
||||||
|
AUTH_INPUT_LEN = 199
|
||||||
|
|
||||||
|
# Implements MAC(k,m) = H(htonll(len(k)) | k | m)
|
||||||
|
def mac(k,m):
|
||||||
|
def htonll(num):
|
||||||
|
return struct.pack('!q', num)
|
||||||
|
|
||||||
|
s = sha3.SHA3256()
|
||||||
|
s.update(htonll(len(k)))
|
||||||
|
s.update(k)
|
||||||
|
s.update(m)
|
||||||
|
return s.digest()
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# Functions that implement the modified HS ntor protocol
|
||||||
|
|
||||||
|
"""As client compute key material for INTRODUCE cell as follows:
|
||||||
|
|
||||||
|
intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
|
||||||
|
info = m_hsexpand | subcredential
|
||||||
|
hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
|
||||||
|
ENC_KEY = hs_keys[0:S_KEY_LEN]
|
||||||
|
MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
|
||||||
|
"""
|
||||||
|
def intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential):
|
||||||
|
|
||||||
|
dh_result = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil)
|
||||||
|
secret = dh_result + intro_auth_pubkey_str + client_ephemeral_enc_pubkey.serialize() + intro_enc_pubkey.serialize() + PROTOID
|
||||||
|
assert(len(secret) == INTRO_SECRET_LEN)
|
||||||
|
info = M_HSEXPAND + subcredential
|
||||||
|
|
||||||
|
kdf = sha3.SHAKE256()
|
||||||
|
kdf.update(secret + T_HSENC + info)
|
||||||
|
key_material = kdf.squeeze(64*8)
|
||||||
|
|
||||||
|
enc_key = key_material[0:32]
|
||||||
|
mac_key = key_material[32:64]
|
||||||
|
|
||||||
|
return enc_key, mac_key
|
||||||
|
|
||||||
|
"""Wrapper over intro2_ntor_client()"""
|
||||||
|
def client_part1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential):
|
||||||
|
enc_key, mac_key = intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential)
|
||||||
|
assert(enc_key)
|
||||||
|
assert(mac_key)
|
||||||
|
|
||||||
|
return enc_key, mac_key
|
||||||
|
|
||||||
|
"""As service compute key material for INTRODUCE cell as follows:
|
||||||
|
|
||||||
|
intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
|
||||||
|
info = m_hsexpand | subcredential
|
||||||
|
hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
|
||||||
|
HS_DEC_KEY = hs_keys[0:S_KEY_LEN]
|
||||||
|
HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
|
||||||
|
"""
|
||||||
|
def intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, service_enc_privkey, service_enc_pubkey, subcredential):
|
||||||
|
dh_result = service_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||||
|
secret = dh_result + intro_auth_pubkey_str + client_enc_pubkey.serialize() + service_enc_pubkey.serialize() + PROTOID
|
||||||
|
assert(len(secret) == INTRO_SECRET_LEN)
|
||||||
|
info = M_HSEXPAND + subcredential
|
||||||
|
|
||||||
|
kdf = sha3.SHAKE256()
|
||||||
|
kdf.update(secret + T_HSENC + info)
|
||||||
|
key_material = kdf.squeeze(64*8)
|
||||||
|
|
||||||
|
enc_key = key_material[0:32]
|
||||||
|
mac_key = key_material[32:64]
|
||||||
|
|
||||||
|
return enc_key, mac_key
|
||||||
|
|
||||||
|
"""As service compute key material for INTRODUCE and REDNEZVOUS cells.
|
||||||
|
|
||||||
|
Use intro2_ntor_service() to calculate the INTRODUCE key material, and use
|
||||||
|
the following computations to do the RENDEZVOUS ones:
|
||||||
|
|
||||||
|
rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
|
||||||
|
NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
|
||||||
|
verify = MAC(rend_secret_hs_input, t_hsverify)
|
||||||
|
auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
|
||||||
|
AUTH_INPUT_MAC = MAC(auth_input, t_hsmac)
|
||||||
|
"""
|
||||||
|
def service_part1(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential):
|
||||||
|
intro_enc_key, intro_mac_key = intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||||
|
assert(intro_enc_key)
|
||||||
|
assert(intro_mac_key)
|
||||||
|
|
||||||
|
service_ephemeral_privkey = PrivateKey()
|
||||||
|
service_ephemeral_pubkey = service_ephemeral_privkey.get_public()
|
||||||
|
|
||||||
|
dh_result1 = service_ephemeral_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||||
|
dh_result2 = intro_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||||
|
rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + PROTOID
|
||||||
|
assert(len(rend_secret_hs_input) == REND_SECRET_LEN)
|
||||||
|
|
||||||
|
ntor_key_seed = mac(rend_secret_hs_input, T_HSENC)
|
||||||
|
verify = mac(rend_secret_hs_input, T_HSVERIFY)
|
||||||
|
auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + client_enc_pubkey.serialize() + PROTOID + "Server"
|
||||||
|
assert(len(auth_input) == AUTH_INPUT_LEN)
|
||||||
|
auth_input_mac = mac(auth_input, T_HSMAC)
|
||||||
|
|
||||||
|
assert(ntor_key_seed)
|
||||||
|
assert(auth_input_mac)
|
||||||
|
assert(service_ephemeral_pubkey)
|
||||||
|
|
||||||
|
return intro_enc_key, intro_mac_key, ntor_key_seed, auth_input_mac, service_ephemeral_pubkey
|
||||||
|
|
||||||
|
"""As client compute key material for rendezvous cells as follows:
|
||||||
|
|
||||||
|
rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
|
||||||
|
NTOR_KEY_SEED = MAC(ntor_secret_input, t_hsenc)
|
||||||
|
verify = MAC(ntor_secret_input, t_hsverify)
|
||||||
|
auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
|
||||||
|
AUTH_INPUT_MAC = MAC(auth_input, t_hsmac)
|
||||||
|
"""
|
||||||
|
def client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_ephemeral_rend_pubkey):
|
||||||
|
dh_result1 = client_ephemeral_enc_privkey.get_shared_key(service_ephemeral_rend_pubkey, hash_nil)
|
||||||
|
dh_result2 = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil)
|
||||||
|
rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + PROTOID
|
||||||
|
assert(len(rend_secret_hs_input) == REND_SECRET_LEN)
|
||||||
|
|
||||||
|
ntor_key_seed = mac(rend_secret_hs_input, T_HSENC)
|
||||||
|
verify = mac(rend_secret_hs_input, T_HSVERIFY)
|
||||||
|
auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + PROTOID + "Server"
|
||||||
|
assert(len(auth_input) == AUTH_INPUT_LEN)
|
||||||
|
auth_input_mac = mac(auth_input, T_HSMAC)
|
||||||
|
|
||||||
|
assert(ntor_key_seed)
|
||||||
|
assert(auth_input_mac)
|
||||||
|
|
||||||
|
return ntor_key_seed, auth_input_mac
|
||||||
|
|
||||||
|
#################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Utilities for communicating with the little-t-tor ntor wrapper to conduct the
|
||||||
|
integration tests
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROG = b"./src/test/test-hs-ntor-cl"
|
||||||
|
enhex=lambda s: binascii.b2a_hex(s)
|
||||||
|
dehex=lambda s: binascii.a2b_hex(s.strip())
|
||||||
|
|
||||||
|
def tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_privkey, subcredential):
|
||||||
|
p = subprocess.Popen([PROG, "client1",
|
||||||
|
enhex(intro_auth_pubkey_str),
|
||||||
|
enhex(intro_enc_pubkey.serialize()),
|
||||||
|
enhex(client_ephemeral_enc_privkey.serialize()),
|
||||||
|
enhex(subcredential)],
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
return map(dehex, p.stdout.readlines())
|
||||||
|
|
||||||
|
def tor_server1(intro_auth_pubkey_str, intro_enc_privkey,
|
||||||
|
client_ephemeral_enc_pubkey, subcredential):
|
||||||
|
p = subprocess.Popen([PROG, "server1",
|
||||||
|
enhex(intro_auth_pubkey_str),
|
||||||
|
enhex(intro_enc_privkey.serialize()),
|
||||||
|
enhex(client_ephemeral_enc_pubkey.serialize()),
|
||||||
|
enhex(subcredential)],
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
return map(dehex, p.stdout.readlines())
|
||||||
|
|
||||||
|
def tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_ephemeral_rend_pubkey, subcredential):
|
||||||
|
p = subprocess.Popen([PROG, "client2",
|
||||||
|
enhex(intro_auth_pubkey_str),
|
||||||
|
enhex(client_ephemeral_enc_privkey.serialize()),
|
||||||
|
enhex(intro_enc_pubkey.serialize()),
|
||||||
|
enhex(service_ephemeral_rend_pubkey.serialize()),
|
||||||
|
enhex(subcredential)],
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
return map(dehex, p.stdout.readlines())
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
|
||||||
|
# Perform a pure python ntor test
|
||||||
|
def do_pure_python_ntor_test():
|
||||||
|
# Initialize all needed key material
|
||||||
|
client_ephemeral_enc_privkey = PrivateKey()
|
||||||
|
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||||
|
intro_enc_privkey = PrivateKey()
|
||||||
|
intro_enc_pubkey = intro_enc_privkey.get_public()
|
||||||
|
intro_auth_pubkey_str = os.urandom(32)
|
||||||
|
subcredential = os.urandom(32)
|
||||||
|
|
||||||
|
client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential)
|
||||||
|
|
||||||
|
service_enc_key, service_mac_key, service_ntor_key_seed, service_auth_input_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||||
|
|
||||||
|
assert(client_enc_key == service_enc_key)
|
||||||
|
assert(client_mac_key == service_mac_key)
|
||||||
|
|
||||||
|
client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_ephemeral_pubkey)
|
||||||
|
|
||||||
|
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||||
|
assert(client_auth_input_mac == service_auth_input_mac)
|
||||||
|
|
||||||
|
print "DONE: python dance [%s]" % repr(client_auth_input_mac)
|
||||||
|
|
||||||
|
# Perform a pure little-t-tor integration test.
|
||||||
|
def do_little_t_tor_ntor_test():
|
||||||
|
# Initialize all needed key material
|
||||||
|
subcredential = os.urandom(32)
|
||||||
|
client_ephemeral_enc_privkey = PrivateKey()
|
||||||
|
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||||
|
intro_enc_privkey = PrivateKey()
|
||||||
|
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||||
|
intro_auth_pubkey_str = os.urandom(32)
|
||||||
|
|
||||||
|
client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_privkey, subcredential)
|
||||||
|
assert(client_enc_key)
|
||||||
|
assert(client_mac_key)
|
||||||
|
|
||||||
|
service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str,
|
||||||
|
intro_enc_privkey,
|
||||||
|
client_ephemeral_enc_pubkey,
|
||||||
|
subcredential)
|
||||||
|
assert(service_enc_key)
|
||||||
|
assert(service_mac_key)
|
||||||
|
assert(service_ntor_auth_mac)
|
||||||
|
assert(service_ntor_key_seed)
|
||||||
|
|
||||||
|
assert(client_enc_key == service_enc_key)
|
||||||
|
assert(client_mac_key == service_mac_key)
|
||||||
|
|
||||||
|
# Turn from bytes to key
|
||||||
|
service_eph_pubkey = curve25519mod.Public(service_eph_pubkey)
|
||||||
|
|
||||||
|
client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_eph_pubkey, subcredential)
|
||||||
|
assert(client_ntor_auth_mac)
|
||||||
|
assert(client_ntor_key_seed)
|
||||||
|
|
||||||
|
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||||
|
assert(client_ntor_auth_mac == service_ntor_auth_mac)
|
||||||
|
|
||||||
|
print "DONE: tor dance [%s]" % repr(client_ntor_auth_mac)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Do mixed test as follows:
|
||||||
|
1. C -> S (python mode)
|
||||||
|
2. C <- S (tor mode)
|
||||||
|
3. Client computes keys (python mode)
|
||||||
|
"""
|
||||||
|
def do_first_mixed_test():
|
||||||
|
subcredential = os.urandom(32)
|
||||||
|
|
||||||
|
client_ephemeral_enc_privkey = PrivateKey()
|
||||||
|
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||||
|
intro_enc_privkey = PrivateKey()
|
||||||
|
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||||
|
|
||||||
|
intro_auth_pubkey_str = os.urandom(32)
|
||||||
|
|
||||||
|
# Let's do mixed
|
||||||
|
client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||||
|
subcredential)
|
||||||
|
|
||||||
|
service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str,
|
||||||
|
intro_enc_privkey,
|
||||||
|
client_ephemeral_enc_pubkey,
|
||||||
|
subcredential)
|
||||||
|
assert(service_enc_key)
|
||||||
|
assert(service_mac_key)
|
||||||
|
assert(service_ntor_auth_mac)
|
||||||
|
assert(service_ntor_key_seed)
|
||||||
|
assert(service_eph_pubkey)
|
||||||
|
|
||||||
|
assert(client_enc_key == service_enc_key)
|
||||||
|
assert(client_mac_key == service_mac_key)
|
||||||
|
|
||||||
|
# Turn from bytes to key
|
||||||
|
service_eph_pubkey = curve25519mod.Public(service_eph_pubkey)
|
||||||
|
|
||||||
|
client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_eph_pubkey)
|
||||||
|
|
||||||
|
assert(client_auth_input_mac == service_ntor_auth_mac)
|
||||||
|
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||||
|
|
||||||
|
print "DONE: 1st mixed dance [%s]" % repr(client_auth_input_mac)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Do mixed test as follows:
|
||||||
|
1. C -> S (tor mode)
|
||||||
|
2. C <- S (python mode)
|
||||||
|
3. Client computes keys (tor mode)
|
||||||
|
"""
|
||||||
|
def do_second_mixed_test():
|
||||||
|
subcredential = os.urandom(32)
|
||||||
|
|
||||||
|
client_ephemeral_enc_privkey = PrivateKey()
|
||||||
|
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||||
|
intro_enc_privkey = PrivateKey()
|
||||||
|
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||||
|
|
||||||
|
intro_auth_pubkey_str = os.urandom(32)
|
||||||
|
|
||||||
|
# Let's do mixed
|
||||||
|
client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||||
|
client_ephemeral_enc_privkey, subcredential)
|
||||||
|
assert(client_enc_key)
|
||||||
|
assert(client_mac_key)
|
||||||
|
|
||||||
|
service_enc_key, service_mac_key, service_ntor_key_seed, service_ntor_auth_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||||
|
|
||||||
|
client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||||
|
intro_enc_pubkey, service_ephemeral_pubkey, subcredential)
|
||||||
|
assert(client_ntor_auth_mac)
|
||||||
|
assert(client_ntor_key_seed)
|
||||||
|
|
||||||
|
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||||
|
assert(client_ntor_auth_mac == service_ntor_auth_mac)
|
||||||
|
|
||||||
|
print "DONE: 2nd mixed dance [%s]" % repr(client_ntor_auth_mac)
|
||||||
|
|
||||||
|
def do_mixed_tests():
|
||||||
|
do_first_mixed_test()
|
||||||
|
do_second_mixed_test()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
do_pure_python_ntor_test()
|
||||||
|
do_little_t_tor_ntor_test()
|
||||||
|
do_mixed_tests()
|
@ -20,7 +20,7 @@ TESTSCRIPTS = \
|
|||||||
src/test/test_switch_id.sh
|
src/test/test_switch_id.sh
|
||||||
|
|
||||||
if USEPYTHON
|
if USEPYTHON
|
||||||
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
|
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
|
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
|
||||||
@ -249,6 +249,7 @@ noinst_HEADERS+= \
|
|||||||
src/test/vote_descriptors.inc
|
src/test/vote_descriptors.inc
|
||||||
|
|
||||||
noinst_PROGRAMS+= src/test/test-ntor-cl
|
noinst_PROGRAMS+= src/test/test-ntor-cl
|
||||||
|
noinst_PROGRAMS+= src/test/test-hs-ntor-cl
|
||||||
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
|
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_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||||
src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
||||||
@ -259,6 +260,17 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
|||||||
src_test_test_ntor_cl_AM_CPPFLAGS = \
|
src_test_test_ntor_cl_AM_CPPFLAGS = \
|
||||||
-I"$(top_srcdir)/src/or"
|
-I"$(top_srcdir)/src/or"
|
||||||
|
|
||||||
|
src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c
|
||||||
|
src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||||
|
src_test_test_hs_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
||||||
|
src/common/libor-ctime.a \
|
||||||
|
src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
|
||||||
|
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
|
||||||
|
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
|
||||||
|
src_test_test_hs_ntor_cl_AM_CPPFLAGS = \
|
||||||
|
-I"$(top_srcdir)/src/or"
|
||||||
|
|
||||||
|
|
||||||
noinst_PROGRAMS += src/test/test-bt-cl
|
noinst_PROGRAMS += src/test/test-bt-cl
|
||||||
src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
|
src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
|
||||||
src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
|
src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
|
||||||
@ -271,12 +283,13 @@ src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
|
|||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
src/test/bt_test.py \
|
src/test/bt_test.py \
|
||||||
src/test/ntor_ref.py \
|
src/test/ntor_ref.py \
|
||||||
|
src/test/hs_ntor_ref.py \
|
||||||
src/test/fuzz_static_testcases.sh \
|
src/test/fuzz_static_testcases.sh \
|
||||||
src/test/slownacl_curve25519.py \
|
src/test/slownacl_curve25519.py \
|
||||||
src/test/zero_length_keys.sh \
|
src/test/zero_length_keys.sh \
|
||||||
src/test/test_keygen.sh \
|
src/test/test_keygen.sh \
|
||||||
src/test/test_zero_length_keys.sh \
|
src/test/test_zero_length_keys.sh \
|
||||||
src/test/test_ntor.sh src/test/test_bt.sh \
|
src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \
|
||||||
src/test/test-network.sh \
|
src/test/test-network.sh \
|
||||||
src/test/test_switch_id.sh \
|
src/test/test_switch_id.sh \
|
||||||
src/test/test_workqueue_cancel.sh \
|
src/test/test_workqueue_cancel.sh \
|
||||||
|
11
src/test/test_hs_ntor.sh
Executable file
11
src/test/test_hs_ntor.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Validate Tor's ntor implementation.
|
||||||
|
|
||||||
|
exitcode=0
|
||||||
|
|
||||||
|
# Run the python integration test sand return the exitcode of the python
|
||||||
|
# script. The python script might ask the testsuite to skip it if not all
|
||||||
|
# python dependencies are covered.
|
||||||
|
"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/hs_ntor_ref.py" || exitcode=$?
|
||||||
|
|
||||||
|
exit ${exitcode}
|
255
src/test/test_hs_ntor_cl.c
Normal file
255
src/test/test_hs_ntor_cl.c
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/** This is a wrapper over the little-t-tor HS ntor functions. The wrapper is
|
||||||
|
* used by src/test/hs_ntor_ref.py to conduct the HS ntor integration
|
||||||
|
* tests.
|
||||||
|
*
|
||||||
|
* The logic of this wrapper is basically copied from src/test/test_ntor_cl.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 "hs_ntor.h"
|
||||||
|
#include "onion_ntor.h"
|
||||||
|
|
||||||
|
#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)) < (int)n ) { \
|
||||||
|
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
|
||||||
|
|
||||||
|
/** The first part of the HS ntor protocol. The client-side computes all
|
||||||
|
necessary key material and sends the appropriate message to the service. */
|
||||||
|
static int
|
||||||
|
client1(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* Inputs */
|
||||||
|
curve25519_public_key_t intro_enc_pubkey;
|
||||||
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
|
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||||
|
uint8_t subcredential[DIGEST256_LEN];
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
N_ARGS(6);
|
||||||
|
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||||
|
BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
|
BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
|
||||||
|
CURVE25519_SECKEY_LEN);
|
||||||
|
BASE16(5, subcredential, DIGEST256_LEN);
|
||||||
|
|
||||||
|
/* Generate keypair */
|
||||||
|
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||||
|
&client_ephemeral_enc_keypair.seckey);
|
||||||
|
|
||||||
|
retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
|
||||||
|
&intro_enc_pubkey,
|
||||||
|
&client_ephemeral_enc_keypair,
|
||||||
|
subcredential,
|
||||||
|
&hs_ntor_intro_cell_keys);
|
||||||
|
if (retval < 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send ENC_KEY */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_intro_cell_keys.enc_key,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys.enc_key));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send MAC_KEY */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_intro_cell_keys.mac_key,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys.mac_key));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The second part of the HS ntor protocol. The service-side computes all
|
||||||
|
necessary key material and sends the appropriate message to the client */
|
||||||
|
static int
|
||||||
|
server1(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* Inputs */
|
||||||
|
curve25519_keypair_t intro_enc_keypair;
|
||||||
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
|
curve25519_public_key_t client_ephemeral_enc_pubkey;
|
||||||
|
uint8_t subcredential[DIGEST256_LEN];
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||||
|
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
||||||
|
curve25519_keypair_t service_ephemeral_rend_keypair;
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
N_ARGS(6);
|
||||||
|
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||||
|
BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
|
||||||
|
BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
|
BASE16(5, subcredential, DIGEST256_LEN);
|
||||||
|
|
||||||
|
/* Generate keypair */
|
||||||
|
curve25519_public_key_generate(&intro_enc_keypair.pubkey,
|
||||||
|
&intro_enc_keypair.seckey);
|
||||||
|
curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
|
||||||
|
|
||||||
|
/* Get INTRODUCE1 keys */
|
||||||
|
retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
|
||||||
|
&intro_enc_keypair,
|
||||||
|
&client_ephemeral_enc_pubkey,
|
||||||
|
subcredential,
|
||||||
|
&hs_ntor_intro_cell_keys);
|
||||||
|
if (retval < 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get RENDEZVOUS1 keys */
|
||||||
|
retval = hs_ntor_service_get_rendezvous1_keys(&intro_auth_pubkey,
|
||||||
|
&intro_enc_keypair,
|
||||||
|
&service_ephemeral_rend_keypair,
|
||||||
|
&client_ephemeral_enc_pubkey,
|
||||||
|
&hs_ntor_rend_cell_keys);
|
||||||
|
if (retval < 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send ENC_KEY */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_intro_cell_keys.enc_key,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys.enc_key));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send MAC_KEY */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_intro_cell_keys.mac_key,
|
||||||
|
sizeof(hs_ntor_intro_cell_keys.mac_key));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send AUTH_MAC */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
|
||||||
|
sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send NTOR_KEY_SEED */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
|
||||||
|
sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send service ephemeral pubkey (Y) */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)service_ephemeral_rend_keypair.pubkey.public_key,
|
||||||
|
sizeof(service_ephemeral_rend_keypair.pubkey.public_key));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The final step of the ntor protocol, the client computes and returns the
|
||||||
|
* rendezvous key material. */
|
||||||
|
static int
|
||||||
|
client2(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* Inputs */
|
||||||
|
curve25519_public_key_t intro_enc_pubkey;
|
||||||
|
ed25519_public_key_t intro_auth_pubkey;
|
||||||
|
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||||
|
curve25519_public_key_t service_ephemeral_rend_pubkey;
|
||||||
|
uint8_t subcredential[DIGEST256_LEN];
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
N_ARGS(7);
|
||||||
|
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||||
|
BASE16(3, client_ephemeral_enc_keypair.seckey.secret_key,
|
||||||
|
CURVE25519_SECKEY_LEN);
|
||||||
|
BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
|
BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||||
|
BASE16(6, subcredential, DIGEST256_LEN);
|
||||||
|
|
||||||
|
/* Generate keypair */
|
||||||
|
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||||
|
&client_ephemeral_enc_keypair.seckey);
|
||||||
|
|
||||||
|
/* Get RENDEZVOUS1 keys */
|
||||||
|
retval = hs_ntor_client_get_rendezvous1_keys(&intro_auth_pubkey,
|
||||||
|
&client_ephemeral_enc_keypair,
|
||||||
|
&intro_enc_pubkey,
|
||||||
|
&service_ephemeral_rend_pubkey,
|
||||||
|
&hs_ntor_rend_cell_keys);
|
||||||
|
if (retval < 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send AUTH_MAC */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
|
||||||
|
sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
/* Send NTOR_KEY_SEED */
|
||||||
|
base16_encode(buf, sizeof(buf),
|
||||||
|
(const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
|
||||||
|
sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
|
||||||
|
printf("%s\n", buf);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Perform a different part of the protocol depdning on the argv used. */
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "I need arguments. Read source for more info.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curve25519_init();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user