diff --git a/mixnet/v2/sim/config.py b/mixnet/v2/sim/config.py index 1d70291..b738bae 100644 --- a/mixnet/v2/sim/config.py +++ b/mixnet/v2/sim/config.py @@ -42,6 +42,8 @@ class SimulationConfig: class MixnetConfig: num_nodes: int num_mix_layers: int + # A size of a message payload in bytes (e.g. the size of a block proposal) + payload_size: int # An interval of sending a new real/cover message # A probability of actually sending a message depends on the following parameters. message_interval: int @@ -61,6 +63,7 @@ class MixnetConfig: def validate(self): assert self.num_nodes > 0 assert 0 < self.num_mix_layers <= self.num_nodes + assert self.payload_size > 0 assert self.message_interval > 0 assert self.real_message_prob > 0 assert len(self.real_message_prob_weights) <= self.num_nodes diff --git a/mixnet/v2/sim/config.yaml b/mixnet/v2/sim/config.yaml index f61b86e..a81b813 100644 --- a/mixnet/v2/sim/config.yaml +++ b/mixnet/v2/sim/config.yaml @@ -5,6 +5,8 @@ simulation: mixnet: num_nodes: 100 num_mix_layers: 3 + # A size of a message payload in bytes (e.g. the size of a block proposal) + payload_size: 320 # An interval of sending a new real/cover message # A probability of actually sending a message depends on the following parameters. message_interval: 1 diff --git a/mixnet/v2/sim/node.py b/mixnet/v2/sim/node.py index b7f17b9..b55a5ba 100644 --- a/mixnet/v2/sim/node.py +++ b/mixnet/v2/sim/node.py @@ -15,7 +15,6 @@ from p2p import P2p class Node: INCENTIVE_TX_SIZE = 512 - PAYLOAD = b"BLOCK" PADDING_SEPARATOR = b'\x01' def __init__(self, id: int, env: simpy.Environment, p2p: P2p, config: Config): @@ -72,7 +71,7 @@ class Node: # Set invalid txs for a cover message, # so that nobody will recognize that as a real message to be forwarded to the next mix. incentive_txs = [Attachment(os.urandom(len(bytes(tx)))) for tx in incentive_txs] - return SphinxPacket(public_keys, incentive_txs, self.PAYLOAD) + return SphinxPacket(public_keys, incentive_txs, self.build_payload()) def receive_message(self, msg: SphinxPacket | bytes): """ @@ -85,15 +84,12 @@ class Node: if self.is_my_incentive_tx(incentive_tx): self.log("Receiving SphinxPacket. It's mine!") if msg.is_all_unwrapped(): - if msg.payload == self.PAYLOAD: - # Pad the final msg to the same size as a SphinxPacket, - # assuming that the final msg is going to be sent via secure channels (TLS, Noise, etc.) - final_padded_msg = (msg.payload - + self.PADDING_SEPARATOR - + bytes(len(msg) - len(msg.payload) - len(self.PADDING_SEPARATOR))) - self.env.process(self.p2p.broadcast(self, final_padded_msg)) - else: - self.log("Dropping a cover message: %s" % msg.payload) + # Pad the final msg to the same size as a SphinxPacket, + # assuming that the final msg is going to be sent via secure channels (TLS, Noise, etc.) + final_padded_msg = (msg.payload + + self.PADDING_SEPARATOR + + bytes(len(msg) - len(msg.payload) - len(self.PADDING_SEPARATOR))) + self.env.process(self.p2p.broadcast(self, final_padded_msg)) else: # TODO: use Poisson delay or something else, if necessary yield self.env.timeout(random.uniform(0, self.config.mixnet.max_mix_delay)) @@ -104,6 +100,9 @@ class Node: final_msg = msg[:msg.rfind(self.PADDING_SEPARATOR)] self.log("Received final message: %s" % final_msg) + def build_payload(self) -> bytes: + return b"P" + bytes(self.config.mixnet.payload_size - len(b"P")) + # TODO: This is a dummy logic @classmethod def create_incentive_tx(cls, mix_public_key: X25519PublicKey) -> Attachment: diff --git a/mixnet/v2/sim/sphinx.py b/mixnet/v2/sim/sphinx.py index e659ba1..c0ea75d 100644 --- a/mixnet/v2/sim/sphinx.py +++ b/mixnet/v2/sim/sphinx.py @@ -5,43 +5,32 @@ from copy import deepcopy from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey, X25519PrivateKey - class SphinxPacket: # TODO: define max path length - PADDED_PAYLOAD_SIZE = 321 - PAYLOAD_TRAIL_PADDING_SEPARATOR = b'\x01' def __init__(self, public_keys: list[X25519PublicKey], attachments: list[Attachment], payload: bytes): assert len(public_keys) == len(attachments) - if len(payload) > self.PADDED_PAYLOAD_SIZE - len(self.PAYLOAD_TRAIL_PADDING_SEPARATOR): - raise ValueError("payload too long", len(payload)) - payload += (self.PAYLOAD_TRAIL_PADDING_SEPARATOR - + bytes(self.PADDED_PAYLOAD_SIZE - len(payload) - len(self.PAYLOAD_TRAIL_PADDING_SEPARATOR))) ephemeral_private_key = X25519PrivateKey.generate() ephemeral_public_key = ephemeral_private_key.public_key() shared_keys = [SharedSecret(ephemeral_private_key, pk) for pk in public_keys] - self._header = SphinxHeader(ephemeral_public_key, shared_keys, attachments) - self._payload = payload # TODO: encrypt payload + self.header = SphinxHeader(ephemeral_public_key, shared_keys, attachments) + self.payload = payload # TODO: encrypt payload def __bytes__(self): - return bytes(self._header) + self._payload + return bytes(self.header) + self.payload def __len__(self): return len(bytes(self)) def unwrap(self, private_key: X25519PrivateKey) -> tuple[SphinxPacket, Attachment]: packet = deepcopy(self) - attachment = packet._header.unwrap_inplace(private_key) + attachment = packet.header.unwrap_inplace(private_key) # TODO: decrypt packet._payload return packet, attachment def is_all_unwrapped(self) -> bool: - return self._header.is_all_unwrapped() - - @property - def payload(self) -> bytes: - return self._payload[:self._payload.rfind(self.PAYLOAD_TRAIL_PADDING_SEPARATOR)] + return self.header.is_all_unwrapped() class SphinxHeader: