def test_process(self): exp = ExpectFinished() # this probably should use mock objects to check if calcFinished # is called with them state = ConnectionState() msg = Message( ContentType.handshake, bytearray([HandshakeType.finished, 0, 0, 12]) + bytearray(b"\xa3;\x9c\xc9\'E\xbc\xf6\xc7\x96\xaf\x7f")) exp.process(state, msg)
def test_process_with_multiple_values_one_matching_description(self): exp = ExpectAlert(AlertLevel.fatal, [ AlertDescription.record_overflow, AlertDescription.decompression_failure ]) state = ConnectionState() msg = Message(ContentType.alert, bytearray(b'\x02\x16')) # does NOT raise exception exp.process(state, msg)
def generate(self, state): """Creata a single message to empty the list.""" msg = self.fragment_list.pop(0) content_type = msg.contentType data = msg.write() while len(self.fragment_list) > 0: msg_frag = self.fragment_list.pop(0) assert msg_frag.contentType == content_type data += msg_frag.write() msg_ret = Message(content_type, data) return msg_ret
def test_sendMessage(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) blocked = False for res in msgSock.queueMessage(msg): if res in (0, 1): blocked = True else: break # no write so no blocking self.assertFalse(blocked) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) blocked = False for res in msgSock.sendMessage(msg): if res in (0, 1): blocked = True else: break self.assertTrue(blocked) self.assertEqual(len(sock.sent), 2) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) self.assertEqual(bytearray( b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1])
def test_process(self): exp = ExpectChangeCipherSpec() state = ConnectionState() state.msg_sock = mock.MagicMock() msg = Message(ContentType.change_cipher_spec, bytearray(1)) exp.process(state, msg) state.msg_sock.calcPendingStates.assert_not_called() state.msg_sock.changeReadState.assert_called_once_with()
def test__sendMsg_with_errored_out_socket(self): mockSock = mock.MagicMock() mockSock.send.side_effect = socket.error(errno.ETIMEDOUT) sock = TLSRecordLayer(mockSock) msg = Message(ContentType.handshake, bytearray(10)) gen = sock._sendMsg(msg, False) with self.assertRaises(TLSAbruptCloseError): next(gen)
def test_queueMessageBlocking(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) msgSock.queueMessageBlocking(msg) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) msgSock.queueMessageBlocking(msg) self.assertEqual(len(sock.sent), 1) self.assertEqual( bytearray(b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa' * 3), sock.sent[0])
def test_queueMessage_with_conflicting_types(self): sock = MockSocket(bytearray()) msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) for res in msgSock.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) for res in msgSock.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual( bytearray(b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa' * 3), sock.sent[0]) for res in msgSock.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 2) self.assertEqual( bytearray(b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1])
def test_process_with_values_not_matching_anything(self): exp = ExpectAlert(AlertLevel.warning, AlertDescription.bad_record_mac) state = ConnectionState() msg = Message(ContentType.alert, bytearray(b'\xff\xff')) with self.assertRaises(AssertionError) as e: exp.process(state, msg) self.assertEqual( str(e.exception), "Alert level 255 != 1, " "Expected alert description " "\"bad_record_mac\" does not match received " "\"255\"")
def test_process(self): exp = ExpectCertificateRequest() state = ConnectionState() msg = CertificateRequest((3, 3)) msg.create([ ClientCertificateType.rsa_sign, ClientCertificateType.rsa_fixed_dh ], [], [(HashAlgorithm.sha1, SignatureAlgorithm.rsa), (HashAlgorithm.sha256, SignatureAlgorithm.rsa), (HashAlgorithm.sha384, SignatureAlgorithm.rsa)]) msg = Message(ContentType.handshake, msg.write()) exp.process(state, msg)
def run(self): """Execute conversation""" node = self.conversation try: while node is not None: msg = None if node.is_command(): # update connection state node.process(self.state) node = node.child continue elif node.is_expect(): # check peer response try: header, parser = self.state.msg_sock.recvMessageBlocking() except TLSAbruptCloseError: close_node = next((n for n in node.get_all_siblings() if isinstance(n, ExpectClose)), None) if close_node: node = close_node.child continue else: raise AssertionError("Unexpected closure from peer") msg = Message(header.type, parser.bytes) node = next((proc for proc in node.get_all_siblings() if proc.is_match(msg)), None) if node is None: # since we're aborting, the user can't clean up self.state.msg_sock.sock.close() raise AssertionError( "Unexpected message from peer: " + str(msg.contentType) + ", " + str(msg.write()[0]) ) node.process(self.state, msg) node = node.child continue elif node.is_generator(): # send message to peer msg = node.generate(self.state) self.state.msg_sock.sendMessageBlocking(msg) node.post_send(self.state) node = node.child continue else: raise AssertionError("Unknown decision tree node") except: # TODO put into a log print("Error encountered while processing node " + str(node) + " with last message being: " + repr(msg)) raise
def test_queueMessage(self): sock = MockSocket(bytearray()) msgSocket = MessageSocket(sock, None) msg = Message(ContentType.alert, bytearray(b'\xff\xbb')) for res in msgSocket.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\xff\xaa')) for res in msgSocket.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) for res in msgSocket.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0], bytearray( b'\x15' + b'\x00\x00' + b'\x00\x04' + b'\xff\xbb' + b'\xff\xaa'))
def new_generate(state, old_generate=generator.generate, fragment_list=fragment_list, size=size): """Monkey patch for the generate method of the message generator.""" msg = old_generate(state) content_type = msg.contentType data = msg.write() # since empty messages can be created much more easily with # RawMessageGenerator, we don't handle 0 length messages here while len(data) > 0: # move the data to fragment_list (outside the method) fragment_list.append(Message(content_type, data[:size])) data = data[size:] return fragment_list.pop(0)
def test_sig_algs_mismatched(self): sig_algs = [(HashAlgorithm.sha1, SignatureAlgorithm.rsa), (HashAlgorithm.sha256, SignatureAlgorithm.rsa), (HashAlgorithm.sha384, SignatureAlgorithm.rsa)] exp = ExpectCertificateRequest(sig_algs=sig_algs[0:0]) state = ConnectionState() msg = CertificateRequest((3, 3)) msg.create([ ClientCertificateType.rsa_sign, ClientCertificateType.rsa_fixed_dh ], [], sig_algs) msg = Message(ContentType.handshake, msg.write()) with self.assertRaises(AssertionError): exp.process(state, msg)
def new_send(message, padding, old_send=old_send, substitutions=substitutions, xors=xors): """ Monkey patch for the send method of msg socket. message.data is the encrypted tls record, e.g. message.data = aead_encrypt(plain); defined by aead suite message.data = encrypt(plain + padding) + mac; etm message.data = encrypt(plain + mac + padding); mte """ data = message.write() data = substitute_and_xor(data, substitutions, xors) new_message = Message(message.contentType, data) return old_send(new_message, padding)
def test_process_with_resumption(self): exp = ExpectChangeCipherSpec() state = ConnectionState() state.msg_sock = mock.MagicMock() state.resuming = True state.cipher = mock.Mock(name="cipher") state.master_secret = mock.Mock(name="master_secret") state.client_random = mock.Mock(name="client_random") state.server_random = mock.Mock(name="server_random") msg = Message(ContentType.change_cipher_spec, bytearray(1)) exp.process(state, msg) state.msg_sock.calcPendingStates.assert_called_once_with( state.cipher, state.master_secret, state.client_random, state.server_random, None) state.msg_sock.changeReadState.assert_called_once_with()
def test__sendMsg(self): mockSock = MockSocket(bytearray(0)) sock = TLSRecordLayer(mockSock) sock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(10)) # XXX using private method for result in sock._sendMsg(msg, False): if result in (0, 1): self.assertTrue(False, "Blocking socket") else: break self.assertEqual(len(mockSock.sent), 1) self.assertEqual( bytearray(b'\x16' + # handshake message b'\x03\x03' + # version b'\x00\x0a' + # payload length b'\x00' * 10 # payload ), mockSock.sent[0])
def generate(self, state): """Create a tlslite-ng message that can be send""" message = Message(self.content_type, self.data) return message
def test_is_match(self): exp = ExpectChangeCipherSpec() msg = Message(ContentType.change_cipher_spec, bytearray([0])) self.assertTrue(exp.is_match(msg))
def process(self, state): """Send the message over the socket.""" msg = Message(self.content_type, self.data) for _ in state.msg_sock._recordSocket.send(msg): pass
def test_is_match_with_unmatching_content_type(self): exp = ExpectChangeCipherSpec() msg = Message(ContentType.application_data, bytearray([0])) self.assertFalse(exp.is_match(msg))
def run(self): """Execute conversation""" node = self.conversation try: while node is not None: old_node = None msg = None if node.is_command(): # update connection state node.process(self.state) node = node.child continue elif node.is_expect(): # check peer response try: header, parser = self.state.msg_sock.recvMessageBlocking() except (TLSAbruptCloseError, socket.error): close_node = next((n for n in node.get_all_siblings() \ if isinstance(n, ExpectClose)), None) if close_node: close_node.process(self.state, None) node = close_node.child continue else: raise AssertionError("Unexpected closure from peer") msg = Message(header.type, parser.bytes) old_node = node node = next((proc for proc in node.get_all_siblings() if proc.is_match(msg)), None) if node is None: # since we're aborting, the user can't clean up self.state.msg_sock.sock.close() raise AssertionError("Unexpected message from peer: " + guess_response(\ msg.contentType, msg.write(), isinstance(header, RecordHeader2))) node.process(self.state, msg) node = node.child continue elif node.is_generator(): # send message to peer msg = node.generate(self.state) try: if msg.write(): # sendMessageBlocking is buffered and fragmenting # that means that 0-length messages would get lost self.state.msg_sock.sendMessageBlocking(msg) else: for _ in self.state.msg_sock.sendRecord(msg): # make the method into a blocking one pass except socket.error: close_node = next((n for n in node.get_all_siblings() if isinstance(n, ExpectClose)), None) if close_node: node = close_node.child continue else: raise AssertionError("Unexpected closure from peer") # allow generators to perform actions after the message # was sent like updating handshake hashes node.post_send(self.state) node = node.child continue else: raise AssertionError("Unknown decision tree node") except: if self.state.msg_sock: self.state.msg_sock.sock.close() # TODO put into a log if node is None: node = old_node print("Error encountered while processing node " + str(node) + " (child: " + str(node.child) + ") with last message " + "being: " + repr(msg)) raise
def run(self): """Execute conversation""" node = self.conversation try: while node is not None: old_node = None msg = None if node.is_command(): # update connection state node.process(self.state) node = node.child continue elif node.is_expect(): if isinstance(node, ExpectNoMessage): old_timeout = self.state.msg_sock.sock.gettimeout() self.state.msg_sock.sock.settimeout(node.timeout) # check peer response try: header, parser = self.state.msg_sock.\ recvMessageBlocking() except (TLSAbruptCloseError, socket.error) as exc: if isinstance(exc, socket.timeout) and \ isinstance(node, ExpectNoMessage): # for ExpectNoMessage we have nothing to do # but to continue self.state.msg_sock.sock.settimeout(old_timeout) node = node.child continue close_node = next((n for n in node.get_all_siblings() if isinstance(n, ExpectClose)), None) # timeout will happen if the other side hanged, to # try differentiated between (when no alerts are sent) # allow for close only when the connection was actively # closed if close_node and not isinstance(exc, socket.timeout): close_node.process(self.state, None) node = close_node.child continue else: if isinstance(exc, socket.timeout): raise AssertionError( "Timeout when waiting for peer message") else: raise AssertionError( "Unexpected closure from peer") msg = Message(header.type, parser.bytes) old_node = node node = next((proc for proc in node.get_all_siblings() if proc.is_match(msg)), None) if node is None: # since we're aborting, the user can't clean up self.state.msg_sock.sock.close() raise AssertionError("Unexpected message from peer: " + guess_response(\ msg.contentType, msg.write(), isinstance(header, RecordHeader2))) node.process(self.state, msg) node = node.child continue elif node.is_generator(): # send message to peer msg = node.generate(self.state) try: if msg.write(): # sendMessageBlocking is buffered and fragmenting # that means that 0-length messages would get lost self.state.msg_sock.sendMessageBlocking(msg) else: for _ in self.state.msg_sock.sendRecord(msg): # make the method into a blocking one pass except socket.error: close_node = next( (n for n in node.get_all_siblings() if isinstance(n, (ExpectClose, ExpectAlert))), None) if close_node: node = close_node.child continue else: raise AssertionError( "Unexpected closure from peer") # allow generators to perform actions after the message # was sent like updating handshake hashes node.post_send(self.state) node = node.child continue else: raise AssertionError("Unknown decision tree node") except: if self.state.msg_sock: self.state.msg_sock.sock.close() # TODO put into a log if node is None: node = old_node print("Error encountered while processing node " + str(node) + " (child: " + str(node.child) + ") with last message " + "being: " + repr(msg)) raise
def test_is_match_with_arbitrary_data(self): exp = ExpectChangeCipherSpec() msg = Message(ContentType.change_cipher_spec, bytearray([243])) self.assertTrue(exp.is_match(msg))
def test_process(self): exp = ExpectVerify() msg = Message(ContentType.handshake, bytearray([SSL2HandshakeType.server_verify])) exp.process(None, msg)
def test_is_match(self): exp = ExpectApplicationData() msg = Message(ContentType.application_data, bytearray(0)) self.assertTrue(exp.is_match(msg))
def test_process(self): exp = ExpectSSL2Alert(SSL2ErrorDescription.bad_certificate) msg = Message(ContentType.handshake, bytearray([SSL2HandshakeType.error, 0x00, 0x04])) exp.process(None, msg)
def test_is_match(self): exp = ExpectAlert() msg = Message(ContentType.alert, bytearray(2)) self.assertTrue(exp.is_match(msg))