commit 84e4e657b4785e4888e567fbc04c8ea29fd43cc4 Author: Damian Johnson atagar@torproject.org Date: Mon Jun 18 13:02:47 2018 -0700
Cell attribute for unused padding
I love our prior commit's effort to be more explicit when Cell data is explicitly ignored. Rather than a commment making this an 'unused' attribute of the base Cell class. This way our callers will have all the information necessary to reconstruct cells if they so desire. --- stem/client/cell.py | 67 ++++++++++++++++++++++++++++++++++-------------- test/unit/client/cell.py | 6 +++++ 2 files changed, 54 insertions(+), 19 deletions(-)
diff --git a/stem/client/cell.py b/stem/client/cell.py index 1b30db6c..788c4c41 100644 --- a/stem/client/cell.py +++ b/stem/client/cell.py @@ -75,12 +75,18 @@ STREAM_ID_DISALLOWED = ( class Cell(object): """ Metadata for ORPort cells. + + :var bytes unused: unused filler that padded the cell to the expected size """
NAME = 'UNKNOWN' VALUE = -1 IS_FIXED_SIZE = False
+ def __init__(self, unused = b''): + super(Cell, self).__init__() + self.unused = unused + @staticmethod def by_name(name): """ @@ -240,7 +246,8 @@ class CircuitCell(Cell): :var int circ_id: circuit id """
- def __init__(self, circ_id): + def __init__(self, circ_id, unused = b''): + super(CircuitCell, self).__init__(unused) self.circ_id = circ_id
@@ -261,6 +268,7 @@ class PaddingCell(Cell): elif len(payload) != FIXED_PAYLOAD_LEN: raise ValueError('Padding payload should be %i bytes, but was %i' % (FIXED_PAYLOAD_LEN, len(payload)))
+ super(PaddingCell, self).__init__() self.payload = payload
def pack(self, link_protocol): @@ -279,12 +287,18 @@ class CreateCell(CircuitCell): VALUE = 1 IS_FIXED_SIZE = True
+ def __init__(self): + super(CreateCell, self).__init__() # TODO: implement +
class CreatedCell(CircuitCell): NAME = 'CREATED' VALUE = 2 IS_FIXED_SIZE = True
+ def __init__(self): + super(CreatedCell, self).__init__() # TODO: implement +
class RelayCell(CircuitCell): """ @@ -302,7 +316,7 @@ class RelayCell(CircuitCell): VALUE = 3 IS_FIXED_SIZE = True
- def __init__(self, circ_id, command, data, digest = 0, stream_id = 0, recognized = 0): + def __init__(self, circ_id, command, data, digest = 0, stream_id = 0, recognized = 0, unused = b''): if 'HASH' in str(type(digest)): # Unfortunately hashlib generates from a dynamic private class so # isinstance() isn't such a great option. With python2/python3 the @@ -316,7 +330,7 @@ class RelayCell(CircuitCell): else: raise ValueError('RELAY cell digest must be a hash, string, or int but was a %s' % type(digest).__name__)
- super(RelayCell, self).__init__(circ_id) + super(RelayCell, self).__init__(circ_id, unused) self.command, self.command_int = RelayCommand.get(command) self.data = str_tools._to_bytes(data) self.recognized = recognized @@ -347,11 +361,9 @@ class RelayCell(CircuitCell): stream_id, content = Size.SHORT.pop(content) digest, content = Size.LONG.pop(content) data_len, content = Size.SHORT.pop(content) - data, _ = split(content, data_len) + data, unused = split(content, data_len)
- # remaining content (if any) is thrown out (ignored) - - return RelayCell(circ_id, command, data, digest, stream_id, recognized) + return RelayCell(circ_id, command, data, digest, stream_id, recognized, unused)
def __hash__(self): return _hash_attr(self, 'command_int', 'stream_id', 'digest', 'data') @@ -481,6 +493,7 @@ class VersionsCell(Cell): IS_FIXED_SIZE = False
def __init__(self, versions): + super(VersionsCell, self).__init__() self.versions = versions
def pack(self, link_protocol = None): @@ -517,7 +530,8 @@ class NetinfoCell(Cell): VALUE = 8 IS_FIXED_SIZE = True
- def __init__(self, receiver_address, sender_addresses, timestamp = None): + def __init__(self, receiver_address, sender_addresses, timestamp = None, unused = b''): + super(NetinfoCell, self).__init__(unused) self.timestamp = timestamp if timestamp else datetime.datetime.now() self.receiver_address = receiver_address self.sender_addresses = sender_addresses @@ -548,9 +562,7 @@ class NetinfoCell(Cell): addr, content = Address.pop(content) sender_addresses.append(addr)
- # remaining content (if any) is thrown out (ignored) - - return NetinfoCell(receiver_address, sender_addresses, datetime.datetime.utcfromtimestamp(timestamp)) + return NetinfoCell(receiver_address, sender_addresses, datetime.datetime.utcfromtimestamp(timestamp), unused = b'')
def __hash__(self): return _hash_attr(self, 'timestamp', 'receiver_address', 'sender_addresses') @@ -561,24 +573,36 @@ class RelayEarlyCell(CircuitCell): VALUE = 9 IS_FIXED_SIZE = True
+ def __init__(self): + super(RelayEarlyCell, self).__init__() # TODO: implement +
class Create2Cell(CircuitCell): NAME = 'CREATE2' VALUE = 10 IS_FIXED_SIZE = True
+ def __init__(self): + super(Create2Cell, self).__init__() # TODO: implement +
class Created2Cell(Cell): NAME = 'CREATED2' VALUE = 11 IS_FIXED_SIZE = True
+ def __init__(self): + super(Created2Cell, self).__init__() # TODO: implement +
class PaddingNegotiateCell(Cell): NAME = 'PADDING_NEGOTIATE' VALUE = 12 IS_FIXED_SIZE = True
+ def __init__(self): + super(PaddingNegotiateCell, self).__init__() # TODO: implement +
class VPaddingCell(Cell): """ @@ -599,6 +623,7 @@ class VPaddingCell(Cell): elif size is not None and payload is not None and size != len(payload): raise ValueError('VPaddingCell constructor specified both a size of %i bytes and payload of %i bytes' % (size, len(payload)))
+ super(VPaddingCell, self).__init__() self.payload = payload if payload is not None else os.urandom(size)
def pack(self, link_protocol): @@ -623,7 +648,8 @@ class CertsCell(Cell): VALUE = 129 IS_FIXED_SIZE = False
- def __init__(self, certs): + def __init__(self, certs, unused = b''): + super(CertsCell, self).__init__(unused) self.certificates = certs
def pack(self, link_protocol): @@ -641,9 +667,7 @@ class CertsCell(Cell): cert, content = Certificate.pop(content) certs.append(cert)
- # remaining content (if any) is thrown out (ignored) - - return CertsCell(certs) + return CertsCell(certs, unused = content)
def __hash__(self): return _hash_attr(self, 'certificates') @@ -662,12 +686,13 @@ class AuthChallengeCell(Cell): VALUE = 130 IS_FIXED_SIZE = False
- def __init__(self, methods, challenge = None): + def __init__(self, methods, challenge = None, unused = b''): if not challenge: challenge = os.urandom(AUTH_CHALLENGE_SIZE) elif len(challenge) != AUTH_CHALLENGE_SIZE: raise ValueError('AUTH_CHALLENGE must be %i bytes, but was %i' % (AUTH_CHALLENGE_SIZE, len(challenge)))
+ super(AuthChallengeCell, self).__init__(unused) self.challenge = challenge self.methods = methods
@@ -698,9 +723,7 @@ class AuthChallengeCell(Cell): method, content = Size.SHORT.pop(content) methods.append(method)
- # remaining content (if any) is thrown out (ignored) - - return AuthChallengeCell(methods, challenge) + return AuthChallengeCell(methods, challenge, unused = content)
def __hash__(self): return _hash_attr(self, 'challenge', 'methods') @@ -711,8 +734,14 @@ class AuthenticateCell(Cell): VALUE = 131 IS_FIXED_SIZE = False
+ def __init__(self): + super(AuthenticateCell, self).__init__() # TODO: implement +
class AuthorizeCell(Cell): NAME = 'AUTHORIZE' VALUE = 132 IS_FIXED_SIZE = False + + def __init__(self): + super(AuthorizeCell, self).__init__() # TODO: implement diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py index 3d054757..b6734fe7 100644 --- a/test/unit/client/cell.py +++ b/test/unit/client/cell.py @@ -173,6 +173,7 @@ class TestCell(unittest.TestCase): self.assertEqual(data, cell.data) self.assertEqual(digest, cell.digest) self.assertEqual(stream_id, cell.stream_id) + self.assertEqual(b'\x00' * (498 - len(cell.data)), cell.unused)
digest = hashlib.sha1(b'hi') self.assertEqual(3257622417, RelayCell(5, 'RELAY_BEGIN_DIR', '', digest, 564346860).digest) @@ -190,6 +191,7 @@ class TestCell(unittest.TestCase): self.assertEqual(circ_id, cell.circ_id) self.assertEqual(reason, cell.reason) self.assertEqual(reason_int, cell.reason_int) + self.assertEqual(b'', cell.unused)
self.assertRaisesRegexp(ValueError, 'Circuit closure reason should be a single byte, but was 2', Cell.pop, b'\x80\x00\x00\x00\x04\x01\x01' + ZERO * 507, 5)
@@ -200,6 +202,7 @@ class TestCell(unittest.TestCase): cell = Cell.pop(cell_bytes, 5)[0] self.assertEqual(circ_id, cell.circ_id) self.assertEqual(key_material, cell.key_material) + self.assertEqual(b'', cell.unused)
self.assertRaisesRegexp(ValueError, 'Key material should be 20 bytes, but was 3', CreateFastCell, 5, 'boo')
@@ -211,6 +214,7 @@ class TestCell(unittest.TestCase): self.assertEqual(circ_id, cell.circ_id) self.assertEqual(key_material, cell.key_material) self.assertEqual(derivative_key, cell.derivative_key) + self.assertEqual(b'', cell.unused)
self.assertRaisesRegexp(ValueError, 'Key material should be 20 bytes, but was 3', CreateFastCell, 5, 'boo')
@@ -227,6 +231,7 @@ class TestCell(unittest.TestCase): self.assertEqual(timestamp, cell.timestamp) self.assertEqual(receiver_address, cell.receiver_address) self.assertEqual(sender_addresses, cell.sender_addresses) + self.assertEqual(b'', cell.unused)
def test_vpadding_cell(self): for cell_bytes, payload in VPADDING_CELLS.items(): @@ -262,6 +267,7 @@ class TestCell(unittest.TestCase): cell = Cell.pop(cell_bytes, 2)[0] self.assertEqual(challenge, cell.challenge) self.assertEqual(methods, cell.methods) + self.assertEqual(b'', cell.unused)
self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE cell should have a payload of 38 bytes, but only had 16', Cell.pop, b'\x00\x00\x82\x00&' + CHALLENGE[:10] + b'\x00\x02\x00\x01\x00\x03', 2) self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE should have 3 methods, but only had 4 bytes for it', Cell.pop, b'\x00\x00\x82\x00&' + CHALLENGE + b'\x00\x03\x00\x01\x00\x03', 2)