commit 9fb787cd89b10254df58deb0d190ac5fdbe58c16 Author: Damian Johnson atagar@torproject.org Date: Sun Jan 14 13:06:00 2018 -0800
Implement PADDING and VPADDING cells
Couple particularly easy cell types. --- stem/client/cell.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++-- test/unit/client/cell.py | 38 +++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/stem/client/cell.py b/stem/client/cell.py index 4fe7952e..995b482a 100644 --- a/stem/client/cell.py +++ b/stem/client/cell.py @@ -38,6 +38,8 @@ Messages communicated over a Tor relay's ORPort.
import inspect import io +import os +import random import sys
from stem import UNDEFINED @@ -219,10 +221,36 @@ class CircuitCell(Cell):
class PaddingCell(Cell): + """ + Randomized content to either keep activity going on a circuit. + + :var bytes payload: randomized payload + """ + NAME = 'PADDING' VALUE = 0 IS_FIXED_SIZE = True
+ def __init__(self, payload): + self.payload = payload + + @classmethod + def pack(cls, link_version, payload = None): + """ + Provides a randomized padding payload. + + :param int link_version: link protocol version + :param bytes payload: padding payload + + :returns: **bytes** with randomized content + """ + + return cls._pack(link_version, payload if payload else os.urandom(FIXED_PAYLOAD_LEN)) + + @classmethod + def _unpack(cls, content, circ_id, link_version): + return PaddingCell(content) +
class CreateCell(CircuitCell): NAME = 'CREATE' @@ -332,10 +360,46 @@ class PaddingNegotiateCell(Cell):
class VPaddingCell(Cell): + """ + Variable length randomized content to either keep activity going on a circuit. + + :var bytes payload: randomized payload + """ + NAME = 'VPADDING' VALUE = 128 IS_FIXED_SIZE = False
+ def __init__(self, payload): + self.payload = payload + + @classmethod + def pack(cls, link_version, size = None, payload = None): + """ + Provides a randomized padding payload. If no size or payload is provided + then this provides padding of an arbitrarily chosen size between 128-1024. + + :param int link_version: link protocol version + :param int size: number of bytes to pad + :param bytes payload: padding payload + + :returns: **bytes** with randomized content + + :raises: **ValueError** if both a size and payload are provided, and they + mismatch + """ + + if payload is None: + payload = os.urandom(size) if size else os.urandom(random.randint(128, 1024)) + elif size is not None and size != len(payload): + raise ValueError('VPaddingCell.pack caller specified both a size of %i bytes and payload of %i bytes' % (size, len(payload))) + + return cls._pack(link_version, payload) + + @classmethod + def _unpack(cls, content, circ_id, link_version): + return VPaddingCell(content) +
class CertsCell(Cell): """ @@ -352,12 +416,12 @@ class CertsCell(Cell): self.certificates = certs
@classmethod - def pack(cls, certs, link_version): + def pack(cls, link_version, certs): """ Provides the payload for a series of certificates.
- :param list certs: series of :class:`~stem.client.Certificate` for the cell :param int link_version: link protocol version + :param list certs: series of :class:`~stem.client.Certificate` for the cell
:returns: **bytes** with a payload for these versions """ diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py index b26549e2..0d2b677d 100644 --- a/test/unit/client/cell.py +++ b/test/unit/client/cell.py @@ -2,18 +2,40 @@ Unit tests for the stem.client.cell. """
+import os import unittest
from stem.client import Certificate -from stem.client.cell import Cell, VersionsCell, CertsCell from test.unit.client import test_data
+from stem.client.cell import ( + FIXED_PAYLOAD_LEN, + Cell, + PaddingCell, + VersionsCell, + VPaddingCell, + CertsCell, +) + +RANDOM_PAYLOAD = os.urandom(FIXED_PAYLOAD_LEN) + +PADDING_CELLS = { + '\x00\x00\x00' + RANDOM_PAYLOAD: RANDOM_PAYLOAD, +} + VERSIONS_CELLS = { '\x00\x00\x07\x00\x00': [], '\x00\x00\x07\x00\x02\x00\x01': [1], '\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03': [1, 2, 3], }
+VPADDING_CELLS = { + '\x00\x00\x80\x00\x00': '', + '\x00\x00\x80\x00\x01\x08': '\x08', + '\x00\x00\x80\x00\x02\x08\x11': '\x08\x11', + '\x00\x00\x80\x01\xfd' + RANDOM_PAYLOAD: RANDOM_PAYLOAD, +} + CERTS_CELLS = { '\x00\x00\x81\x00\x01\x00': [], '\x00\x00\x81\x00\x04\x01\x01\x00\x00': [Certificate(type = 1, value = '')], @@ -49,14 +71,26 @@ class TestCell(unittest.TestCase): # TODO: we need to support more cell types before we can test this self.assertRaisesRegexp(NotImplementedError, 'Unpacking not yet implemented for AUTH_CHALLENGE cells', Cell.unpack, test_data('new_link_cells'), 2)
+ def test_padding_packing(self): + for cell_bytes, payload in PADDING_CELLS.items(): + self.assertEqual(cell_bytes, PaddingCell.pack(2, payload)) + self.assertEqual(payload, Cell.unpack(cell_bytes, 2)[0].payload) + def test_versions_packing(self): for cell_bytes, versions in VERSIONS_CELLS.items(): self.assertEqual(cell_bytes, VersionsCell.pack(versions)) self.assertEqual(versions, Cell.unpack(cell_bytes, 2)[0].versions)
+ def test_vpadding_packing(self): + for cell_bytes, payload in VPADDING_CELLS.items(): + self.assertEqual(cell_bytes, VPaddingCell.pack(2, payload = payload)) + self.assertEqual(payload, Cell.unpack(cell_bytes, 2)[0].payload) + + self.assertRaisesRegexp(ValueError, 'VPaddingCell.pack caller specified both a size of 5 bytes and payload of 1 bytes', VPaddingCell.pack, 2, 5, '\x02') + def test_certs_packing(self): for cell_bytes, certs in CERTS_CELLS.items(): - self.assertEqual(cell_bytes, CertsCell.pack(certs, 2)) + self.assertEqual(cell_bytes, CertsCell.pack(2, certs)) self.assertEqual(certs, Cell.unpack(cell_bytes, 2)[0].certificates)
# extra bytes after the last certificate should be ignored