diff --git a/scapy/layers/tls/automaton.py b/scapy/layers/tls/automaton.py index 2064c7aaa1d..3b951fe42ee 100644 --- a/scapy/layers/tls/automaton.py +++ b/scapy/layers/tls/automaton.py @@ -39,6 +39,16 @@ class _TLSAutomaton(Automaton): We call these successive groups of messages: ClientFlight1, ServerFlight1, ClientFlight2 and ServerFlight2. + With TLS 1.3, the handshake require only 1-RTT: + + Client Server + | --------->>> | C1 - ClientHello + | <<<--------- | S1 - ServerHello + | <<<--------- | S1 - Certificate [encrypted] + | <<<--------- | S1 - CertificateVerify [encrypted] + | <<<--------- | S1 - Finished [encrypted] + | --------->>> | C2 - Finished [encrypted] + We want to send our messages from the same flight all at once through the socket. This is achieved by managing a list of records in 'buffer_out'. We may put several messages (i.e. what RFC 5246 calls the record fragments) @@ -137,20 +147,27 @@ def get_next_msg(self, socket_timeout=2, retry=2): # Remote peer is not willing to respond return - p = TLS(self.remain_in, tls_session=self.cur_session) - self.cur_session = p.tls_session - self.remain_in = b"" - if isinstance(p, SSLv2) and not p.msg: - p.msg = Raw("") - if self.cur_session.tls_version is None or \ - self.cur_session.tls_version < 0x0304: - self.buffer_in += p.msg + if (byte0 == 0x17 and + (self.cur_session.advertised_tls_version >= 0x0304 or + self.cur_session.tls_version >= 0x0304)): + p = TLS13(self.remain_in, tls_session=self.cur_session) + self.remain_in = b"" + self.buffer_in += p.inner.msg else: - if isinstance(p, TLS13): - self.buffer_in += p.inner.msg - else: - # should be TLS13ServerHello only + p = TLS(self.remain_in, tls_session=self.cur_session) + self.cur_session = p.tls_session + self.remain_in = b"" + if isinstance(p, SSLv2) and not p.msg: + p.msg = Raw("") + if self.cur_session.tls_version is None or \ + self.cur_session.tls_version < 0x0304: self.buffer_in += p.msg + else: + if isinstance(p, TLS13): + self.buffer_in += p.inner.msg + else: + # should be TLS13ServerHello only + self.buffer_in += p.msg while p.payload: if isinstance(p.payload, Raw): @@ -173,8 +190,11 @@ def raise_on_packet(self, pkt_cls, state, get_next_msg=True): # Maybe we already parsed the expected packet, maybe not. if get_next_msg: self.get_next_msg() + from scapy.layers.tls.handshake import TLSClientHello if (not self.buffer_in or - not isinstance(self.buffer_in[0], pkt_cls)): + (not isinstance(self.buffer_in[0], pkt_cls) and + not (isinstance(self.buffer_in[0], TLSClientHello) and + self.cur_session.advertised_tls_version == 0x0304))): return self.cur_pkt = self.buffer_in[0] self.buffer_in = self.buffer_in[1:] diff --git a/scapy/layers/tls/automaton_cli.py b/scapy/layers/tls/automaton_cli.py index 82deae47171..2953d4937d7 100644 --- a/scapy/layers/tls/automaton_cli.py +++ b/scapy/layers/tls/automaton_cli.py @@ -20,6 +20,7 @@ from __future__ import print_function import socket +from scapy.config import conf from scapy.pton_ntop import inet_pton from scapy.utils import randstring, repr_hex from scapy.automaton import ATMT @@ -27,11 +28,11 @@ from scapy.layers.tls.basefields import _tls_version, _tls_version_options from scapy.layers.tls.session import tlsSession from scapy.layers.tls.extensions import TLS_Ext_SupportedGroups, \ - TLS_Ext_SupportedVersions, TLS_Ext_SignatureAlgorithms + TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \ TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, \ TLSEncryptedExtensions, TLSFinished, TLSServerHello, TLSServerHelloDone, \ - TLSServerKeyExchange, TLS13Certificate, TLS13ServerHello + TLSServerKeyExchange, TLS13Certificate, TLS13ClientHello, TLS13ServerHello from scapy.layers.tls.handshake_sslv2 import SSLv2ClientHello, \ SSLv2ServerHello, SSLv2ClientMasterKey, SSLv2ServerVerify, \ SSLv2ClientFinished, SSLv2ServerFinished, SSLv2ClientCertificate, \ @@ -40,6 +41,8 @@ KeyShareEntry from scapy.layers.tls.record import TLSAlert, TLSChangeCipherSpec, \ TLSApplicationData +from scapy.layers.tls.crypto.suites import _tls_cipher_suites +from scapy.layers.tls.crypto.groups import _tls_named_groups from scapy.modules import six from scapy.packet import Raw from scapy.compat import bytes_encode @@ -68,6 +71,8 @@ def parse_args(self, server="127.0.0.1", dport=4433, server_name=None, mycert=None, mykey=None, client_hello=None, version=None, data=None, + ciphersuite=None, + curve=None, **kargs): super(TLSClientAutomaton, self).parse_args(mycert=mycert, @@ -94,7 +99,11 @@ def parse_args(self, server="127.0.0.1", dport=4433, server_name=None, self.local_port = None self.socket = None - self.client_hello = client_hello + if (isinstance(client_hello, TLSClientHello) or + isinstance(client_hello, TLS13ClientHello)): + self.client_hello = client_hello + else: + self.client_hello = None self.advertised_tls_version = None if version: v = _tls_version_options.get(version, None) @@ -112,6 +121,28 @@ def parse_args(self, server="127.0.0.1", dport=4433, server_name=None, self.data_to_send = list(bytes_encode(d) for d in reversed(data)) else: self.data_to_send = [] + self.curve = None + + if self.advertised_tls_version == 0x0304: + self.ciphersuite = 0x1301 + if ciphersuite is not None: + cs = int(ciphersuite, 16) + if cs in _tls_cipher_suites.keys(): + self.ciphersuite = cs + if conf.crypto_valid_advanced: + # Default to x25519 if supported + self.curve = 29 + else: + # Or secp256r1 otherwise + self.curve = 23 + if curve is not None: + for (group_id, ng) in _tls_named_groups.items(): + if ng == curve: + if curve == "x25519": + if conf.crypto_valid_advanced: + self.curve = group_id + else: + self.curve = group_id def vprint_sessioninfo(self): if self.verbose: @@ -172,7 +203,14 @@ def PREPARE_CLIENTFLIGHT1(self): @ATMT.condition(PREPARE_CLIENTFLIGHT1) def should_add_ClientHello(self): - self.add_msg(self.client_hello or TLSClientHello()) + if self.client_hello: + p = self.client_hello + else: + p = TLSClientHello() + # Add TLS_Ext_SignatureAlgorithms for TLS 1.2 ClientHello + if self.cur_session.advertised_tls_version == 0x0303: + p.ext = TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsa"]) + self.add_msg(p) raise self.ADDED_CLIENTHELLO() @ATMT.state() @@ -820,44 +858,71 @@ def TLS13_START(self): @ATMT.condition(TLS13_START) def tls13_should_add_ClientHello(self): # we have to use the legacy, plaintext TLS record here + supported_groups = ["secp256r1", "secp384r1"] + if conf.crypto_valid_advanced: + supported_groups.append("x25519") self.add_record(is_tls13=False) + ext = [TLS_Ext_SupportedVersion_CH(versions=["TLS 1.3"]), + TLS_Ext_SupportedGroups(groups=supported_groups), + TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=self.curve)]), # noqa: E501 + TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss", + "sha256+rsa"])] if self.client_hello: + if not self.client_hello.ext: + self.client_hello.ext = ext p = self.client_hello else: - # When trying to connect to a public TLS 1.3 server, - # you will most likely need to provide an SNI extension. - # sn = ServerName(servername="") - ext = [TLS_Ext_SupportedGroups(groups=["secp256r1"]), - # TLS_Ext_ServerName(servernames=[sn]), - TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=23)]), # noqa: E501 - TLS_Ext_SupportedVersions(versions=["TLS 1.3-d18"]), - TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsapss", - "sha256+rsa"])] - p = TLSClientHello(ciphers=0x1301, ext=ext) + p = TLS13ClientHello(ciphers=self.ciphersuite, ext=ext) self.add_msg(p) raise self.TLS13_ADDED_CLIENTHELLO() @ATMT.state() def TLS13_ADDED_CLIENTHELLO(self): + raise self.TLS13_SENDING_CLIENTFLIGHT1() + + @ATMT.state() + def TLS13_SENDING_CLIENTFLIGHT1(self): pass - @ATMT.condition(TLS13_ADDED_CLIENTHELLO) - def tls13_should_send_ClientHello(self): + @ATMT.condition(TLS13_SENDING_CLIENTFLIGHT1) + def tls13_should_send_ClientFlight1(self): self.flush_records() - raise self.TLS13_SENT_CLIENTHELLO() + raise self.TLS13_SENT_CLIENTFLIGHT1() @ATMT.state() - def TLS13_SENT_CLIENTHELLO(self): - raise self.TLS13_WAITING_SERVERHELLO() + def TLS13_SENT_CLIENTFLIGHT1(self): + raise self.TLS13_WAITING_SERVERFLIGHT1() @ATMT.state() - def TLS13_WAITING_SERVERHELLO(self): + def TLS13_WAITING_SERVERFLIGHT1(self): self.get_next_msg() + raise self.TLS13_RECEIVED_SERVERFLIGHT1() + + @ATMT.state() + def TLS13_RECEIVED_SERVERFLIGHT1(self): + pass - @ATMT.condition(TLS13_WAITING_SERVERHELLO) + @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=1) def tls13_should_handle_ServerHello(self): + """ + XXX We should check the ServerHello attributes for discrepancies with + our own ClientHello. + """ self.raise_on_packet(TLS13ServerHello, - self.TLS13_WAITING_ENCRYPTEDEXTENSIONS) + self.TLS13_HANDLED_SERVERHELLO) + + @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=2) + def tls13_should_handle_AlertMessage_(self): + self.raise_on_packet(TLSAlert, + self.CLOSE_NOTIFY) + + @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=3) + def tls13_missing_ServerHello(self): + raise self.MISSING_SERVERHELLO() + + @ATMT.state() + def TLS13_HANDLED_SERVERHELLO(self): + pass @ATMT.state() def TLS13_WAITING_ENCRYPTEDEXTENSIONS(self): @@ -868,55 +933,58 @@ def tls13_should_handle_EncryptedExtensions(self): self.raise_on_packet(TLSEncryptedExtensions, self.TLS13_WAITING_CERTIFICATE) - @ATMT.state() - def TLS13_WAITING_CERTIFICATE(self): - self.get_next_msg() + @ATMT.condition(TLS13_HANDLED_SERVERHELLO, prio=1) + def tls13_should_handle_encrytpedExtensions(self): + self.raise_on_packet(TLSEncryptedExtensions, + self.TLS13_HANDLED_ENCRYPTEDEXTENSIONS) - @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=1) - def tls13_should_handle_Certificate(self): - self.raise_on_packet(TLS13Certificate, - self.TLS13_WAITING_CERTIFICATEVERIFY) + @ATMT.condition(TLS13_HANDLED_SERVERHELLO, prio=2) + def tls13_missing_encryptedExtension(self): + self.vprint("Missing TLS 1.3 EncryptedExtensions message!") + raise self.CLOSE_NOTIFY() - @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=2) - def tls13_should_handle_CertificateRequest(self): - hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] - if TLSCertificateRequest in hs_msg: - self.vprint("TLSCertificateRequest already received!") - self.raise_on_packet(TLSCertificateRequest, - self.TLS13_WAITING_CERTIFICATE) + @ATMT.state() + def TLS13_HANDLED_ENCRYPTEDEXTENSIONS(self): + pass - @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=3) - def tls13_should_handle_ServerFinished_from_EncryptedExtensions(self): - self.raise_on_packet(TLSFinished, - self.TLS13_CONNECTED) + @ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=2) + def tls13_should_handle_certificate_from_encryptedExtensions(self): + self.tls13_should_handle_Certificate() - @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=4) - def tls13_missing_Certificate(self): - self.vprint("Missing TLS 1.3 message after EncryptedExtensions!") - raise self.FINAL() + def tls13_should_handle_Certificate(self): + self.raise_on_packet(TLS13Certificate, + self.TLS13_HANDLED_CERTIFICATE) @ATMT.state() - def TLS13_WAITING_CERTIFICATEVERIFY(self): - self.get_next_msg() + def TLS13_HANDLED_CERTIFICATE(self): + pass - @ATMT.condition(TLS13_WAITING_CERTIFICATEVERIFY) + @ATMT.condition(TLS13_HANDLED_CERTIFICATE, prio=1) def tls13_should_handle_CertificateVerify(self): self.raise_on_packet(TLSCertificateVerify, - self.TLS13_WAITING_SERVERFINISHED) + self.TLS13_HANDLED_CERTIFICATE_VERIFY) + + @ATMT.condition(TLS13_HANDLED_CERTIFICATE, prio=2) + def tls13_missing_CertificateVerify(self): + self.vprint("Missing TLS 1.3 CertificateVerify message!") + raise self.CLOSE_NOTIFY() @ATMT.state() - def TLS13_WAITING_SERVERFINISHED(self): - self.get_next_msg() + def TLS13_HANDLED_CERTIFICATE_VERIFY(self): + pass - @ATMT.condition(TLS13_WAITING_SERVERFINISHED) - def tls13_should_handle_ServerFinished_from_CertificateVerify(self): + @ATMT.condition(TLS13_HANDLED_CERTIFICATE_VERIFY, prio=1) + def tls13_should_handle_finished(self): self.raise_on_packet(TLSFinished, - self.TLS13_PREPARE_CLIENTFLIGHT2) + self.TLS13_HANDLED_FINISHED) + + @ATMT.state() + def TLS13_HANDLED_FINISHED(self): + raise self.TLS13_PREPARE_CLIENTFLIGHT2() @ATMT.state() def TLS13_PREPARE_CLIENTFLIGHT2(self): self.add_record(is_tls13=True) - # raise self.FINAL() @ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2) def tls13_should_add_ClientFinished(self): @@ -934,7 +1002,10 @@ def tls13_should_send_ClientFlight2(self): @ATMT.state() def TLS13_SENT_CLIENTFLIGHT2(self): - raise self.HANDLED_SERVERFINISHED() + self.vprint("TLS 1.3 handshake completed!") + self.vprint_sessioninfo() + self.vprint("You may send data or use 'quit'.") + raise self.WAIT_CLIENTDATA() @ATMT.state(final=True) def FINAL(self): diff --git a/scapy/layers/tls/automaton_srv.py b/scapy/layers/tls/automaton_srv.py index 27fc5af4144..656c5ef1a2c 100644 --- a/scapy/layers/tls/automaton_srv.py +++ b/scapy/layers/tls/automaton_srv.py @@ -27,9 +27,14 @@ from scapy.layers.tls.cert import PrivKeyRSA, PrivKeyECDSA from scapy.layers.tls.basefields import _tls_version from scapy.layers.tls.session import tlsSession +from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH +from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH, \ + KeyShareEntry from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \ TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, TLSFinished, \ - TLSServerHello, TLSServerHelloDone, TLSServerKeyExchange + TLSServerHello, TLSServerHelloDone, TLSServerKeyExchange, \ + _ASN1CertAndExt, TLS13ServerHello, TLS13Certificate, TLS13ClientHello, \ + TLSEncryptedExtensions from scapy.layers.tls.handshake_sslv2 import SSLv2ClientCertificate, \ SSLv2ClientFinished, SSLv2ClientHello, SSLv2ClientMasterKey, \ SSLv2RequestCertificate, SSLv2ServerFinished, SSLv2ServerHello, \ @@ -103,10 +108,18 @@ def vprint_sessioninfo(self): self.vprint("Version : %s" % v) cs = s.wcs.ciphersuite.name self.vprint("Cipher suite : %s" % cs) - ms = s.master_secret + if s.tls_version < 0x0304: + ms = s.master_secret + else: + ms = s.tls13_master_secret self.vprint("Master secret : %s" % repr_hex(ms)) if s.client_certs: self.vprint("Client certificate chain: %r" % s.client_certs) + + if s.tls_version >= 0x0304: + res_secret = s.tls13_derived_secrets["resumption_secret"] + self.vprint("Resumption master secret : %s" % + repr_hex(res_secret)) self.vprint() def http_sessioninfo(self): @@ -200,18 +213,18 @@ def RECEIVED_CLIENTFLIGHT1(self): pass # TLS handshake # - @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=1) + def tls13_should_handle_ClientHello(self): + self.raise_on_packet(TLS13ClientHello, + self.tls13_HANDLED_CLIENTHELLO) + + @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=2) def should_handle_ClientHello(self): self.raise_on_packet(TLSClientHello, self.HANDLED_CLIENTHELLO) @ATMT.state() def HANDLED_CLIENTHELLO(self): - raise self.PREPARE_SERVERFLIGHT1() - - @ATMT.condition(HANDLED_CLIENTHELLO) - def should_check_ciphersuites(self): """ We extract cipher suites candidates from the client's proposition. """ @@ -220,7 +233,7 @@ def should_check_ciphersuites(self): elif isinstance(self.mykey, PrivKeyECDSA): kx = "ECDSA" if get_usable_ciphersuites(self.cur_pkt.ciphers, kx): - return + raise self.PREPARE_SERVERFLIGHT1() raise self.NO_USABLE_CIPHERSUITE() @ATMT.state() @@ -482,6 +495,116 @@ def SENT_SERVERFLIGHT2(self): # end of TLS handshake # + # TLS 1.3 handshake # + @ATMT.state() + def tls13_HANDLED_CLIENTHELLO(self): + raise self.tls13_PREPARE_SERVERFLIGHT1() + + @ATMT.state() + def tls13_PREPARE_SERVERFLIGHT1(self): + self.add_record(is_tls13=False) + + @ATMT.condition(tls13_PREPARE_SERVERFLIGHT1) + def tls13_should_add_ServerHello(self): + if isinstance(self.mykey, PrivKeyRSA): + kx = "RSA" + elif isinstance(self.mykey, PrivKeyECDSA): + kx = "ECDSA" + usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx) + c = usable_suites[0] + group = next(iter(self.cur_session.tls13_client_pubshares)) + ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3")] + ext += TLS_Ext_KeyShare_SH(server_share=KeyShareEntry(group=group)) + + if self.cur_session.sid is not None: + p = TLS13ServerHello(cipher=c, sid=self.cur_session.sid, ext=ext) + else: + p = TLS13ServerHello(cipher=c, ext=ext) + self.add_msg(p) + raise self.tls13_ADDED_SERVERHELLO() + + @ATMT.state() + def tls13_ADDED_SERVERHELLO(self): + pass + + @ATMT.condition(tls13_ADDED_SERVERHELLO) + def tls13_should_add_EncryptedExtensions(self): + self.add_record(is_tls13=True) + self.add_msg(TLSEncryptedExtensions(extlen=0)) + raise self.tls13_ADDED_ENCRYPTEDEXTENSIONS() + + @ATMT.state() + def tls13_ADDED_ENCRYPTEDEXTENSIONS(self): + pass + + @ATMT.condition(tls13_ADDED_ENCRYPTEDEXTENSIONS) + def tls13_should_add_CertificateRequest(self): + raise self.tls13_ADDED_CERTIFICATEREQUEST() + + @ATMT.state() + def tls13_ADDED_CERTIFICATEREQUEST(self): + pass + + @ATMT.condition(tls13_ADDED_CERTIFICATEREQUEST) + def tls13_should_add_Certificate(self): + certs = [] + for c in self.cur_session.server_certs: + certs += _ASN1CertAndExt(cert=c) + + self.add_msg(TLS13Certificate(certs=certs)) + raise self.tls13_ADDED_CERTIFICATE() + + @ATMT.state() + def tls13_ADDED_CERTIFICATE(self): + pass + + @ATMT.condition(tls13_ADDED_CERTIFICATE) + def tls13_should_add_CertificateVerifiy(self): + self.add_msg(TLSCertificateVerify()) + raise self.tls13_ADDED_CERTIFICATEVERIFY() + + @ATMT.state() + def tls13_ADDED_CERTIFICATEVERIFY(self): + pass + + @ATMT.condition(tls13_ADDED_CERTIFICATEVERIFY) + def tls13_should_add_Finished(self): + self.add_msg(TLSFinished()) + raise self.tls13_ADDED_SERVERFINISHED() + + @ATMT.state() + def tls13_ADDED_SERVERFINISHED(self): + pass + + @ATMT.condition(tls13_ADDED_SERVERFINISHED) + def tls13_should_send_ServerFlight1(self): + self.flush_records() + raise self.tls13_WAITING_CLIENTFLIGHT2() + + @ATMT.state() + def tls13_WAITING_CLIENTFLIGHT2(self): + self.get_next_msg() + raise self.tls13_RECEIVED_CLIENTFLIGHT2() + + @ATMT.state() + def tls13_RECEIVED_CLIENTFLIGHT2(self): + pass + + @ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=1) + def tls13_should_handle_ClientFinished(self): + self.raise_on_packet(TLSFinished, + self.TLS13_HANDLED_CLIENTFINISHED) + + @ATMT.state() + def TLS13_HANDLED_CLIENTFINISHED(self): + self.vprint("TLS handshake completed!") + self.vprint_sessioninfo() + if self.is_echo_server: + self.vprint("Will now act as a simple echo server.") + raise self.WAITING_CLIENTDATA() + + # end of TLS 1.3 handshake # + @ATMT.state() def WAITING_CLIENTDATA(self): self.get_next_msg(self.max_client_idle_time, 1) diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py index faf35b98b78..a9bfc8b57f4 100644 --- a/scapy/layers/tls/crypto/cipher_aead.py +++ b/scapy/layers/tls/crypto/cipher_aead.py @@ -329,8 +329,7 @@ def auth_encrypt(self, P, A, seq_num): else: if (conf.crypto_valid_advanced and isinstance(self._cipher, AESCCM)): - res = self._cipher.encrypt(self._get_nonce(seq_num), P, A, - tag_length=self.tag_len) + res = self._cipher.encrypt(self._get_nonce(seq_num), P, A) else: res = self._cipher.encrypt(self._get_nonce(seq_num), P, A) return res @@ -360,8 +359,7 @@ def auth_decrypt(self, A, C, seq_num): try: if (conf.crypto_valid_advanced and isinstance(self._cipher, AESCCM)): - P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A, # noqa: E501 - tag_length=self.tag_len) + P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A) # noqa: E501 else: if (conf.crypto_valid_advanced and isinstance(self, Cipher_CHACHA20_POLY1305)): diff --git a/scapy/layers/tls/crypto/suites.py b/scapy/layers/tls/crypto/suites.py index 2f6eeff6dd1..3644c2f8884 100644 --- a/scapy/layers/tls/crypto/suites.py +++ b/scapy/layers/tls/crypto/suites.py @@ -1312,6 +1312,8 @@ def get_usable_ciphersuites(l, kx): if ciph.usable: # XXX select among RSA and ECDSA cipher suites # according to the key(s) the server was given - if ciph.kx_alg.anonymous or kx in ciph.kx_alg.name: + if (ciph.kx_alg.anonymous or + kx in ciph.kx_alg.name or + ciph.kx_alg.name == "TLS13"): res.append(c) return res diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py index b488d06f48c..24cb6cc1e34 100644 --- a/scapy/layers/tls/extensions.py +++ b/scapy/layers/tls/extensions.py @@ -501,7 +501,7 @@ class TLS_Ext_SessionTicket(TLS_Ext_Unknown): # RFC 5077 class TLS_Ext_KeyShare(TLS_Ext_Unknown): name = "TLS Extension - Key Share (dummy class)" - fields_desc = [ShortEnumField("type", 0x28, _tls_ext), + fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None)] diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 8f6ade476fe..3d5e562b9a2 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -305,7 +305,7 @@ def tls_session_update(self, msg_str): s.advertised_sig_algs = e.sig_algs -class TLS13ClientHello(TLSClientHello): +class TLS13ClientHello(_TLSHandshake): """ TLS 1.3 ClientHello, with abilities to handle extensions. @@ -595,9 +595,9 @@ def post_build_tls_session_update(self, msg_str): # EndOfEarlyData message if connection_end == "server": if not early_data_accepted: - s.prcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite), - connection_end=connection_end, - tls_version=s.tls_version) + s.prcs = readConnState(ciphersuite=type(s.wcs.ciphersuite), + connection_end=connection_end, + tls_version=s.tls_version) s.triggered_prcs_commit = True chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501 diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py index 5c0709ddb13..337b1b1d97e 100644 --- a/scapy/layers/tls/keyexchange.py +++ b/scapy/layers/tls/keyexchange.py @@ -49,12 +49,10 @@ 0x0502: "sha384+dsa", 0x0503: "sha384+ecdsa", 0x0600: "sha512+anon", 0x0601: "sha512+rsa", 0x0602: "sha512+dsa", 0x0603: "sha512+ecdsa", - 0x0804: "sha256+rsaepss", - 0x0805: "sha384+rsaepss", - 0x0806: "sha512+rsaepss", - 0x0807: "ed25519", - 0x0808: "ed448", - 0x0809: "sha256+rsapss"} + 0x0804: "sha256+rsaepss", 0x0805: "sha384+rsaepss", + 0x0806: "sha512+rsaepss", 0x0807: "ed25519", + 0x0808: "ed448", 0x0809: "sha256+rsapss", + 0x080a: "sha384+rsapss", 0x080b: "sha512+rsapss"} def phantom_mode(pkt): @@ -165,9 +163,13 @@ class _TLSSignature(_GenericTLSSessionInheritance): def __init__(self, *args, **kargs): super(_TLSSignature, self).__init__(*args, **kargs) if (self.tls_session and - self.tls_session.tls_version and - self.tls_session.tls_version < 0x0303): - self.sig_alg = None + self.tls_session.tls_version): + if self.tls_session.tls_version < 0x0303: + self.sig_alg = None + elif self.tls_session.tls_version == 0x0304: + # For TLS 1.3 signatures, set the signature + # algorithm to RSA-PSS + self.sig_alg = 0x0804 def _update_sig(self, m, key): """ diff --git a/scapy/layers/tls/keyexchange_tls13.py b/scapy/layers/tls/keyexchange_tls13.py index c464127cf05..fb0e9304498 100644 --- a/scapy/layers/tls/keyexchange_tls13.py +++ b/scapy/layers/tls/keyexchange_tls13.py @@ -285,7 +285,7 @@ class PSKBinderEntry(Packet): class TLS_Ext_PreSharedKey_CH(TLS_Ext_Unknown): # XXX define post_build and post_dissection methods name = "TLS Extension - Pre Shared Key (for ClientHello)" - fields_desc = [ShortEnumField("type", 0x28, _tls_ext), + fields_desc = [ShortEnumField("type", 0x29, _tls_ext), ShortField("len", None), FieldLenField("identities_len", None, length_of="identities"), diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py index b04722d7899..10e5f277193 100644 --- a/scapy/layers/tls/record.py +++ b/scapy/layers/tls/record.py @@ -23,7 +23,8 @@ from scapy.layers.inet import TCP from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake, - _tls13_handshake_cls, TLS13ServerHello) + _tls13_handshake_cls, TLS13ServerHello, + TLS13ClientHello) from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version, _TLSIVField, _TLSMACField, _TLSPadField, _TLSPadLenField, @@ -199,12 +200,17 @@ def addfield(self, pkt, s, val): res = b"" for p in val: res += self.i2m(pkt, p) + + # Add TLS13ClientHello in case of HelloRetryRequest if (isinstance(pkt, _GenericTLSSessionInheritance) and - _tls_version_check(pkt.tls_session.tls_version, 0x0304) and - not isinstance(pkt, TLS13ServerHello)): + _tls_version_check(pkt.tls_session.tls_version, 0x0304) and + not isinstance(pkt.msg[0], TLS13ServerHello) and + not isinstance(pkt.msg[0], TLS13ClientHello)): return s + res + if not pkt.type: pkt.type = 0 + hdr = struct.pack("!B", pkt.type) + s[1:5] return hdr + res @@ -298,7 +304,8 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs): return SSLv2 s = kargs.get("tls_session", None) if s and _tls_version_check(s.tls_version, 0x0304): - if s.rcs and not isinstance(s.rcs.cipher, Cipher_NULL): + if (s.rcs and not isinstance(s.rcs.cipher, Cipher_NULL) and + byte0 == 0x16): from scapy.layers.tls.record_tls13 import TLS13 return TLS13 if plen < 5: diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index 594e2dfdec6..24b2d111b8a 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -418,6 +418,9 @@ def __init__(self, self.pre_master_secret = None self.master_secret = None + # The agreed-upon signature algorithm (for TLS 1.2-TLS 1.3 only) + self.selected_sig_alg = None + # A session ticket received by the client. self.client_session_ticket = None diff --git a/test/tls/example_client.py b/test/tls/example_client.py index 31a1fcefd2b..b3503cf15a3 100755 --- a/test/tls/example_client.py +++ b/test/tls/example_client.py @@ -18,16 +18,23 @@ sys.path=[basedir]+sys.path from scapy.layers.tls.automaton_cli import TLSClientAutomaton -from scapy.layers.tls.handshake import TLSClientHello +from scapy.layers.tls.handshake import TLSClientHello, TLS13ClientHello if len(sys.argv) == 2: - ch = TLSClientHello(ciphers=int(sys.argv[1], 16)) + ciphers = int(sys.argv[1], 16) + if ciphers not in list(range(0x1301, 0x1306)): + ch = TLSClientHello(ciphers=ciphers) + version = "tls12" + else: + ch = TLS13ClientHello(ciphers=ciphers) + version = "tls13" else: ch = None + version = "tls13" t = TLSClientAutomaton(client_hello=ch, - version="tls13-d18", + version=version, mycert=basedir+"/test/tls/pki/cli_cert.pem", mykey=basedir+"/test/tls/pki/cli_key.pem") t.run() diff --git a/test/tls/tests_tls_netaccess.uts b/test/tls/tests_tls_netaccess.uts index 02502a4e730..2b6f6548c59 100644 --- a/test/tls/tests_tls_netaccess.uts +++ b/test/tls/tests_tls_netaccess.uts @@ -158,7 +158,7 @@ test_tls_server("ECDHE-RSA-AES256-GCM-SHA384", "-tls1_2") import sys, os, time, threading from scapy.layers.tls.automaton_cli import TLSClientAutomaton -from scapy.layers.tls.handshake import TLSClientHello +from scapy.layers.tls.handshake import TLSClientHello, TLS13ClientHello from scapy.modules.six.moves.queue import Queue @@ -168,6 +168,9 @@ def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None): print("Loading client...") if version == "0002": t = TLSClientAutomaton(data=[send_data, b"stop_server", b"quit"], version="sslv2", debug=5) + elif version == "0304": + ch = TLS13ClientHello(ciphers=int(cipher_suite_code, 16)) + t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], version="tls13", debug=5) else: ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16)) t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], debug=5) @@ -224,3 +227,16 @@ test_tls_client("009e", "0303") test_tls_client("c016", "0303") += Testing TLS server and client with TLS 1.3 and TLS_AES_128_GCM_SHA256 + +test_tls_client("1301", "0304") + += Testing TLS server and client with TLS 1.3 and TLS_CHACHA20_POLY1305_SHA256 +~ crypto_advanced + +test_tls_client("1303", "0304") + += Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 +~ crypto_advanced + +test_tls_client("1305", "0304")