Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions contrib/conformance_test/conformance_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from sphinxmix.SphinxClient import *
from sphinxmix.SphinxParams import SphinxParams
from sphinxmix.SphinxNode import sphinx_process

from petlib.bn import Bn
from petlib.ec import EcPt

from base64 import b64decode

import sys

if __name__ == '__main__':
sphinx_params = SphinxParams()
args = sys.argv[1:]

dest = b64decode(args[0])
message = b64decode(args[1])

nodes_routing = []
node_keys = []
for node_key_pair in args[2:]:
node_str, key_str = node_key_pair.split(':')

node = Nenc(int(node_str))
key = EcPt.from_binary(b64decode(key_str), sphinx_params.group.G)

nodes_routing.append(node)
node_keys.append(key)

header, delta = create_forward_message(sphinx_params, nodes_routing, node_keys, dest, message)
bin_message = pack_message(sphinx_params, (header, delta))

sys.stdout.buffer.write(bin_message)

96 changes: 96 additions & 0 deletions contrib/conformance_test/conformance_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from sphinxmix.SphinxClient import *
from sphinxmix.SphinxParams import SphinxParams
from sphinxmix.SphinxNode import sphinx_process

from petlib.bn import Bn

from base64 import b64encode

import sys
import subprocess

pki_tuple = namedtuple('pki_tuple', ['id', 'private_key', 'public_key'])

def run_client_under_test(client_command, dest, message, use_nodes, node_keys):
dest_encoded = b64encode(dest).decode()
message_encoded = b64encode(message).decode()

node_key_pairs = []
for i in range(len(use_nodes)):
pair = str(use_nodes[i]) + ':' + b64encode(node_keys[i].export()).decode()
node_key_pairs.append(pair)

run_command = []
for command in client_command.split(' '):
run_command.append(command)

run_command.append(dest_encoded)
run_command.append(message_encoded)

for node_key_pair in node_key_pairs:
run_command.append(node_key_pair)

return subprocess.run(run_command, stdout=subprocess.PIPE).stdout


def initialise_pki(sphinx_params, num_mix_nodes):
pki = {}

for i in range(num_mix_nodes):
node_id = i
private_key = sphinx_params.group.gensecret()
public_key = sphinx_params.group.expon(sphinx_params.group.g, [private_key])
pki[node_id] = pki_tuple(node_id, private_key, public_key)

return pki


def route_message_through_network(sphinx_params, pki, current_node_id, bin_message):
param_dict = { (sphinx_params.max_len, sphinx_params.m): sphinx_params }
_, (header, delta) = unpack_message(param_dict, bin_message)

current_private_key = pki[current_node_id].private_key

while True:
ret = sphinx_process(sphinx_params, current_private_key, header, delta)
(tag, B, (header, delta), mac_key) = ret
routing = PFdecode(sphinx_params, B)

if routing[0] == Relay_flag:
current_node_id = routing[1]
current_private_key = pki[current_node_id].private_key
elif routing[0] == Dest_flag:
return receive_forward(sphinx_params, mac_key, delta)
else:
return None


def test_create_forward_message_creation(client_command, num_mix_nodes=10, num_path_nodes=5):
sphinx_params = SphinxParams()

pki = initialise_pki(sphinx_params, num_mix_nodes)

use_nodes = rand_subset(pki.keys(), num_path_nodes)
nodes_routing = list(map(Nenc, use_nodes))
node_keys = [pki[n].public_key for n in use_nodes]

dest = b'bob'
message = b'this is a test'

bin_message = run_client_under_test(client_command, dest, message, use_nodes, node_keys)

routing_result = route_message_through_network(sphinx_params, pki, use_nodes[0], bin_message)
if routing_result:
routed_dest, routed_message = routing_result

if routed_dest == dest and routed_message == message:
print('Success')
else:
print('Failure')
else:
print('Failure')


if __name__ == '__main__':
test_create_forward_message_creation(sys.argv[1])

40 changes: 40 additions & 0 deletions contrib/conformance_test/conformance_test_usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Forward Sphinx message conformance test from X to Python

This is a conformance test that allows testing whether a forward message created using the given Sphinx client is conformant with the Python implementation of Sphinx. That is, if the given client were to be used to create a forward Sphinx message, and this message was forwarded to a Sphinx network that runs based off of the Python implementation, then it would be routed and received correctly.

## Usage of conformance test

Provided that `client_run_command` represents the command that runs the client under test (which works as specified below), the conformance test is ran as follows:
```
python3 conformance_test.py 'client_run_command'
```
For example, the provided `conformance_client.py` would be tested as follows:
```
python3 conformance_test.py 'python3 conformance_client.py'
```
The test will generate parameters for the client (as specified below), execute it and capture the standard output. It will then simulate routing the resulting message through a Sphinx network using the Python implementation, and checks if it gets routed correctly. `Success` will be printed if the test is successful, `Failure` otherwise.

## Specification for client under test

To perform the conformance test, the client under test must be implemented according to the specification described below. An example "conformance client" implemented in Python can be seen in the `conformance_client.py` file.

The client under test must be runnable from the command line, and accept command line arguments in the following format:
```
client_run_command [destination] [message] [mix_node_1_id]:[mix_node_1_public_key] [mix_node_2_id]:[mix_node_2_public_key] ...
```
For example, like this:
```
python3 conformance_client.py Ym9i dGhpcyBpcyBhIHRlc3Q= 4:AsNPySfFEjwKay1+w1PMeuURgZ9aLJjSbHdOaeo= 9:AntpLX/rveimvKCA1piwMTV4yKqH9b7EyXxROTw=
```
Specifics:
* `client_run_command` is a command that is used to run the client, that can contain spaces. For example:
* `./client`
* `python3 client.py`
* `java -jar client.jar`
* `[destination]` is the Sphinx packet destination as a base64-encoded string
* `[message]` is the Sphinx packet message as a base64-encoded string
* `[mix_node_n_id]` is a string representation of the id for the n-th Sphinx node (e.g `1`)
* `[mix_node_n_public_key]` is a base64-encoded string representation of the n-th Sphinx node's public key
* The client should be able to accept a varying number of (id, public key) pairs

The client must use the input to create a forward Sphinx message, and pack it appropriately into a binary representation. It must then write this message to standard output.