def dataReceived(self, data): '''We received data from the remote connection. Extract cells from the data stream and send them along to be processed. :param str data: data received from remote end ''' self._buffer += data while Cell.enoughDataForCell(self._buffer): try: cell = Cell.parse(self._buffer, encrypted=True) self._deliverCell(cell) self._buffer = self._buffer[len(cell):] # this shouldn't happen and if it does, it's probably a bug except NotEnoughBytes as e: logging.debug(str(e)) break # XXX remove len(unimplemented cell bytes) from buffer except NotImplementedError: logging.debug("Received a cell we can't handle yet.") logging.debug('buffer contents:\n') logging.debug([ord(i) for i in self._buffer]) raise except (BadHandshakeState, HandshakeFailed, UnexpectedCell) as e: logging.warning(e) self.closeConnection() self._buffer = '' break
def test_parse(self): """Try to parse concrete cell type from a byte string and verify we read the correct cell attributes. """ cell = Cell.parse(self.cell_constants["cell-bytes-good"], encrypted=self.encrypted) assert isinstance(cell, self.cell_constants["cell-type"]) assert cell.header.__dict__ == self.cell_header for key in self.cell_attributes: assert getattr(cell, key) == self.cell_attributes[key] cell2 = Cell.parse(cell.getBytes(), encrypted=self.encrypted) assert cell.getBytes() == cell2.getBytes() assert cell == cell2
def test_parse(self): '''Try to parse concrete cell type from a byte string and verify we read the correct cell attributes. ''' cell = Cell.parse(self.cell_constants['cell-bytes-good'], encrypted=self.encrypted) assert isinstance(cell, self.cell_constants['cell-type']) assert cell.header.__dict__ == self.cell_header for key in self.cell_attributes: assert getattr(cell, key) == self.cell_attributes[key] cell2 = Cell.parse(cell.getBytes(), encrypted=self.encrypted) assert cell.getBytes() == cell2.getBytes() assert cell == cell2
def test_repr(self): '''Verify that a cell's repr can be used to create the same cell. ''' from oppy.cell.fixedlen import ( FixedLenCell, Create2Cell, Created2Cell, DestroyCell, EncryptedCell, NetInfoCell, PaddingCell, ) from oppy.cell.varlen import ( VarLenCell, AuthChallengeCell, CertsCell, VersionsCell, VPaddingCell, ) from oppy.cell.util import ( TLVTriple, CertsCellPayloadItem, ) # XXX should realy just define eq method on cells... cell = Cell.parse(self.cell_constants['cell-bytes-good'], encrypted=self.encrypted) cell2 = eval(repr(cell)) assert cell.getBytes() == cell2.getBytes() assert len(cell) == len(cell2) assert cell == cell2
def test_getBytes(self): '''Verify that cell's 'getBytes()' method returns the correct byte string when cell is parsed from a string. ''' cell = Cell.parse(self.cell_constants['cell-bytes-good'], encrypted=self.encrypted) assert cell.getBytes() == self.cell_constants['cell-bytes-good']
def test_fixed_length_command_shorter_than_required(self): self.mock_struct.unpack.return_value = (None, 0) result = AbstractCell.enoughDataForCell( self.gen_data(509), link_version=4) self.assertFalse(result) self.mock_struct.unpack.assert_called_once_with('!IB', 'abcde')
def test_fixed_length_command_shorter_than_required(self): self.mock_struct.unpack.return_value = (None, 0) result = AbstractCell.enoughDataForCell(self.gen_data(509), link_version=4) self.assertFalse(result) self.mock_struct.unpack.assert_called_once_with('!IB', 'abcde')
def test_variable_length_command_shorter_than_required(self): self.mock_struct.unpack.side_effect = [(None, 7), (8, None)] result = AbstractCell.enoughDataForCell(self.gen_data(7), link_version=4) self.assertFalse(result) self.mock_struct.unpack.assert_has_calls( [call('!IB', 'abcde'), call('!H', 'fg')])
def test_len(self): '''Verify that len(cell) works properly.''' cell = Cell.parse(self.cell_constants['cell-bytes-good'], encrypted=self.encrypted) cell_2 = self.cell_constants['cell-type'].make( self.cell_header['circ_id'], *self.cell_attributes.values()) assert len(cell) == len(cell_2) assert len(cell) == len(self.cell_constants['cell-bytes-good'])
def test_variable_length_command_shorter_than_required(self): self.mock_struct.unpack.side_effect = [(None, 7), (8, None)] result = AbstractCell.enoughDataForCell( self.gen_data(7), link_version=4) self.assertFalse(result) self.mock_struct.unpack.assert_has_calls([ call('!IB', 'abcde'), call('!H', 'fg')])
def test_len(self): """Verify that len(cell) works properly.""" cell = Cell.parse(self.cell_constants["cell-bytes-good"], encrypted=self.encrypted) cell_2 = self.cell_constants["cell-type"].make(self.cell_header["circ_id"], *self.cell_attributes.values()) assert len(cell) == len(cell_2) assert len(cell) == len(self.cell_constants["cell-bytes-good"]) if cell.header.link_version < 4: assert len(cell) == 512 else: assert len(cell) == 514
def dataReceived(self, data): self._buffer += data # TODO: fix underlying cell parsing code. current code does not # handle malicious inputs properly and can't recover from # weird/broken inputs while Cell.enoughDataForCell(self._buffer): try: cell = Cell.parse(self._buffer) self._read_queue.put(cell) self._buffer = self._buffer[len(cell):] # TODO: catch all exceptions and remove that length # from the buffer # TODO: remove len(NotImplementedBytes) from buffer except NotImplementedError as e: msg = ("Connection to {} received an unexpected cell. {}." .format(self.micro_status_entry.fingerprint, e)) self._current_task.errback(msg) break
def dataReceived(self, data): self._buffer += data # TODO: fix underlying cell parsing code. current code does not # handle malicious inputs properly and can't recover from # weird/broken inputs while Cell.enoughDataForCell(self._buffer): try: cell = Cell.parse(self._buffer) self._read_queue.put(cell) self._buffer = self._buffer[len(cell):] # TODO: catch all exceptions and remove that length # from the buffer # TODO: remove len(NotImplementedBytes) from buffer except NotImplementedError as e: msg = ( "Connection to {} received an unexpected cell. {}.".format( self.micro_status_entry.fingerprint, e)) self._current_task.errback(msg) break
def test_parse_helper(self): self.mock_cell.__len__.return_value = 5 header = Mock(spec=AbstractCell.Header, cmd='command', link_version=4) result = AbstractCell._parse(self.gen_data(5), header) self.assertEqual(result, self.mock_cell) self.mock_get_subclass.assert_called_once_with(header, 'abcde') self.mock_get_subclass.return_value.assert_called_once_with(header) self.mock_cell._parseHeader.assert_called_once_with('abcde') self.mock_cell._parsePayload.assert_called_once_with('abcde')
def test_parse_helper(self): self.mock_cell.__len__.return_value = 5 header = Mock( spec=AbstractCell.Header, cmd='command', link_version=4) result = AbstractCell._parse(self.gen_data(5), header) self.assertEqual(result, self.mock_cell) self.mock_get_subclass.assert_called_once_with(header, 'abcde') self.mock_get_subclass.return_value.assert_called_once_with(header) self.mock_cell._parseHeader.assert_called_once_with('abcde') self.mock_cell._parsePayload.assert_called_once_with('abcde')
def test_relay_cmd(self, mock_relaycell): self.mock_struct.unpack.return_value = ('circ id', 3) result = AbstractCell.parse(self.gen_data(6), link_version=4) self.assertEqual(result, mock_relaycell._parse.return_value) self.mock_struct.calcsize.assert_called_once_with('!IB') self.mock_struct.unpack.assert_called_once_with('!IB', 'abcde') mock_relaycell.Header.assert_called_once_with( circ_id='circ id', cmd=3, link_version=4) mock_relaycell._parse.assert_called_once_with( 'abcdef', mock_relaycell.Header.return_value)
def test_fixedlen(self, mock_fixedlencell): self.mock_struct.unpack.return_value = ('circ id', 0) result = AbstractCell.parse(self.gen_data(6), link_version=1) self.assertEqual(result, mock_fixedlencell._parse.return_value) self.mock_struct.calcsize.assert_called_once_with('!HB') self.mock_struct.unpack.assert_called_once_with('!HB', 'abcde') mock_fixedlencell.Header.assert_called_once_with( circ_id='circ id', cmd=0, link_version=1) mock_fixedlencell._parse.assert_called_once_with( 'abcdef', mock_fixedlencell.Header.return_value)
def test_varlen(self, mock_varlencell): self.mock_struct.unpack.return_value = ('circ id', 7) result = AbstractCell.parse(self.gen_data(6), link_version=1) self.assertEqual(result, mock_varlencell._parse.return_value) self.mock_struct.calcsize.assert_called_once_with('!HB') self.mock_struct.unpack.assert_called_once_with('!HB', 'abcde') mock_varlencell.Header.assert_called_once_with(circ_id='circ id', cmd=7, link_version=1) mock_varlencell._parse.assert_called_once_with( 'abcdef', mock_varlencell.Header.return_value)
def dataReceived(self, data): '''We received data from the remote connection. Extract cells from the data stream and send them along to be processed. :param str data: data received from remote end ''' self._buffer += data while Cell.enoughDataForCell(self._buffer): try: cell = Cell.parse(self._buffer, encrypted=True) self._recv(cell) self._buffer = self._buffer[len(cell):] except NotEnoughBytes as e: logging.debug(e) break # TODO: remove len(unimplemented cell bytes) from buffer except NotImplementedError: logging.debug("Received a cell we can't handle yet.") logging.debug('buffer contents:\n') logging.debug([ord(i) for i in self._buffer]) raise
def test_relay_early_encrypted(self, mock_fixedlencell): self.mock_struct.unpack.return_value = ('circ id', 9) result = AbstractCell.parse(self.gen_data(6), link_version=4, encrypted=True) self.assertEqual(result, mock_fixedlencell._parse.return_value) self.mock_struct.calcsize.assert_called_once_with('!IB') self.mock_struct.unpack.assert_called_once_with('!IB', 'abcde') mock_fixedlencell.Header.assert_called_once_with(circ_id='circ id', cmd=9, link_version=4) mock_fixedlencell._parse.assert_called_once_with( 'abcdef', mock_fixedlencell.Header.return_value)
def test_repr(self): """Verify that a cell's repr can be used to create the same cell. """ from oppy.cell.fixedlen import ( FixedLenCell, Create2Cell, Created2Cell, DestroyCell, EncryptedCell, NetInfoCell, PaddingCell, ) from oppy.cell.varlen import VarLenCell, AuthChallengeCell, CertsCell, VersionsCell, VPaddingCell from oppy.cell.util import TLVTriple, CertsCellPayloadItem # XXX should realy just define eq method on cells... cell = Cell.parse(self.cell_constants["cell-bytes-good"], encrypted=self.encrypted) cell2 = eval(repr(cell)) assert cell.getBytes() == cell2.getBytes() assert len(cell) == len(cell2) assert cell == cell2
def decryptCellUntilRecognized(cell, crypt_path, origin=2): '''Decrypt *cell* until it is recognized or we've tried all RelayCrypto's in *crypt_path*. Attempt to decrypt the cell one hop at a time. Stop if the cell is recognized. Raise an exception if the cell is not recognized at all. :param cell cell: cell to decrypt :param list, oppy.crypto.relaycrypto.RelayCrypto crypt_path: list of RelayCrypto instances to use for decryption :param int origin: the originating hop we think this cell came from :returns: the concrete RelayCell type of this decrypted cell ''' assert 0 <= origin <= 2, 'We can only handle 3-hop paths' recognized = False payload = cell.getPayload() for node in xrange(len(crypt_path)): relay_crypto = crypt_path[node] payload = relay_crypto.backward_cipher.decrypt(payload) if cellRecognized(payload, relay_crypto): recognized = True origin = node break if recognized: updated_payload = makePayloadWithDigest(payload) crypt_path[origin].backward_digest.update(updated_payload) if cell.header.link_version < 4: cid = struct.pack('!H', cell.header.circ_id) else: cid = struct.pack('!I', cell.header.circ_id) cmd = struct.pack('!B', cell.header.cmd) dec = Cell.parse(cid + cmd + payload) return (dec, origin) else: raise UnrecognizedCell()
def decryptCell(cell, crypt_path): '''Decrypt *cell* until it is recognized or we've tried all RelayCrypto's in *crypt_path*. Attempt to decrypt the cell one hop at a time. Stop if the cell is recognized. Raise an exception if the cell is not recognized at all. :param cell cell: cell to decrypt :param list, oppy.crypto.relaycrypto.RelayCrypto crypt_path: list of RelayCrypto instances to use for decryption :param int origin: the originating hop we think this cell came from :returns: the concrete RelayCell type of this decrypted cell ''' origin = 0 recognized = False payload = cell.getPayload() for node in crypt_path: payload = node.backward_cipher.decrypt(payload) if _cellRecognized(payload, node): recognized = True break origin += 1 if not recognized: raise UnrecognizedCell() updated_payload = _makePayloadWithDigest(payload) crypt_path[origin].backward_digest.update(updated_payload) if cell.header.link_version < 4: cid = struct.pack('!H', cell.header.circ_id) else: cid = struct.pack('!I', cell.header.circ_id) cmd = struct.pack('!B', cell.header.cmd) dec = Cell.parse(cid + cmd + payload) return (dec, origin)
def test_getBytes_trimmed(self): cell = Cell.parse(self.cell_constants['cell-bytes-good'], encrypted=self.encrypted) assert cell.getBytes(trimmed=True) == self.cell_constants['cell-bytes-good-nopadding']
def test_len(self): from oppy.cell.cell import Cell cell = Cell.parse(vpadding_bytes_good) cell_2 = VPaddingCell.make(CIRC_ID, padding_len=VPADDING_CELL_LEN) assert len(cell) == len(cell_2) assert len(cell) == VPADDING_CELL_LEN + 2 + 1 + 2
def test_data_shorter_than_header(self): result = AbstractCell.enoughDataForCell( self.gen_data(2), link_version=1) self.assertFalse(result) self.assertFalse(self.mock_struct.unpack.called)
def test_data_shorter_than_header(self): result = AbstractCell.enoughDataForCell(self.gen_data(2), link_version=1) self.assertFalse(result) self.assertFalse(self.mock_struct.unpack.called)
def test_len(self): from oppy.cell.cell import Cell cell = Cell.parse(versions_bytes_good) cell_2 = VersionsCell.make(versions=[3]) assert len(cell) == len(cell_2) assert len(cell) == VERSIONS_CELL_LEN + 2 + 1 + 2
def test_getBytes(self): """Verify that cell's 'getBytes()' method returns the correct byte string when cell is parsed from a string. """ cell = Cell.parse(self.cell_constants["cell-bytes-good"], encrypted=self.encrypted) assert cell.getBytes() == self.cell_constants["cell-bytes-good"]
def test_getBytes_trimmed(self): cell = Cell.parse(self.cell_constants["cell-bytes-good"], encrypted=self.encrypted) assert cell.getBytes(trimmed=True) == self.cell_constants["cell-bytes-good-nopadding"]