From 2ffe87a223c67e548aa6ae1b6f5a59dc6a406aaa Mon Sep 17 00:00:00 2001 From: rperez Date: Fri, 12 Jul 2019 17:43:07 +0200 Subject: [PATCH 1/9] Basic TLS 1.3 handshake functionalities --- scapy/layers/tls/cert.py | 5 +- scapy/layers/tls/crypto/cipher_aead.py | 16 +- scapy/layers/tls/crypto/hkdf.py | 4 +- scapy/layers/tls/extensions.py | 82 ++- scapy/layers/tls/handshake.py | 296 +++++++-- scapy/layers/tls/keyexchange.py | 21 +- scapy/layers/tls/keyexchange_tls13.py | 26 +- scapy/layers/tls/record.py | 34 +- scapy/layers/tls/record_tls13.py | 23 +- scapy/layers/tls/session.py | 150 +++-- test/tls13.uts | 856 ++++++++++++++++++------- 11 files changed, 1127 insertions(+), 386 deletions(-) diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py index dafca992801..f57c682ba7c 100644 --- a/scapy/layers/tls/cert.py +++ b/scapy/layers/tls/cert.py @@ -673,7 +673,10 @@ def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None): return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None): - return self.pubKey.verify(msg, sig, t=t, h=h, mgf=mgf, L=L) + if isinstance(self.pubKey, PubKeyECDSA): + return self.pubKey.verify(msg, sig) + else: + return self.pubKey.verify(msg, sig, t=t, h=h, mgf=mgf, L=L) def remainingDays(self, now=None): """ diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py index 99b77712e0c..8ec0caaeff6 100644 --- a/scapy/layers/tls/crypto/cipher_aead.py +++ b/scapy/layers/tls/crypto/cipher_aead.py @@ -286,7 +286,11 @@ def __init__(self, key=None, fixed_iv=None, nonce_explicit=None): self.pc_cls_mode(fixed_iv), backend=default_backend()) else: - self._cipher = self.cipher_cls(key) + if self.cipher_cls == ChaCha20Poly1305: + # ChaCha20Poly1305 doesn't have a tag_length argument... + self._cipher = self.cipher_cls(key) + else: + self._cipher = self.cipher_cls(key, tag_length=self.tag_len) def __setattr__(self, name, val): if name == "key": @@ -326,8 +330,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 @@ -346,20 +349,18 @@ def auth_decrypt(self, A, C, seq_num): if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce(seq_num) - self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: - decryptor.finalize() + decryptor.finalize_with_tag(mac) except InvalidTag: raise AEADTagError(P, mac) else: 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)): @@ -412,6 +413,7 @@ class Cipher_AES_128_CCM_TLS13(_AEADCipher_TLS13): cipher_cls = AESCCM key_len = 16 tag_len = 16 + fixed_iv_len = 12 class Cipher_AES_128_CCM_8_TLS13(Cipher_AES_128_CCM_TLS13): tag_len = 8 diff --git a/scapy/layers/tls/crypto/hkdf.py b/scapy/layers/tls/crypto/hkdf.py index 5cad7823e39..f9c69c49312 100644 --- a/scapy/layers/tls/crypto/hkdf.py +++ b/scapy/layers/tls/crypto/hkdf.py @@ -36,8 +36,8 @@ def expand(self, prk, info, L): def expand_label(self, secret, label, hash_value, length): hkdf_label = struct.pack("!H", length) - hkdf_label += struct.pack("B", 9 + len(label)) - hkdf_label += b"TLS 1.3, " + hkdf_label += struct.pack("B", 6 + len(label)) + hkdf_label += b"tls13 " hkdf_label += label hkdf_label += struct.pack("B", len(hash_value)) hkdf_label += hash_value diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py index ea56a5e8bd0..25c22a2dfee 100644 --- a/scapy/layers/tls/extensions.py +++ b/scapy/layers/tls/extensions.py @@ -42,19 +42,25 @@ 0x0f: "heartbeat", # RFC 6520 0x10: "alpn", # RFC 7301 0x12: "signed_certificate_timestamp", # RFC 6962 + 0x13: "client_certificate_type", # RFC 7250 + 0x14: "server_certificate_type", # RFC 7250 0x15: "padding", # RFC 7685 0x16: "encrypt_then_mac", # RFC 7366 0x17: "extended_master_secret", # RFC 7627 + 0x1c: "record_size_limit", # RFC 8449 0x23: "session_ticket", # RFC 5077 - 0x28: "key_share", + # 0x28: "key_share", 0x29: "pre_shared_key", - 0x2a: "early_data", + 0x2a: "early_data_indication", 0x2b: "supported_versions", 0x2c: "cookie", 0x2d: "psk_key_exchange_modes", - 0x2e: "ticket_early_data_info", + # 0x2e: "ticket_early_data_info", 0x2f: "certificate_authorities", 0x30: "oid_filters", + 0x31: "post_handshake_auth", + 0x32: "signature_algorithms_cert", + 0x33: "key_share", 0x3374: "next_protocol_negotiation", # RFC-draft-agl-tls-nextprotoneg-03 0xff01: "renegotiation_info" # RFC 5746 @@ -507,14 +513,20 @@ class TLS_Ext_PreSharedKey(TLS_Ext_Unknown): ShortField("len", None)] -class TLS_Ext_EarlyData(TLS_Ext_Unknown): +class TLS_Ext_EarlyDataIndication(TLS_Ext_Unknown): name = "TLS Extension - Early Data" fields_desc = [ShortEnumField("type", 0x2a, _tls_ext), ShortField("len", None)] class TLS_Ext_SupportedVersions(TLS_Ext_Unknown): - name = "TLS Extension - Supported Versions" + name = "TLS Extension - Supported Versions (dummy class)" + fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), + ShortField("len", None)] + + +class TLS_Ext_SupportedVersion_CH(TLS_Ext_Unknown): + name = "TLS Extension - Supported Versions (for ClientHello)" fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), ShortField("len", None), FieldLenField("versionslen", None, fmt='B', @@ -525,6 +537,22 @@ class TLS_Ext_SupportedVersions(TLS_Ext_Unknown): length_from=lambda pkt: pkt.versionslen)] +class TLS_Ext_SupportedVersion_SH(TLS_Ext_Unknown): + name = "TLS Extension - Supported Versions (for ServerHello)" + fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), + ShortField("len", None), + ShortEnumField("version", None, _tls_version)] + + def post_dissection(self, r): + if isinstance(r, TLS_Ext_SupportedVersion_SH): + self.tls_session.tls_version = r.version + return super(TLS_Ext_SupportedVersion_SH, self).post_dissection(r) + + +_tls_ext_supported_version_cls = {1: TLS_Ext_SupportedVersion_CH, + 2: TLS_Ext_SupportedVersion_SH} + + class TLS_Ext_Cookie(TLS_Ext_Unknown): name = "TLS Extension - Cookie" fields_desc = [ShortEnumField("type", 0x2c, _tls_ext), @@ -567,6 +595,24 @@ class TLS_Ext_NPN(TLS_Ext_PrettyPacketList): length_from=lambda pkt:pkt.len)] +class TLS_Ext_PostHandshakeAuth(TLS_Ext_Unknown): # RFC 8446 + name = "TLS Extension - Post Handshake Auth" + fields_desc = [ShortEnumField("type", 0x31, _tls_ext), + ShortField("len", None)] + + +class TLS_Ext_SignatureAlgorithmsCert(TLS_Ext_Unknown): # RFC 8446 + name = "TLS Extension - Signature Algorithms Cert" + fields_desc = [ShortEnumField("type", 0x31, _tls_ext), + ShortField("len", None), + SigAndHashAlgsLenField("sig_algs_len", None, + length_of="sig_algs"), + SigAndHashAlgsField("sig_algs", [], + EnumField("hash_sig", None, + _tls_hash_sig), + length_from=lambda pkt: pkt.sig_algs_len)] # noqa: E501 + + class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown): # RFC 5746 name = "TLS Extension - Renegotiation Indication" fields_desc = [ShortEnumField("type", 0xff01, _tls_ext), @@ -577,6 +623,13 @@ class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown): # RFC 5746 length_from=lambda pkt: pkt.reneg_conn_len)] +class TLS_Ext_RecordSizeLimit(TLS_Ext_Unknown): # RFC 8449 + name = "TLS Extension - Record Size Limit" + fields_desc = [ShortEnumField("type", 0x1c, _tls_ext), + ShortField("len", None), + ShortField("record_size_limit", None)] + + _tls_ext_cls = {0: TLS_Ext_ServerName, 1: TLS_Ext_MaxFragLen, 2: TLS_Ext_ClientCertURL, @@ -596,14 +649,18 @@ class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown): # RFC 5746 0x15: TLS_Ext_Padding, 0x16: TLS_Ext_EncryptThenMAC, 0x17: TLS_Ext_ExtendedMasterSecret, + 0x1c: TLS_Ext_RecordSizeLimit, 0x23: TLS_Ext_SessionTicket, - 0x28: TLS_Ext_KeyShare, + # 0x28: TLS_Ext_KeyShare, 0x29: TLS_Ext_PreSharedKey, - 0x2a: TLS_Ext_EarlyData, + 0x2a: TLS_Ext_EarlyDataIndication, 0x2b: TLS_Ext_SupportedVersions, 0x2c: TLS_Ext_Cookie, 0x2d: TLS_Ext_PSKKeyExchangeModes, - 0x2e: TLS_Ext_TicketEarlyDataInfo, + # 0x2e: TLS_Ext_TicketEarlyDataInfo, + 0x31: TLS_Ext_PostHandshakeAuth, + 0x32: TLS_Ext_SignatureAlgorithmsCert, + 0x33: TLS_Ext_KeyShare, # 0x2f: TLS_Ext_CertificateAuthorities, #XXX # 0x30: TLS_Ext_OIDFilters, #XXX 0x3374: TLS_Ext_NPN, @@ -689,11 +746,20 @@ def m2i(self, pkt, m): tmp_len = struct.unpack("!H", m[2:4])[0] cls = _tls_ext_cls.get(t, TLS_Ext_Unknown) if cls is TLS_Ext_KeyShare: + # TLS_Ext_KeyShare can be : + # - TLS_Ext_KeyShare_CH if the message is a ClientHello + # - TLS_Ext_KeyShare_SH if the message is a ServerHello + # and all parameters are accepted by the serveur + # - TLS_Ext_KeyShare_HRR if message is a ServerHello and + # the client has not provided a sufficient "key_share" + # extension from scapy.layers.tls.keyexchange_tls13 import _tls_ext_keyshare_cls # noqa: E501 cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown) elif cls is TLS_Ext_PreSharedKey: from scapy.layers.tls.keyexchange_tls13 import _tls_ext_presharedkey_cls # noqa: E501 cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 + elif cls is TLS_Ext_SupportedVersions: + cls = _tls_ext_supported_version_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 res.append(cls(m[:tmp_len + 4], tls_session=pkt.tls_session)) m = m[tmp_len + 4:] return res diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 79c0650e120..ad64d66f5ac 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -19,7 +19,7 @@ FieldLenField, IntField, PacketField, PacketListField, ShortField, \ StrFixedLenField, StrLenField, ThreeBytesField, UTCTimeField -from scapy.compat import bytes_hex, orb, raw +from scapy.compat import hex_bytes, orb, raw from scapy.config import conf from scapy.modules import six from scapy.packet import Packet, Raw, Padding @@ -29,7 +29,10 @@ from scapy.layers.tls.basefields import (_tls_version, _TLSVersionField, _TLSClientVersionField) from scapy.layers.tls.extensions import (_ExtensionsLenField, _ExtensionsField, - _cert_status_type, TLS_Ext_SupportedVersions) # noqa: E501 + _cert_status_type, TLS_Ext_SupportedVersion_CH, # noqa: E501 + TLS_Ext_SignatureAlgorithms, + TLS_Ext_SupportedVersion_SH, + TLS_Ext_EarlyDataIndication) from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField, _TLSSignatureField, ServerRSAParams, SigAndHashAlgsField, _tls_hash_sig, @@ -235,7 +238,7 @@ class TLSClientHello(_TLSHandshake): FieldLenField("sidlen", None, fmt="B", length_of="sid"), _SessionIDField("sid", "", - length_from=lambda pkt:pkt.sidlen), + length_from=lambda pkt: pkt.sidlen), FieldLenField("cipherslen", None, fmt="!H", length_of="ciphers"), @@ -266,7 +269,7 @@ def post_build(self, p, pay): if self.ciphers is None: cipherstart = 39 + (self.sidlen or 0) s = b"001ac02bc023c02fc027009e0067009c003cc009c0130033002f000a" - p = p[:cipherstart] + bytes_hex(s) + p[cipherstart + 2:] + p = p[:cipherstart] + hex_bytes(s) + p[cipherstart + 2:] if self.ext is None: ext_len = b'\x00\x2c' ext_reneg = b'\xff\x01\x00\x01\x00' @@ -284,25 +287,96 @@ def tls_session_update(self, msg_str): """ super(TLSClientHello, self).tls_session_update(msg_str) - self.tls_session.advertised_tls_version = self.version + s = self.tls_session + s.advertised_tls_version = self.version self.random_bytes = msg_str[10:38] - self.tls_session.client_random = (struct.pack('!I', - self.gmt_unix_time) + - self.random_bytes) + s.client_random = (struct.pack('!I', self.gmt_unix_time) + + self.random_bytes) + + # No distinction between a TLS 1.2 ClientHello and a TLS + # 1.3 ClientHello when dissecting : TLS 1.3 CH will be + # parsed as TLSClientHello if self.ext: for e in self.ext: - if isinstance(e, TLS_Ext_SupportedVersions): - if self.tls_session.tls13_early_secret is None: - # this is not recomputed if there was a TLS 1.3 HRR - self.tls_session.compute_tls13_early_secrets() - break + if isinstance(e, TLS_Ext_SupportedVersion_CH): + s.advertised_tls_version = e.versions[0] + + if isinstance(e, TLS_Ext_SignatureAlgorithms): + s.advertised_sig_algs = e.sig_algs + + +class TLS13ClientHello(_TLSHandshake): + """ + TLS ClientHello, with abilities to handle extensions. + + The Random structure follows the RFC 5246: while it is 32-byte long, + many implementations use the first 4 bytes as a gmt_unix_time, and then + the remaining 28 byts should be completely random. This was designed in + order to (sort of) mitigate broken RNGs. If you prefer to show the full + 32 random bytes without any GMT time, just comment in/out the lines below. + """ + name = "TLS 1.3 Handshake - Client Hello" + fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type), + ThreeBytesField("msglen", None), + _TLSClientVersionField("version", None, _tls_version), + + _TLSRandomBytesField("random_bytes", None, 32), + + FieldLenField("sidlen", None, fmt="B", length_of="sid"), + _SessionIDField("sid", "", + length_from=lambda pkt: pkt.sidlen), + + FieldLenField("cipherslen", None, fmt="!H", + length_of="ciphers"), + _CipherSuitesField("ciphers", None, + _tls_cipher_suites, itemfmt="!H", + length_from=lambda pkt: pkt.cipherslen), + + FieldLenField("complen", None, fmt="B", length_of="comp"), + _CompressionMethodsField("comp", [0], + _tls_compression_algs, + itemfmt="B", + length_from=lambda pkt: pkt.complen), # noqa: E501 + + _ExtensionsLenField("extlen", None, length_of="ext"), + _ExtensionsField("ext", None, + length_from=lambda pkt: (pkt.msglen - + (pkt.sidlen or 0) - # noqa: E501 + (pkt.cipherslen or 0) - # noqa: E501 + (pkt.complen or 0) - # noqa: E501 + 40))] + + def post_build(self, p, pay): + if self.random_bytes is None: + p = p[:6] + randstring(32) + p[6 + 32:] + return super(TLS13ClientHello, self).post_build(p, pay) + + def tls_session_update(self, msg_str): + """ + Either for parsing or building, we store the client_random + along with the raw string representing this handshake message. + """ + super(TLS13ClientHello, self).tls_session_update(msg_str) + s = self.tls_session + + if self.sidlen and self.sidlen > 0: + s.sid = self.sid + self.random_bytes = msg_str[10:38] + s.client_random = self.random_bytes + if self.ext: + for e in self.ext: + if isinstance(e, TLS_Ext_SupportedVersion_CH): + self.tls_session.advertised_tls_version = e.versions[0] + if isinstance(e, TLS_Ext_SignatureAlgorithms): + s.advertised_sig_algs = e.sig_algs + ############################################################################### # ServerHello # ############################################################################### -class TLSServerHello(TLSClientHello): +class TLSServerHello(_TLSHandshake): """ TLS ServerHello, with abilities to handle extensions. @@ -399,19 +473,31 @@ def tls_session_update(self, msg_str): tls_version=self.version) -class TLS13ServerHello(TLSClientHello): +class TLS13ServerHello(_TLSHandshake): """ TLS 1.3 ServerHello """ name = "TLS 1.3 Handshake - Server Hello" fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), ThreeBytesField("msglen", None), - _TLSVersionField("version", None, _tls_version), + _TLSVersionField("version", 0x0303, _tls_version), _TLSRandomBytesField("random_bytes", None, 32), + FieldLenField("sidlen", None, length_of="sid", fmt="B"), + _SessionIDField("sid", "", + length_from=lambda pkt: pkt.sidlen), EnumField("cipher", None, _tls_cipher_suites), + _CompressionMethodsField("comp", [0], + _tls_compression_algs, + itemfmt="B", + length_from=lambda pkt: 1), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - 38))] + def post_build(self, pkt, pay): + if self.random_bytes is None: + pkt = pkt[:6] + randstring(32) + pkt[6 + 32:] + return super(TLS13ServerHello, self).post_build(pkt, pay) + def tls_session_update(self, msg_str): """ Either for parsing or building, we store the server_random along with @@ -419,11 +505,16 @@ def tls_session_update(self, msg_str): cipher suite (if recognized), and finally we instantiate the write and read connection states. """ - super(TLSClientHello, self).tls_session_update(msg_str) + super(TLS13ServerHello, self).tls_session_update(msg_str) s = self.tls_session - s.tls_version = self.version + if self.ext: + for e in self.ext: + if isinstance(e, TLS_Ext_SupportedVersion_SH): + s.tls_version = e.version + break s.server_random = self.random_bytes + s.ciphersuite = self.cipher cs_cls = None if self.cipher: @@ -435,40 +526,57 @@ def tls_session_update(self, msg_str): cs_cls = _tls_cipher_suites_cls[cs_val] connection_end = s.connection_end - s.pwcs = writeConnState(ciphersuite=cs_cls, - connection_end=connection_end, - tls_version=self.version) - s.triggered_pwcs_commit = True - s.prcs = readConnState(ciphersuite=cs_cls, - connection_end=connection_end, - tls_version=self.version) - s.triggered_prcs_commit = True - - if self.tls_session.tls13_early_secret is None: - # In case the connState was not pre-initialized, we could not - # compute the early secrets at the ClientHello, so we do it here. - self.tls_session.compute_tls13_early_secrets() + + if connection_end == "server": + s.pwcs = writeConnState(ciphersuite=cs_cls, + connection_end=connection_end, + tls_version=s.tls_version) + s.triggered_pwcs_commit = True + elif connection_end == "client": + s.prcs = readConnState(ciphersuite=cs_cls, + connection_end=connection_end, + tls_version=s.tls_version) + s.triggered_prcs_commit = True + s.compute_tls13_handshake_secrets() + if connection_end == "server": + shts = s.tls13_derived_secrets["server_handshake_traffic_secret"] + s.pwcs.tls13_derive_keys(shts) + elif connection_end == "client": + shts = s.tls13_derived_secrets["server_handshake_traffic_secret"] + s.prcs.tls13_derive_keys(shts) ############################################################################### # HelloRetryRequest # ############################################################################### -class TLSHelloRetryRequest(_TLSHandshake): +class TLS13HelloRetryRequest(_TLSHandshake): name = "TLS 1.3 Handshake - Hello Retry Request" - fields_desc = [ByteEnumField("msgtype", 6, _tls_handshake_type), + + fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), ThreeBytesField("msglen", None), - _TLSVersionField("version", None, _tls_version), + _TLSVersionField("version", 0x0303, _tls_version), + _TLSRandomBytesField("random_bytes", None, 32), + FieldLenField("sidlen", None, length_of="sid", fmt="B"), + _SessionIDField("sid", "", + length_from=lambda pkt: pkt.sidlen), + EnumField("cipher", None, _tls_cipher_suites), + _CompressionMethodsField("comp", [0], + _tls_compression_algs, + itemfmt="B", + length_from=lambda pkt: 1), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, - length_from=lambda pkt: pkt.msglen - 4)] + length_from=lambda pkt: (pkt.msglen - + 38))] ############################################################################### # EncryptedExtensions # ############################################################################### + class TLSEncryptedExtensions(_TLSHandshake): name = "TLS 1.3 Handshake - Encrypted Extensions" fields_desc = [ByteEnumField("msgtype", 8, _tls_handshake_type), @@ -477,18 +585,77 @@ class TLSEncryptedExtensions(_TLSHandshake): _ExtensionsField("ext", None, length_from=lambda pkt: pkt.msglen - 2)] + def post_build_tls_session_update(self, msg_str): + self.tls_session_update(msg_str) + + s = self.tls_session + connection_end = s.connection_end + + # Check if the server early_data extension is present in + # EncryptedExtensions message (if so, early data was accepted by the + # server) + early_data_accepted = False + if self.ext: + for e in self.ext: + if isinstance(e, TLS_Ext_EarlyDataIndication): + early_data_accepted = True + + # If the serveur did not accept early_data, we change prcs traffic + # encryption keys. Otherwise, the the keys will be updated after the + # 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.triggered_prcs_commit = True + chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501 + s.prcs.tls13_derive_keys(chts) + + s.rcs = self.tls_session.prcs + s.triggered_prcs_commit = False + + def post_dissection_tls_session_update(self, msg_str): + self.tls_session_update(msg_str) + s = self.tls_session + connection_end = s.connection_end + + # Check if the server early_data extension is present in + # EncryptedExtensions message (if so, early data was accepted by the + # server) + early_data_accepted = False + if self.ext: + for e in self.ext: + if isinstance(e, TLS_Ext_EarlyDataIndication): + early_data_accepted = True + + # If the serveur did not accept early_data, we change pwcs traffic + # encryption key. Otherwise, the the keys will be updated after the + # EndOfEarlyData message + if connection_end == "client": + if not early_data_accepted: + s.pwcs = writeConnState(ciphersuite=type(s.rcs.ciphersuite), + connection_end=connection_end, + tls_version=s.tls_version) + + s.triggered_pwcs_commit = True + chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501 + s.pwcs.tls13_derive_keys(chts) + + s.wcs = self.tls_session.pwcs + s.triggered_pwcs_commit = False ############################################################################### # Certificate # ############################################################################### # XXX It might be appropriate to rewrite this mess with basic 3-byte FieldLenField. # noqa: E501 + class _ASN1CertLenField(FieldLenField): """ This is mostly a 3-byte FieldLenField. """ - def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x): self.length_of = length_of self.adjust = adjust @@ -843,10 +1010,23 @@ class TLSCertificateRequest(_TLSHandshake): length_from=lambda pkt: pkt.certauthlen)] # noqa: E501 +class TLS13CertificateRequest(_TLSHandshake): + name = "TLS 1.3 Handshake - Certificate Request" + fields_desc = [ByteEnumField("msgtype", 13, _tls_handshake_type), + ThreeBytesField("msglen", None), + FieldLenField("cert_req_ctxt_len", None, fmt="B", + length_of="cert_req_ctxt"), + StrLenField("cert_req_ctxt", "", + length_from=lambda pkt: pkt.cert_req_ctxt_len), + _ExtensionsLenField("extlen", None, length_of="ext"), + _ExtensionsField("ext", None, + length_from=lambda pkt: pkt.msglen - + pkt.cert_req_ctxt_len - 3)] ############################################################################### # ServerHelloDone # ############################################################################### + class TLSServerHelloDone(_TLSHandshake): name = "TLS Handshake - Server Hello Done" fields_desc = [ByteEnumField("msgtype", 14, _tls_handshake_type), @@ -871,9 +1051,9 @@ def build(self, *args, **kargs): m = b"".join(s.handshake_messages) if s.tls_version >= 0x0304: if s.connection_end == "client": - context_string = "TLS 1.3, client CertificateVerify" + context_string = b"TLS 1.3, client CertificateVerify" elif s.connection_end == "server": - context_string = "TLS 1.3, server CertificateVerify" + context_string = b"TLS 1.3, server CertificateVerify" m = b"\x20" * 64 + context_string + b"\x00" + s.wcs.hash.digest(m) # noqa: E501 self.sig = _TLSSignature(tls_session=s) if s.connection_end == "client": @@ -1227,11 +1407,15 @@ class TLS13NewSessionTicket(_TLSHandshake): """ Uncomment the TicketField line for parsing a RFC 5077 ticket. """ - name = "TLS Handshake - New Session Ticket" + name = "TLS 1.3 Handshake - New Session Ticket" fields_desc = [ByteEnumField("msgtype", 4, _tls_handshake_type), ThreeBytesField("msglen", None), IntField("ticket_lifetime", 0xffffffff), IntField("ticket_age_add", 0), + FieldLenField("noncelen", None, fmt="B", + length_of="ticket_nonce"), + StrLenField("ticket_nonce", "", + length_from=lambda pkt: pkt.noncelen), FieldLenField("ticketlen", None, length_of="ticket"), # TicketField("ticket", "", StrLenField("ticket", "", @@ -1240,7 +1424,7 @@ class TLS13NewSessionTicket(_TLSHandshake): _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - (pkt.ticketlen or 0) - # noqa: E501 - 12))] + pkt.noncelen or 0) - 13)] # noqa: E501 def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) @@ -1248,16 +1432,46 @@ def post_dissection_tls_session_update(self, msg_str): self.tls_session.client_session_ticket = self.ticket +############################################################################### +# EndOfEarlyData # +############################################################################### + +class TLS13EndOfEarlyData(_TLSHandshake): + name = "TLS 1.3 Handshake - End Of Early Data" + + fields_desc = [ByteEnumField("msgtype", 5, _tls_handshake_type), + ThreeBytesField("msglen", None)] + + +############################################################################### +# KeyUpdate # +############################################################################### +_key_update_request = {0: "update_not_requested", 1: "update_requested"} + + +class TLS13KeyUpdate(_TLSHandshake): + name = "TLS 1.3 Handshake - Key Update" + fields_desc = [ByteEnumField("msgtype", 24, _tls_handshake_type), + ThreeBytesField("msglen", None), + ByteEnumField("request_update", 0, _key_update_request)] + + ############################################################################### # All handshake messages defined in this module # ############################################################################### _tls_handshake_cls = {0: TLSHelloRequest, 1: TLSClientHello, 2: TLSServerHello, 3: TLSHelloVerifyRequest, - 4: TLSNewSessionTicket, 6: TLSHelloRetryRequest, + 4: TLSNewSessionTicket, 8: TLSEncryptedExtensions, 11: TLSCertificate, 12: TLSServerKeyExchange, 13: TLSCertificateRequest, 14: TLSServerHelloDone, 15: TLSCertificateVerify, 16: TLSClientKeyExchange, 20: TLSFinished, 21: TLSCertificateURL, 22: TLSCertificateStatus, 23: TLSSupplementalData} + +_tls13_handshake_cls = {1: TLS13ClientHello, 2: TLS13ServerHello, + 4: TLS13NewSessionTicket, 5: TLS13EndOfEarlyData, + 8: TLSEncryptedExtensions, 11: TLS13Certificate, + 13: TLS13CertificateRequest, 15: TLSCertificateVerify, + 20: TLSFinished, 24: TLS13KeyUpdate} diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py index 836008e44cc..d6170499a56 100644 --- a/scapy/layers/tls/keyexchange.py +++ b/scapy/layers/tls/keyexchange.py @@ -49,11 +49,12 @@ 0x0502: "sha384+dsa", 0x0503: "sha384+ecdsa", 0x0600: "sha512+anon", 0x0601: "sha512+rsa", 0x0602: "sha512+dsa", 0x0603: "sha512+ecdsa", - 0x0804: "sha256+rsapss", - 0x0805: "sha384+rsapss", - 0x0806: "sha512+rsapss", + 0x0804: "sha256+rsaepss", + 0x0805: "sha384+rsaepss", + 0x0806: "sha512+rsaepss", 0x0807: "ed25519", - 0x0808: "ed448"} + 0x0808: "ed448", + 0x0809: "sha256+rsapss"} def phantom_mode(pkt): @@ -155,7 +156,7 @@ class _TLSSignature(_GenericTLSSessionInheritance): #XXX 'sig_alg' should be set in __init__ depending on the context. """ name = "TLS Digital Signature" - fields_desc = [SigAndHashAlgField("sig_alg", 0x0401, _tls_hash_sig), + fields_desc = [SigAndHashAlgField("sig_alg", 0x0804, _tls_hash_sig), SigLenField("sig_len", None, fmt="!H", length_of="sig_val"), SigValField("sig_val", None, @@ -164,7 +165,7 @@ 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 and self.tls_session.tls_version < 0x0303): self.sig_alg = None @@ -183,6 +184,8 @@ def _update_sig(self, m, key): h, sig = _tls_hash_sig[self.sig_alg].split('+') if sig.endswith('pss'): t = "pss" + elif sig.endswith('ecdsa'): + t = "" else: t = "pkcs" self.sig_val = key.sign(m, t=t, h=h) @@ -195,7 +198,9 @@ def _verify_sig(self, m, cert): if self.sig_val: if self.sig_alg: h, sig = _tls_hash_sig[self.sig_alg].split('+') - if sig.endswith('pss'): + if sig.endswith('ecdsa'): + return cert.verify(m, self.sig_val, t="") + elif sig.endswith('pss'): t = "pss" else: t = "pkcs" @@ -235,7 +240,7 @@ def getfield(self, pkt, s): remain = b"" if conf.padding_layer in i: r = i[conf.padding_layer] - del(r.underlayer.payload) + del r.underlayer.payload remain = r.load return remain, i diff --git a/scapy/layers/tls/keyexchange_tls13.py b/scapy/layers/tls/keyexchange_tls13.py index 951dbeb0090..c464127cf05 100644 --- a/scapy/layers/tls/keyexchange_tls13.py +++ b/scapy/layers/tls/keyexchange_tls13.py @@ -119,8 +119,8 @@ def register_pubkey(self): self.pubkey = import_point(self.key_exchange) elif _tls_named_curves[self.group] != "x448": curve = ec._CURVE_TYPES[_tls_named_curves[self.group]]() - import_point = ec.EllipticCurvePublicNumbers.from_encoded_point - public_numbers = import_point(curve, self.key_exchange) + import_point = ec.EllipticCurvePublicKey.from_encoded_point + public_numbers = import_point(curve, self.key_exchange).public_numbers() # noqa: E501 self.pubkey = public_numbers.public_key(default_backend()) def post_dissection(self, r): @@ -135,7 +135,7 @@ def extract_padding(self, s): class TLS_Ext_KeyShare_CH(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for ClientHello)" - fields_desc = [ShortEnumField("type", 0x28, _tls_ext), + fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), FieldLenField("client_shares_len", None, length_of="client_shares"), @@ -169,14 +169,14 @@ def post_dissection(self, r): class TLS_Ext_KeyShare_HRR(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for HelloRetryRequest)" - fields_desc = [ShortEnumField("type", 0x28, _tls_ext), + fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), ShortEnumField("selected_group", None, _tls_named_groups)] class TLS_Ext_KeyShare_SH(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for ServerHello)" - fields_desc = [ShortEnumField("type", 0x28, _tls_ext), + fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), PacketField("server_share", None, KeyShareEntry)] @@ -207,7 +207,7 @@ def post_dissection(self, r): if not self.tls_session.frozen and self.server_share.pubkey: # if there is a pubkey, we assume the crypto library is ok pubshare = self.tls_session.tls13_server_pubshare - if len(pubshare) > 0: + if pubshare: pkt_info = r.firstlayer().summary() log_runtime.info("TLS: overwriting previous server key share [%s]", pkt_info) # noqa: E501 group_name = _tls_named_groups[self.server_share.group] @@ -224,12 +224,22 @@ def post_dissection(self, r): else: pms = privkey.exchange(ec.ECDH(), pubkey) self.tls_session.tls13_dhe_secret = pms + elif group_name in self.tls_session.tls13_server_privshare: + pubkey = self.tls_session.tls13_client_pubshares[group_name] + privkey = self.tls_session.tls13_server_privshare[group_name] + if group_name in six.itervalues(_tls_named_ffdh_groups): + pms = privkey.exchange(pubkey) + elif group_name in six.itervalues(_tls_named_curves): + if group_name == "x25519": + pms = privkey.exchange(pubkey) + else: + pms = privkey.exchange(ec.ECDH(), pubkey) + self.tls_session.tls13_dhe_secret = pms return super(TLS_Ext_KeyShare_SH, self).post_dissection(r) _tls_ext_keyshare_cls = {1: TLS_Ext_KeyShare_CH, - 2: TLS_Ext_KeyShare_SH, - 6: TLS_Ext_KeyShare_HRR} + 2: TLS_Ext_KeyShare_SH} class Ticket(Packet): diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py index 758c2e8f689..cde4a477910 100644 --- a/scapy/layers/tls/record.py +++ b/scapy/layers/tls/record.py @@ -23,7 +23,7 @@ from scapy.layers.inet import TCP from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake, - TLS13ServerHello) + _tls13_handshake_cls, TLS13ServerHello) from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version, _TLSIVField, _TLSMACField, _TLSPadField, _TLSPadLenField, @@ -89,7 +89,13 @@ def m2i(self, pkt, m): if pkt.type == 22: if len(m) >= 1: msgtype = orb(m[0]) - cls = _tls_handshake_cls.get(msgtype, Raw) + if ((pkt.tls_session.advertised_tls_version == 0x0304) or + (pkt.tls_session.tls_version and + pkt.tls_session.tls_version == 0x0304)): + cls = _tls13_handshake_cls.get(msgtype, Raw) + else: + cls = _tls_handshake_cls.get(msgtype, Raw) + elif pkt.type == 20: cls = TLSChangeCipherSpec elif pkt.type == 21: @@ -145,7 +151,7 @@ def getfield(self, pkt, s): if Padding in p: pad = p[Padding] remain = pad.load - del(pad.underlayer.payload) + del pad.underlayer.payload if len(remain) != 0: raw_msg = raw_msg[:-len(remain)] else: @@ -501,16 +507,18 @@ def post_dissect(self, s): nothing if the prcs was not set, as this probably means that we're working out-of-context (and we need to keep the default rcs). """ - if self.tls_session.triggered_prcs_commit: - if self.tls_session.prcs is not None: - self.tls_session.rcs = self.tls_session.prcs - self.tls_session.prcs = None - self.tls_session.triggered_prcs_commit = False - if self.tls_session.triggered_pwcs_commit: - if self.tls_session.pwcs is not None: - self.tls_session.wcs = self.tls_session.pwcs - self.tls_session.pwcs = None - self.tls_session.triggered_pwcs_commit = False + if (self.tls_session.tls_version and + self.tls_session.tls_version <= 0x0303): + if self.tls_session.triggered_prcs_commit: + if self.tls_session.prcs is not None: + self.tls_session.rcs = self.tls_session.prcs + self.tls_session.prcs = None + self.tls_session.triggered_prcs_commit = False + if self.tls_session.triggered_pwcs_commit: + if self.tls_session.pwcs is not None: + self.tls_session.wcs = self.tls_session.pwcs + self.tls_session.pwcs = None + self.tls_session.triggered_pwcs_commit = False return s def do_dissect_payload(self, s): diff --git a/scapy/layers/tls/record_tls13.py b/scapy/layers/tls/record_tls13.py index 30e770e7d10..4f944c51ec5 100644 --- a/scapy/layers/tls/record_tls13.py +++ b/scapy/layers/tls/record_tls13.py @@ -15,7 +15,7 @@ from scapy.config import conf from scapy.error import log_runtime, warning -from scapy.compat import raw +from scapy.compat import raw, orb from scapy.fields import ByteEnumField, PacketField, XStrField from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.basefields import _TLSVersionField, _tls_version, \ @@ -24,12 +24,14 @@ from scapy.layers.tls.crypto.cipher_aead import AEADTagError from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL from scapy.layers.tls.crypto.common import CipherError +from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp ############################################################################### # TLS Record Protocol # ############################################################################### + class TLSInnerPlaintext(_GenericTLSSessionInheritance): name = "TLS Inner Plaintext" fields_desc = [_TLSMsgListField("msg", []), @@ -90,7 +92,7 @@ class TLS13(_GenericTLSSessionInheritance): __slots__ = ["deciphered_len"] name = "TLS 1.3" fields_desc = [ByteEnumField("type", 0x17, _tls_type), - _TLSVersionField("version", 0x0301, _tls_version), + _TLSVersionField("version", 0x0303, _tls_version), _TLSLengthField("len", None), _TLSInnerPlaintextField("inner", TLSInnerPlaintext()), _TLSMACField("auth_tag", None)] @@ -112,8 +114,11 @@ def _tls_auth_decrypt(self, s): rcs = self.tls_session.rcs read_seq_num = struct.pack("!Q", rcs.seq_num) rcs.seq_num += 1 + add_data = (pkcs_i2osp(self.type, 1) + + pkcs_i2osp(self.version, 2) + + pkcs_i2osp(len(s), 2)) try: - return rcs.cipher.auth_decrypt(b"", s, read_seq_num) + return rcs.cipher.auth_decrypt(add_data, s, read_seq_num) except CipherError as e: return e.args except AEADTagError as e: @@ -128,7 +133,9 @@ def pre_dissect(self, s): if len(s) < 5: raise Exception("Invalid record: header is too short.") - if isinstance(self.tls_session.rcs.cipher, Cipher_NULL): + self.type = orb(s[0]) + if (isinstance(self.tls_session.rcs.cipher, Cipher_NULL) or + self.type == 0x14): self.deciphered_len = None return s else: @@ -176,7 +183,11 @@ def _tls_auth_encrypt(self, s): wcs = self.tls_session.wcs write_seq_num = struct.pack("!Q", wcs.seq_num) wcs.seq_num += 1 - return wcs.cipher.auth_encrypt(s, b"", write_seq_num) + add_data = (pkcs_i2osp(self.type, 1) + + pkcs_i2osp(self.version, 2) + + pkcs_i2osp(len(s) + wcs.cipher.tag_len, 2)) + + return wcs.cipher.auth_encrypt(s, add_data, write_seq_num) def post_build(self, pkt, pay): """ @@ -184,7 +195,7 @@ def post_build(self, pkt, pay): """ # Compute the length of TLSPlaintext fragment hdr, frag = pkt[:5], pkt[5:] - if not isinstance(self.tls_session.rcs.cipher, Cipher_NULL): + if not isinstance(self.tls_session.wcs.cipher, Cipher_NULL): frag = self._tls_auth_encrypt(frag) if self.len is not None: diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index e619aa72718..8fabdd72020 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -431,11 +431,15 @@ def __init__(self, # These attributes should only be used with TLS 1.3 connections. self.tls13_psk_secret = None + self.tls13_psk_mode = None self.tls13_early_secret = None self.tls13_dhe_secret = None self.tls13_handshake_secret = None self.tls13_master_secret = None self.tls13_derived_secrets = {} + self.post_handshake_auth = False + self.tls13_ticket_ciphersuite = None + self.tls13_retry = False # Handshake messages needed for Finished computation/validation. # No record layer headers, no HelloRequests, no ChangeCipherSpecs. @@ -558,98 +562,109 @@ def compute_sslv2_km_and_derive_keys(self): # Secrets management for TLS 1.3 - def compute_tls13_early_secrets(self): + def compute_tls13_early_secrets(self, external=False): """ Ciphers key and IV are updated accordingly for 0-RTT data. self.handshake_messages should be ClientHello only. """ - # we use the prcs rather than the pwcs in a totally arbitrary way - if self.prcs is None: - # too soon - return - - hkdf = self.prcs.hkdf - self.tls13_early_secret = hkdf.extract(None, - self.tls13_psk_secret) + # if no hash algorithm is set, default to SHA-256 + if self.prcs and self.prcs.hkdf: + hkdf = self.prcs.hkdf + elif self.pwcs and self.pwcs.hkdf: + hkdf = self.pwcs.hkdf + else: + hkdf = TLS13_HKDF("sha256") - bk = hkdf.derive_secret(self.tls13_early_secret, - b"external psk binder key", - # "resumption psk binder key", - b"") - self.tls13_derived_secrets["binder_key"] = bk + if self.tls13_early_secret is None: + self.tls13_early_secret = hkdf.extract(None, + self.tls13_psk_secret) + + if "binder_key" not in self.tls13_derived_secrets: + if external: + bk = hkdf.derive_secret(self.tls13_early_secret, + b"ext binder", + b"") + else: + bk = hkdf.derive_secret(self.tls13_early_secret, + b"res binder", + b"") - if len(self.handshake_messages) > 1: - # these secrets are not defined in case of HRR - return + self.tls13_derived_secrets["binder_key"] = bk cets = hkdf.derive_secret(self.tls13_early_secret, - b"client early traffic secret", + b"c e traffic", b"".join(self.handshake_messages)) - self.tls13_derived_secrets["client_early_traffic_secret"] = cets + self.tls13_derived_secrets["client_early_traffic_secret"] = cets ees = hkdf.derive_secret(self.tls13_early_secret, - b"early exporter master secret", + b"e exp master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["early_exporter_secret"] = ees if self.connection_end == "server": - self.prcs.tls13_derive_keys(cets) + if self.prcs: + self.prcs.tls13_derive_keys(cets) elif self.connection_end == "client": - self.pwcs.tls13_derive_keys(cets) + if self.pwcs: + self.pwcs.tls13_derive_keys(cets) def compute_tls13_handshake_secrets(self): """ Ciphers key and IV are updated accordingly for Handshake data. self.handshake_messages should be ClientHello...ServerHello. """ - if self.tls13_early_secret is None: - warning("No early secret. This is abnormal.") + if self.prcs: + hkdf = self.prcs.hkdf + elif self.pwcs: + hkdf = self.pwcs.hkdf + else: + raise - hkdf = self.prcs.hkdf + if self.tls13_early_secret is None: + self.tls13_early_secret = hkdf.extract(None, + self.tls13_psk_secret) - self.tls13_handshake_secret = hkdf.extract(self.tls13_early_secret, - self.tls13_dhe_secret) + secret = hkdf.derive_secret(self.tls13_early_secret, b"derived", b"") + self.tls13_handshake_secret = hkdf.extract(secret, self.tls13_dhe_secret) # noqa: E501 chts = hkdf.derive_secret(self.tls13_handshake_secret, - b"client handshake traffic secret", + b"c hs traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["client_handshake_traffic_secret"] = chts shts = hkdf.derive_secret(self.tls13_handshake_secret, - b"server handshake traffic secret", + b"s hs traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["server_handshake_traffic_secret"] = shts - if self.connection_end == "server": - self.prcs.tls13_derive_keys(chts) - self.pwcs.tls13_derive_keys(shts) - elif self.connection_end == "client": - self.pwcs.tls13_derive_keys(chts) - self.prcs.tls13_derive_keys(shts) - def compute_tls13_traffic_secrets(self): """ Ciphers key and IV are updated accordingly for Application data. self.handshake_messages should be ClientHello...ServerFinished. """ - hkdf = self.prcs.hkdf + if self.prcs and self.prcs.hkdf: + hkdf = self.prcs.hkdf + elif self.pwcs and self.pwcs.hkdf: + hkdf = self.pwcs.hkdf - self.tls13_master_secret = hkdf.extract(self.tls13_handshake_secret, - None) + tmp = hkdf.derive_secret(self.tls13_handshake_secret, + b"derived", + b"") + self.tls13_master_secret = hkdf.extract(tmp, None) cts0 = hkdf.derive_secret(self.tls13_master_secret, - b"client application traffic secret", + b"c ap traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["client_traffic_secrets"] = [cts0] sts0 = hkdf.derive_secret(self.tls13_master_secret, - b"server application traffic secret", + b"s ap traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["server_traffic_secrets"] = [sts0] es = hkdf.derive_secret(self.tls13_master_secret, - b"exporter master secret", + b"exp master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["exporter_secret"] = es @@ -699,31 +714,50 @@ def compute_tls13_resumption_secret(self): elif self.connection_end == "client": hkdf = self.pwcs.hkdf rs = hkdf.derive_secret(self.tls13_master_secret, - b"resumption master secret", + b"res master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["resumption_secret"] = rs - def compute_tls13_next_traffic_secrets(self): + def compute_tls13_next_traffic_secrets(self, connection_end, read_or_write): # noqa : E501 """ Ciphers key and IV are updated accordingly. """ - hkdf = self.prcs.hkdf - hl = hkdf.hash.digest_size + if self.rcs.hkdf: + hkdf = self.rcs.hkdf + hl = hkdf.hash.digest_size + elif self.wcs.hkdf: + hkdf = self.wcs.hkdf + hl = hkdf.hash.digest_size + + if read_or_write == "read": + if connection_end == "client": + cts = self.tls13_derived_secrets["client_traffic_secrets"] + ctsN = cts[-1] + ctsN_1 = hkdf.expand_label(ctsN, b"traffic upd", b"", hl) + cts.append(ctsN_1) + self.prcs.tls13_derive_keys(ctsN_1) + elif connection_end == "server": + sts = self.tls13_derived_secrets["server_traffic_secrets"] + stsN = sts[-1] + stsN_1 = hkdf.expand_label(stsN, b"traffic upd", b"", hl) + sts.append(stsN_1) - cts = self.tls13_derived_secrets["client_traffic_secrets"] - ctsN = cts[-1] - ctsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl) - cts.append(ctsN_1) + self.prcs.tls13_derive_keys(stsN_1) - stsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl) - cts.append(stsN_1) + elif read_or_write == "write": + if connection_end == "client": + cts = self.tls13_derived_secrets["client_traffic_secrets"] + ctsN = cts[-1] + ctsN_1 = hkdf.expand_label(ctsN, b"traffic upd", b"", hl) + cts.append(ctsN_1) + self.pwcs.tls13_derive_keys(ctsN_1) + elif connection_end == "server": + sts = self.tls13_derived_secrets["server_traffic_secrets"] + stsN = sts[-1] + stsN_1 = hkdf.expand_label(stsN, b"traffic upd", b"", hl) + sts.append(stsN_1) - if self.connection_end == "server": - self.prcs.tls13_derive_keys(ctsN_1) - self.pwcs.tls13_derive_keys(stsN_1) - elif self.connection_end == "client": - self.pwcs.tls13_derive_keys(ctsN_1) - self.prcs.tls13_derive_keys(stsN_1) + self.pwcs.tls13_derive_keys(stsN_1) # Tests for record building/parsing diff --git a/test/tls13.uts b/test/tls13.uts index f8aa9b66190..256e45c8297 100644 --- a/test/tls13.uts +++ b/test/tls13.uts @@ -1,266 +1,654 @@ -% Tests for TLS 1.3 + Tests for TLS 1.3 # # Try me with : # bash test/run_tests -t test/tls13.uts -F -+ Read a TLS 1.3 session ++ Read a protected TLS 1.3 session # /!\ These tests will not catch our 'INTEGRITY CHECK FAILED's. /!\ # We deem the knowledge of the plaintext sufficient for passing... -= Reading TLS 1.3 test session (vectors 5 from draft-ietf-tls-tls13-vectors-00) -~ crypto -import binascii -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers -from cryptography.hazmat.backends import default_backend +#~ crypto + + + += Reading test session - Loading unparsed TLS 1.3 records +import binascii def clean(s): - return binascii.unhexlify(''.join(c for c in s if c.isalnum())) - -clientHello1 = clean(""" - 16030100ae010000 aa0303d9e9898df6 - 3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b - 14555c0000061301 130313020100007b 0000000b00090000 - 06736572766572ff 01000100000a0008 0006001d00170018 - 002800260024001d 002005efa94d13f5 adcd14219379d5a3 - 7dbce4721d9294e5 72c6651aeb761838 815b002b0003027f - 12000d0020001e04 0305030603020308 0408050806040105 - 0106010201040205 0206020202002d00 020101 - """) -t = TLS(clientHello1) + return binascii.unhexlify(''.join(c for c in s if c.isalnum())) + +clientHello = clean(""" + 16 03 01 00 c4 01 00 00 c0 03 03 cb + 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 + ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 + 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d + e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d + 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + """) -helloRetryRequest = clean(""" - 160301000e060000 0a7f120006002800 020017 - """) -t = TLS(helloRetryRequest, tls_session=t.tls_session.mirror()) -secp256r1_client_privkey = clean(""" - 11fa48d153c917ff d89dff13140760a1 - 36265d399fa9f10e 2d766d42a6c84e90 - """) -clientHello2 = clean(""" - 16030100cf010000 cb0303d9e9898df6 - 3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b - 14555c0000061301 130313020100009c 0000000b00090000 - 06736572766572ff 01000100000a0008 0006001d00170018 - 0028004700450017 0041041e5a785f54 17fb18db42938435 - 34a5c0ba6e744baa 6846d0b32f4e9ea3 922724a08f2adb09 - f071f81402e7fd8c a33b76abe1cd556f d3e8fe20e0fd2e82 - 02f969002b000302 7f12000d0020001e 0403050306030203 - 0804080508060401 0501060102010402 050206020202002d 00020101 - """) -t = TLS(clientHello2, tls_session=t.tls_session.mirror()) -pubnum = t.tls_session.tls13_client_pubshares["secp256r1"].public_numbers() -privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256r1_client_privkey), pubnum) -privkey = privnum.private_key(default_backend()) -t.tls_session.tls13_client_privshares["secp256r1"] = privkey - -#secp256r1_server_privkey = clean(""" -# ff265d2062c70725 ca22513e1e6841ff -# 475e8a00421f0818 186edd1c0080cc6a -# """) serverHello = clean(""" - 1603010073020000 6f7f1296ff693075 - d8465651a9c28773 f5496542206ba390 199b9c997545d9a1 - 2666151301004900 2800450017004104 8a4d09cde58dbc04 - 1955b9a41a43c169 6dc5429ffa96f9cd 194a863ac782f181 - 59f072b4f610215d 86407dd7368b754a b2e64f2c1b3f9d45 - 7c264e2b1781a36b - """) -t = TLS(serverHello, tls_session=t.tls_session.mirror()) + 16 03 03 00 5a 02 00 00 56 03 03 a6 + af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 + 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 + 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 + cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + """) serverEncHS = clean(""" - 170301029081de4f cfd700da4573d570 - 5942f14a11e569aa 9aacc95260520102 6f74f2b2ad6abe08 - 7b53a4940ff94208 9e02d3159b1c6f11 75d7fcb51abad6fd - d4f7ff4af6590b47 16c1d90e1031e1a1 e32079f531108c6b - 9f79d6120319e0a3 73010e82d780a8f9 c3fdf8474840cdb6 - 7e4943d3808a27cd 5d9375c766a95ef4 8393c235d83ad26a - 20628671793f75df aa0be78b11fed206 6506d19a769d9d32 - adc0437784994359 ef5e452609353670 1c46004cf6fc252e - 546e797238c73b94 b073461158301f78 1498917c32dc0ece - 658a53790c667397 f7744775c2bef907 b5f7d5677b2e57fe - 7c4bfd43c7ad1ee4 6fd400c3d3c3c05f e8775f055263e98a - 692b49a818d0f698 4400c1db2f429fa8 9fb61d523398e1d0 - 2bc5c393027146c0 f326032d18cb8283 473f2b6d554df942 - c7b1a0050694c7b2 bf31a816f7ff77f1 d7db873dbb6e4646 - acabfa73c317a34c e6212a3469f549e6 cde71ab229a6f220 - acda60832b510663 02a23d02c734bd5e 71b04fb248ca47ba - 0c7b1fd28fee9b5d 86e6b1a6a2a1a43e 3831210519f54134 - c96486d11ef3125f 74969785690487e0 aa5c0a310ebf9d31 - 95ec5543af8a6ffb 710eb0a90285960d c1ccdc10ecee9669 - 9171e97eae526a17 205012ab6f262e44 31ae9a70ff2ed7bd - 966ef6bd4563f56a 7a14970dcabf97ae 7e4354db1ea27548 - c55c11542ad07bcd 6f47a7143b86c4e6 678ce7dc6d51a1b7 - 75687644d6526efa 3c864f592819e7b7 f9f1bbc02ed8821a - e66019b240b41f5e ebf9475069700030 7122f7c8a8d6c0da - a264c63183238d72 0eacb86879fab9ba 8a673c51a52c8284 - 75e3211223cd2238 bd8b8a934af3e4dd e10e788df23ad6d8 - 51d68b78082ac667 a854356415e7858b e526307332990d8c - c38a5dc4cfc22a2c a2bdd9126a2ce13d 7015264921 - """) -t = TLS(serverEncHS, tls_session=t.tls_session) + 17 03 03 02 a2 d1 ff 33 4a 56 f5 bf + f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df + 78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45 + cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3 + 89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b + d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9 + b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf + 51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d + 2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55 + cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f + d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6 + 86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac + 66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea + 52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e + a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6 + 54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb + 31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59 + 62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e + 92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af + 36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37 + 8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c + f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88 + 2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80 + f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69 + 18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99 + 2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11 + c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51 + 56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42 + f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f + 60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd + d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af + 93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da + bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b + """) -clientFinished = clean(""" - 170301003543adad e592362412fb77d7 - 28b181c01b77cd62 a661e4125e6f9851 826e418f4c292ec6 - 3254e8b0342d65db 8a7f074eed527ea6 98a6 - """) -t = TLS(clientFinished, tls_session=t.tls_session.mirror()) +clientEncHS = clean(""" + 17 03 03 00 35 75 ec 4d c2 38 cc e6 + 0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 + d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 + 26 c4 05 46 + """) -clientRecord = clean(""" - 17030100131ef5c9 e7205f31a1edf9b1 - 3600fec1271e4f5d - """) -t = TLS(clientRecord, tls_session=t.tls_session) -serverRecord = clean(""" - 170301001350ff6e 907c508b6b191ff6 - 094faf4c0b32d6a8 - """) -t = TLS(serverRecord, tls_session=t.tls_session.mirror()) += Reading TLS 1.3 session - TLS parsing (no encryption) does not throw any error +# We will need to distinguish between connection ends. See next XXX below. +from scapy.layers.tls.record import TLS +t1 = TLS(clientHello) +t2 = TLS(serverHello, tls_session=t1.tls_session.mirror()) -alert = t.inner.msg[0] -assert(isinstance(alert, TLSAlert)) -alert.level == 1 and alert.descr == 0 += Reading TLS 1.3 session - TLS Record header +# We leave the possibility for some attributes to be either '' or None. +assert(t1.type == 0x16) +assert(t1.version == 0x0301) +assert(t1.len == 196) +assert(not t1.iv) +assert(not t1.mac) +assert(not t1.pad and not t1.padlen) +len(t1.msg) == 1 -= Reading TLS 1.3 test session (vectors 3 from draft-ietf-tls-tls13-vectors-00) -~ crypto_advanced -from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey += Reading TLS 1.3 session - TLS Record __getitem__ +from scapy.layers.tls.handshake import TLSClientHello +TLSClientHello in t1 + + += Reading TLS 1.3 session - ClientHello +ch = t1.msg[0] +assert(isinstance(ch, TLSClientHello)) +assert(ch.msgtype == 1) +assert(ch.msglen == 192) +assert(ch.version == 0x0303) +assert(ch.gmt_unix_time == 0xcb34ecb1) +assert(ch.random_bytes == b'\xe7\x81c\xba\x1c8\xc6\xda\xcb\x19jm\xff\xa2\x1a\x8d\x99\x12\xec\x18\xa2\xefb\x83\x02M\xec\xe7') +assert(ch.sidlen == 0) +assert(not ch.sid) +assert(ch.cipherslen == 6) +assert(ch.ciphers == [4865, 4867, 4866]) +assert(ch.complen == 1) +assert(ch.comp == [0]) + + += Reading TLS 1.3 session - ClientHello extensions +from scapy.layers.tls.extensions import (TLS_Ext_ServerName, +TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, +TLS_Ext_SessionTicket, TLS_Ext_SupportedVersion_CH, +TLS_Ext_SignatureAlgorithms, TLS_Ext_PSKKeyExchangeModes, +TLS_Ext_RecordSizeLimit) + +from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_CH + +assert(ch.extlen == 145) +ext = ch.ext +assert(len(ext) == 9) +assert(isinstance(ext[0], TLS_Ext_ServerName)) +assert(ext[0].type == 0) +assert(ext[0].len == 11) +assert(ext[0].servernameslen == 9) +assert(len(ext[0].servernames) == 1) +assert(ext[0].servernames[0].nametype == 0) +assert(ext[0].servernames[0].namelen == 6) +assert(ext[0].servernames[0].servername == b"server") +assert(isinstance(ext[1], TLS_Ext_RenegotiationInfo)) +assert(not ext[1].renegotiated_connection) +assert(isinstance(ext[2], TLS_Ext_SupportedGroups)) +assert(ext[2].groups == [29, 23, 24, 25, 256, 257, 258, 259, 260]) +assert(isinstance(ext[3], TLS_Ext_SessionTicket)) +assert(not ext[3].ticket) +assert(isinstance(ext[4], TLS_Ext_KeyShare_CH)) +assert(ext[4].client_shares_len == 36) +assert(len(ext[4].client_shares) == 1) +assert(ext[4].client_shares[0].group == 29) +assert(ext[4].client_shares[0].kxlen == 32) +assert(ext[4].client_shares[0].key_exchange == b'\x998\x1d\xe5`\xe4\xbdC\xd2=\x8eCZ}\xba\xfe\xb3\xc0nQ\xc1<\xaeMT\x13i\x1eR\x9a\xaf,') +assert(isinstance(ext[5],TLS_Ext_SupportedVersion_CH)) +assert(ext[5].len == 3) +assert(ext[5].versionslen == 2) +assert(ext[5].versions == [772]) +assert(isinstance(ext[6], TLS_Ext_SignatureAlgorithms)) +assert(ext[6].sig_algs_len == 30) +assert(len(ext[6].sig_algs) == 15) +assert(ext[6].sig_algs[0] == 1027) +assert(ext[6].sig_algs[-1] == 514) +assert(isinstance(ext[7], TLS_Ext_PSKKeyExchangeModes)) +assert(ext[7].kxmodeslen == 1) +assert(ext[7].kxmodes[0] == 1) +assert(isinstance(ext[8], TLS_Ext_RecordSizeLimit)) +assert(ext[8].record_size_limit == 16385) + + += Reading TLS 1.3 session - ServerHello +from scapy.layers.tls.handshake import TLS13ServerHello +from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH +from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH + +assert(TLS13ServerHello in t2) +sh = t2.msg[0] +ext = sh.ext +assert(isinstance(sh, TLS13ServerHello)) +assert(sh.random_bytes == b'\xa6\xaf\x06\xa4\x12\x18`\xdc^n`$\x9c\xd3L\x95\x93\x0c\x8a\xc5\xcb\x144\xda\xc1Uw.\xd3\xe2i(') +assert(sh.cipher == 0x1301) +assert(len(sh.ext) == 2) +assert(isinstance(ext[0], TLS_Ext_KeyShare_SH)) +assert(ext[0].len == 36) +assert(ext[0].server_share.group == 29) +assert(ext[0].server_share.key_exchange == b'\xc9\x82\x88v\x11 \x95\xfefv+\xdb\xf7\xc6r\xe1V\xd6\xcc%;\x83=\xf1\xddi\xb1\xb0Nu\x1f\x0f') +assert(isinstance(ext[1], TLS_Ext_SupportedVersion_SH)) +assert(ext[1].version == 0x0304) + + += Reading TLS 1.3 session - TLS parsing (with encryption) does not throw any error +from scapy.layers.tls.record_tls13 import TLS13 +t3 = TLS13(serverEncHS, tls_session=t2.tls_session) + + += Reading TLS 1.3 session - TLS13 Record header +assert(t3.type == 0x17) +assert(t3.version == 0x0303) +assert(t3.len == 674) + + += Reading TLS 1.3 session - TLS13 Record __getitem__ +TLS13 in t3 + += Reading TLS 1.3 session - TLS13 ApplicationData +from scapy.layers.tls.record_tls13 import TLSInnerPlaintext +TLSInnerPlaintext in t3 +assert(len(t3.auth_tag) == 16) +assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') + + ++ Decrypt a TLS 1.3 session + += Decrypt a TLS 1.3 session - Parse client Hello +from scapy.layers.tls.extensions import TLS_Ext_SessionTicket +# Values from RFC8448, section 3 +x25519_clt_priv = clean(""" + 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 + 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 + """) + +x25519_clt_pub = clean(""" + 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c + """) -x25519_client_privkey = clean(""" - 00b4198a84ed6a7c 218702891735239d - 40b7c66505330364 3d3c67f7458ecbc9 - """) -clientHello = clean(""" - 1603010200010001 fc03039a464db650 - dcc81fed6f1fea63 5f15861574c0ed0b fb5778de7724fb92 - 7c5ef100003e1301 13031302c02bc02f cca9cca8c00ac009 - c013c023c027c014 009eccaa00330032 006700390038006b - 00160013009c002f 003c0035003d000a 0005000401000195 - 001500fc00000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000000000000 - 0000000000000000 0000000000000000 0000000b00090000 - 06736572766572ff 01000100000a0014 0012001d00170018 - 0019010001010102 01030104000b0002 0100002300000028 - 00260024001d0020 35e58b160db6124f 01a1d2475a22b72a - bd6896701eed4c7e fd6124ee231ba458 002b0007067f1203 - 030302000d002000 1e04030503060302 0308040805080604 - 0105010601020104 0205020602020200 2d00020101 - """) t = TLS(clientHello) -privkey = X25519PrivateKey._from_private_bytes(x25519_client_privkey) +assert(len(t.msg) == 1) +assert(t.msg[0].msgtype == 1) +assert(t.msg[0].extlen == 145) +assert(len(t.msg[0].ext) == 9) +e = t.msg[0].ext +assert(isinstance(e[0], TLS_Ext_ServerName)) +assert(isinstance(e[1], TLS_Ext_RenegotiationInfo)) +assert(isinstance(e[2], TLS_Ext_SupportedGroups)) +assert(isinstance(e[3],TLS_Ext_SessionTicket)) +assert(e[3].len == 0) +assert(isinstance(e[4], TLS_Ext_KeyShare_CH)) +assert(len(e[4].client_shares) == 1) +assert(e[4].client_shares[0].group == 29) +assert(e[4].client_shares[0].key_exchange == x25519_clt_pub) +assert(isinstance(e[5], TLS_Ext_SupportedVersion_CH)) +assert(isinstance(e[6], TLS_Ext_SignatureAlgorithms)) +assert(isinstance(e[7], TLS_Ext_PSKKeyExchangeModes)) +assert(e[7].kxmodeslen == 1) +assert(len(e[7].kxmodes) == 1) +assert(e[7].kxmodes[0] == 1) +assert(isinstance(e[8], TLS_Ext_RecordSizeLimit)) + + += Decrypt a TLS 1.3 session - Parse server Hello +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip + +# Values from RFC8448, section 3 +x25519_srv_priv = clean(""" + b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e + """) + +x25519_srv_pub = clean(""" + c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f + """) + +privkey = X25519PrivateKey.from_private_bytes(x25519_clt_priv) t.tls_session.tls13_client_privshares["x25519"] = privkey -x25519_server_privkey = clean(""" - 03d43f48ed52076f 4ce9bab73d1f39ec - 689cf304075829f5 2b90f9f13bea6f34 - """) -serverHello = clean(""" - 1603010052020000 4e7f1298e3436403 - 8683391cbec1039a a0fba2f496d8c8e6 327151cc94bbc5ef - 7390751301002800 280024001d0020a2 0ed1b7f2d96a7f12 - 568f0e460bb0fc86 dc8d1db6c07d6b10 d4dc74aaac9219 - """) t = TLS(serverHello, tls_session=t.tls_session.mirror()) -serverEncHS = clean(""" - 170301029c4e1f34 2dba17a54a09f7a1 - 8ffb2c6a29df17a6 db843044c52861bf 78988527ce366159 - e6a24871b704d2b9 fade56488921796d 719173a753bdfec8 - 0554c8c15e128695 450ccfdde1204ffd 2fb1ecdcd87b8070 - 644eb5a6b86ec951 aba3ed314754a2f3 14d4d2620b92da1f - 28f24b9559d76b67 a7b35c17cc231ba5 77a94fb2be59c74f - 84c8c78bf5faf4cb b2f8a37091580743 3c67d9f4e1b1923a - 3969b85a2ae9064e 34e84363aae43aa9 f58717836a017b9c - 33c3ad733c2fd3ce 288ae362764403d0 102a371047d9e49d - f9b30596262b1704 f0e9839fff5641ba a7041a4bcf9e4d46 - 7108922fc0ea0bc1 48dab2ebdd155f51 76c632be04a7c610 - 3fbc92754dba7962 4f8a09f8e8d65c17 eee87f98636fbc93 - bb734674b80d183c da904200a20d8f15 0a214902b6953209 - aa2431c3973bda3b d92a33878baca7b9 0507f433a55f2fe8 - f0db81898ebacf31 b68eaabfa27c39b6 a2453a322c005030 - 4e60bf53f0402b38 65b43fe5a7454c13 17a2dc76d1323fb1 - aa553996876a0dfe 8e789d6adf3dc85b 0636bb58a96e6aad - 851e7a6fc1dfa796 ec65e33bf9e3c05d 6de35f11e1f32731 - fb9550a60cb75e90 9345eb0edb81f99f cad883cb41d4a3ef - 7cbe671b92a8176b 472772be401b83a4 99b06b7ab0a1d9cd - 795e5ba0b67ce2d6 5c45565028824aa2 08797f405bbcf243 - 27dd69a1d986032f 544b15d110e4d8c4 681cb85c09960adb - 57fb9723eef0e0bb 275552af25fbdfc1 a4215adf14a9dba2 - 4462dd095f1a78f5 6ed6db3de139936f 14b091ab7f4adc81 - c277e68bfb6fd925 d92c06c0a4ddd105 9c071073a8a2e987 - f98948599f27bf6d 1f4369ac6c5a3323 2932fb8aa52ec4e1 - 85790dff0ef5eee0 13b4e90b5bc1cd4a c42b7ce82d856cc0 - f5d1c80400e68d61 b434cec56d437141 1e31849d4cf88862 - 8ba288548df6a19e c4 - """) -t = TLS(serverEncHS, tls_session=t.tls_session) +assert(len(t.msg) == 1) +assert(isinstance(t.msg[0], TLS13ServerHello)) +assert(len(t.msg[0].ext) == 2) +e = t.msg[0].ext +assert(isinstance(e[0], TLS_Ext_KeyShare_SH)) +assert(e[0].server_share.group == 29) +assert(e[0].server_share.key_exchange == x25519_srv_pub) +assert(isinstance(e[1], TLS_Ext_SupportedVersion_SH)) -clientFinished = clean(""" - 1703010035161e94 818226d7bd618063 - 0804644debc52bdd 661034243217ac45 a084228c82086baa - 4893ecfc969624d6 8e19d88c3e67ccb4 8bdf - """) -t = TLS(clientFinished, tls_session=t.tls_session.mirror()) - -serverRecord1 = clean(""" - 17030100bbe6b3e9 89df694688f29f5d - a42d9f56053fc6d2 f73ee23accad26f9 599ee4dcf4e0cf9e - de80128b48156a65 e5e47dee679a8401 1234862b6728fb12 - be5198d5c023d6f2 0c355fc417a5eade 1aff0bf9ecba14c8 - 7277ea7aeb30055e a4d9b37bc12f7517 27ca7a1efc9285f8 - ed5e9e3be42ff475 30f2b7347a90618b 6f7f4eba9b8b6564 - f2159fcfcf09e4b6 2b4b09bb129e7c76 5c877966ca66e5cd - a84cdb6087a07fc0 50c97f275568623c 5d0f459d2b1133d1 - d5d37cd441192da7 - """) -t = TLS(serverRecord1, tls_session=t.tls_session.mirror()) += Decrypt a TLS 1.3 session - Handshake traffic secret derivation +# Values from RFC8448, section 3 +early_secret = clean(""" + 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + """) -clientRecord1 = clean(""" - 170301004341b540 bf5adeaf9d209001 - 9f0733e281964724 526678a1946852cf 6f586dffacf1151d - bf7c9262ef6ae960 4a423fff339fd7e4 0cc3e7604ae661f0 - afa2f775c3668867 - """) -t = TLS(clientRecord1, tls_session=t.tls_session.mirror()) -app_data = t.inner.msg[0] -assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01') - -serverRecord2 = clean(""" - 17030100438c3168 1fb21f820ef0603c - dc3b9d3deedeb2bb 615aa418fb2590a0 9b0dec00c2299feb - 17c4206f89ab28d2 7a605e288ac9bd69 657593addd1046be - 51b23940f8746634 - """) -t = TLS(serverRecord2, tls_session=t.tls_session.mirror()) -app_data = t.inner.msg[0] -assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01') +ecdhe_secret = clean(""" + 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d + """) -clientRecord2 = clean(""" - 17030100131ce9b1 f21ba236bca94455 - ab2aad71c666534a - """) -t = TLS(clientRecord2, tls_session=t.tls_session.mirror()) -alert = t.inner.msg[0] -assert(isinstance(alert, TLSAlert)) -assert(alert.level == 1 and alert.descr == 0) - -serverRecord3 = clean(""" - 1703010013aabcdb 9d293d23fb00deb7 - 11b562afeddffeed +handshake_secret = clean(""" + 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + """) + +client_handshake_traffic_secret = clean(""" + b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + """) + +server_handshake_traffic_secret = clean(""" + b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + """) + +assert(len(t.tls_session.tls13_derived_secrets) == 5) +assert(t.tls_session.tls13_early_secret is not None) +assert(t.tls_session.tls13_early_secret == early_secret) +assert(t.tls_session.tls13_dhe_secret == ecdhe_secret) +assert(t.tls_session.tls13_handshake_secret is not None) +assert(t.tls_session.tls13_handshake_secret == handshake_secret) +assert( 'client_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) +assert( t.tls_session.tls13_derived_secrets['client_handshake_traffic_secret'] == client_handshake_traffic_secret) +assert( 'server_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) +assert(t.tls_session.tls13_derived_secrets['server_handshake_traffic_secret'] == server_handshake_traffic_secret) + + += Decrypt a TLS 1.3 session - Server handshake traffic key calculation +# Values from RFC8448, section 3 +server_hs_traffic_key = clean(""" + 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e + e4 03 bc + """) + +server_hs_traffic_iv = clean(""" + 5d 31 3e b2 67 12 76 ee 13 00 0b 30 + """) + +assert(t.tls_session.prcs.cipher.key == server_hs_traffic_key) +assert(t.tls_session.prcs.cipher.fixed_iv == server_hs_traffic_iv) + + + += Decrypt a TLS 1.3 session - Decrypt and parse server encrypted handshake +# Values from RFC8448, section 3 +server_finished = clean(""" + 88 63 e6 bf b0 42 0a 92 7f a2 7f 34 33 6a + 70 ae 42 6e 96 8e 3e b8 84 94 5b 96 85 6d ba 39 76 d1 + """) + +t = TLS13(serverEncHS, tls_session=t.tls_session) +assert(t.deciphered_len == 658) +assert(t.inner.type == 22) +assert(len(t.inner.msg) == 4) +assert(t.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') + +m = t.inner.msg + += Decrypt a TLS 1.3 session - Parse decrypted EncryptedExtension +from scapy.layers.tls.handshake import TLSEncryptedExtensions +assert(isinstance(m[0], TLSEncryptedExtensions)) +assert(m[0].msgtype == 8) +assert(m[0].msglen == 36) +assert(m[0].extlen == 34) +assert(len(m[0].ext) == 3) +assert(isinstance(m[0].ext[0], TLS_Ext_SupportedGroups)) +assert(m[0].ext[0].groupslen == 18) +assert(m[0].ext[0].groups == [29, 23, 24, 25, 256, 257, 258, 259, 260]) +assert(isinstance(m[0].ext[1], TLS_Ext_RecordSizeLimit)) +assert(m[0].ext[1].record_size_limit == 16385) +assert(isinstance(m[0].ext[2], TLS_Ext_ServerName)) +assert(m[0].ext[2].len == 0) + += Decrypt a TLS 1.3 session - Parse decrypted TLS13Certificate +from scapy.layers.tls.cert import Cert +from scapy.layers.tls.handshake import (_ASN1CertAndExt, TLS13Certificate) + +assert(isinstance(m[1], TLS13Certificate)) +assert(m[1].msgtype, 11) +assert(m[1].msglen == 441) +assert(m[1].cert_req_ctxt_len == 0) +assert(m[1].cert_req_ctxt == b'') +assert(m[1].certslen == 437) +assert(len(m[1].certs) == 1) +assert(isinstance(m[1].certs[0], _ASN1CertAndExt)) +assert(m[1].certs[0].cert[0], 432) +assert(isinstance(m[1].certs[0].cert[1], Cert)) +assert(m[1].certs[0].cert[1].cA == False) +assert(m[1].certs[0].cert[1].isSelfSigned() == True) +assert(m[1].certs[0].cert[1].issuer['commonName'] == 'rsa') +assert(m[1].certs[0].cert[1].keyUsage == ['digitalSignature', 'keyEncipherment']) +assert(m[1].certs[0].cert[1].notAfter_str == 'Jul 30 01:23:59 2026 GMT') +assert(m[1].certs[0].cert[1].notBefore_str == 'Jul 30 01:23:59 2016 GMT') +assert(m[1].certs[0].cert[1].serial == 2) +assert(m[1].certs[0].cert[1].sigAlg == 'sha256WithRSAEncryption') +assert(m[1].certs[0].cert[1].signatureLen == 128) +assert(m[1].certs[0].cert[1].subject['commonName'] == 'rsa') +assert(m[1].certs[0].cert[1].version == 3) + + += Decrypt a TLS 1.3 session - Parse decrypted TLSCertificateVerify +from scapy.layers.tls.handshake import TLSCertificateVerify +from scapy.layers.tls.keyexchange import _TLSSignature +assert(isinstance(m[2], TLSCertificateVerify)) +assert(isinstance(m[2], TLSCertificateVerify)) +assert(m[2].msgtype == 15) +assert(m[2].msglen == 132) +assert(isinstance(m[2].sig, _TLSSignature)) +assert(m[2].sig.sig_alg == 2052) +assert(m[2].sig.sig_len == 128) +assert(m[2].sig.sig_val == b"Zt|]\x88\xfa\x9b\xd2\xe5Z\xb0\x85\xa6\x10\x15\xb7!\x1f\x82L\xd4\x84\x14Z\xb3\xffR\xf1\xfd\xa8G{\x0bz\xbc\x90\xdbx\xe2\xd3:\\\x14\x1a\x07\x86S\xfak\xefx\x0c^\xa2H\xee\xaa\xa7\x85\xc4\xf3\x94\xca\xb6\xd3\x0b\xbe\x8dHY\xeeQ\x1f`)W\xb1T\x11\xac\x02vqE\x9eFD\\\x9e\xa5\x8c\x18\x1e\x81\x8e\x95\xb8\xc3\xfb\x0b\xf3'\x84\t\xd3\xbe\x15*=\xa5\x04>\x06=\xdae\xcd\xf5\xae\xa2\rS\xdf\xac\xd4/t\xf3") + += Decrypt a TLS 1.3 session - Parse decrypted TLSFinished +from scapy.layers.tls.handshake import TLSFinished +# Values from RFC8448, section 3 +server_finished = clean(""" + 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 + de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18 + """) +assert(isinstance(m[3], TLSFinished)) +assert(m[3].msgtype == 20) +assert(m[3].msglen == 32) +assert(m[3].vdata == server_finished) + + += Decrypt a TLS 1.3 session - Client handshake traffic key calculation + +# Values from RFC8448, section 3 +client_hs_traffic_key = clean(""" + db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 + 25 8d 01 + """) +client_hs_traffic_iv = clean(""" + 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f + """) + +assert(t.tls_session.pwcs.cipher.key == client_hs_traffic_key) +assert(t.tls_session.pwcs.cipher.fixed_iv == client_hs_traffic_iv) + += Decrypt a TLS 1.3 session - Decrypt and parse client encrypted handshake +# Values from RFC8448, section 3 +client_finished = clean(""" + a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 + 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61 + """) + +t = TLS13(clientEncHS, tls_session=t.tls_session.mirror()) +assert(t.deciphered_len == 37) +assert(t.inner.type == 22) +assert(len(t.inner.msg) == 1) +m = t.inner.msg +assert(isinstance(m[0], TLSFinished)) +assert(m[0].vdata == client_finished) + += Decrypt a TLS 1.3 session - Application traffic secret derivation +# Values from RFC8448, section 3 +master_secret = clean(""" + 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + """) + +client_application_traffic_secret_0 = clean(""" + 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 + """) + +server_application_traffic_secret_0 = clean(""" + a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + """) + + +exporter_master_secret = clean(""" + fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 + """) + +resumption_master_secret = clean(""" + 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 + b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + """) + + +assert(t.tls_session.tls13_master_secret is not None) +assert(t.tls_session.tls13_master_secret == master_secret) + +assert(len(t.tls_session.tls13_derived_secrets) == 9) +assert('client_traffic_secrets' in t.tls_session.tls13_derived_secrets) +assert(len(t.tls_session.tls13_derived_secrets['client_traffic_secrets']) == 1) +assert(t.tls_session.tls13_derived_secrets['client_traffic_secrets'][0] == client_application_traffic_secret_0) + +assert('server_traffic_secrets' in t.tls_session.tls13_derived_secrets) +assert(len(t.tls_session.tls13_derived_secrets['server_traffic_secrets']) == 1) +assert(t.tls_session.tls13_derived_secrets['server_traffic_secrets'][0] == server_application_traffic_secret_0) + +assert('exporter_secret' in t.tls_session.tls13_derived_secrets) +assert(t.tls_session.tls13_derived_secrets['exporter_secret'] == exporter_master_secret) + +assert('resumption_secret' in t.tls_session.tls13_derived_secrets) +assert(t.tls_session.tls13_derived_secrets['resumption_secret'] == resumption_master_secret) + += Decrypt a TLS 1.3 session - Application traffic keys calculation + +# Values from RFC8448, section 3 +client_ap_traffic_key = clean(""" + 17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 + 3f 50 51 + """) + +client_ap_traffic_iv = clean(""" + 5b 78 92 3d ee 08 57 90 33 e5 23 d9 + """) + +server_ap_traffic_key = clean(""" + 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac + 92 e3 56 + """) + +server_ap_traffic_iv = clean(""" + cf 78 2b 88 dd 83 54 9a ad f1 e9 84 + """) + +# wcs instead of pwcs ? +assert(t.tls_session.rcs.cipher.key == client_ap_traffic_key) +assert(t.tls_session.rcs.cipher.fixed_iv == client_ap_traffic_iv) +assert(t.tls_session.wcs.cipher.key == server_ap_traffic_key) +assert(t.tls_session.wcs.cipher.fixed_iv == server_ap_traffic_iv) + += Decrypt a TLS 1.3 session - Decrypt and parse server NewSessionTicket +from scapy.layers.tls.extensions import TLS_Ext_EarlyDataIndicationTicket +# Value from RFC8448, section 3 +serverEncTicket = clean(""" + 17 03 03 00 de 3a 6b 8f 90 41 4a 97 + d6 95 9c 34 87 68 0d e5 13 4a 2b 24 0e 6c ff ac 11 6e 95 d4 1d + 6a f8 f6 b5 80 dc f3 d1 1d 63 c7 58 db 28 9a 01 59 40 25 2f 55 + 71 3e 06 1d c1 3e 07 88 91 a3 8e fb cf 57 53 ad 8e f1 70 ad 3c + 73 53 d1 6d 9d a7 73 b9 ca 7f 2b 9f a1 b6 c0 d4 a3 d0 3f 75 e0 + 9c 30 ba 1e 62 97 2a c4 6f 75 f7 b9 81 be 63 43 9b 29 99 ce 13 + 06 46 15 13 98 91 d5 e4 c5 b4 06 f1 6e 3f c1 81 a7 7c a4 75 84 + 00 25 db 2f 0a 77 f8 1b 5a b0 5b 94 c0 13 46 75 5f 69 23 2c 86 + 51 9d 86 cb ee ac 87 aa c3 47 d1 43 f9 60 5d 64 f6 50 db 4d 02 + 3e 70 e9 52 ca 49 fe 51 37 12 1c 74 bc 26 97 68 7e 24 87 46 d6 + df 35 30 05 f3 bc e1 86 96 12 9c 81 53 55 6b 3b 6c 67 79 b3 7b + f1 59 85 68 4f """) -t = TLS(serverRecord3, tls_session=t.tls_session.mirror()) -alert = t.inner.msg[0] -assert(isinstance(alert, TLSAlert)) -alert.level == 1 and alert.descr == 0 +t = TLS13(serverEncTicket, tls_session=t.tls_session.mirror()) + +assert(t.deciphered_len == 206) +assert(t.inner.type == 22) +assert(t.auth_tag == b'\x9c\x81SUk;lgy\xb3{\xf1Y\x85hO') +assert(len(t.inner.msg) == 1) +m = t.inner.msg[0] +assert(m.msgtype == 4) +assert(m.ticket_lifetime == 30) +assert(m.ticket_age_add == 4208372421) +assert(m.noncelen == 2) +assert(len(m.ticket_nonce) == 2) +assert(m.ticket_nonce == b'\x00\x00') +assert(m.ticket == b',\x03]\x82\x93Y\xee_\xf7\xafN\xc9\x00\x00\x00\x00&*d\x94\xdcHm,\x8a4\xcb3\xfa\x90\xbf\x1b\x00p\xad=\xa2g\x7f\xa5\x90l[?}\x8f\x92\xf2(\xbd\xa4\r\xdar\x14p\xf9\xfb\xf2\x97\xb5\xae\xa6\x17do\xac\\\x03\'.\x97\x07\'\xc6!\xa7\x91A\xef_}\xe6P^[\xfb\xc3\x88\xe93Ci@\x93\x93J\xe4\xd3W') +assert(len(m.ext) == 1) +assert(isinstance(m.ext[0], TLS_Ext_EarlyDataIndicationTicket)) +assert(m.ext[0].max_early_data_size == 1024) + + += Decrypt a TLS 1.3 session - Compute the PSK associated with the ticket +from scapy.layers.tls.crypto.hkdf import TLS13_HKDF +hash_len = t.tls_session.rcs.ciphersuite.hash_alg.hash_len +hkdf = TLS13_HKDF(t.tls_session.rcs.ciphersuite.hash_alg.name.lower()) +tls13_psk_secret = hkdf.expand_label(t.tls_session.tls13_derived_secrets['resumption_secret'], + b"resumption", + m.ticket_nonce, + hash_len) + +# Value from RFC8448, section 3 +psk_resumption = clean(""" + 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + """) + +assert(hash_len == 32) +assert(tls13_psk_secret == psk_resumption) + += Decrypt a TLS 1.3 session - Decrypt and parse client Application Data +from scapy.layers.tls.record import TLSApplicationData +# Values from RFC8448, section 3 +payload = clean(""" + 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e + 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 + 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 + """) + + +clientEncAppData = clean(""" + 17 03 03 00 43 a2 3f 70 54 b6 2c 94 + d0 af fa fe 82 28 ba 55 cb ef ac ea 42 f9 14 aa 66 bc ab 3f 2b + 98 19 a8 a5 b4 6b 39 5b d5 4a 9a 20 44 1e 2b 62 97 4e 1f 5a 62 + 92 a2 97 70 14 bd 1e 3d ea e6 3a ee bb 21 69 49 15 e4 + """) +t = TLS13(clientEncAppData, tls_session=t.tls_session.mirror()) + +assert(t.deciphered_len == 51) +assert(len(t.inner.msg) == 1) +assert(t.inner.type == 23) +m = t.inner.msg[0] +assert(isinstance(m, TLSApplicationData)) +assert(m.data == payload) + += Decrypt a TLS 1.3 session - Decrypt and parse server Application Data +# Values from RFC8448, section 3 +payload = clean(""" + 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e + 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 + 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 + """) + +serverEncAppData = clean(""" + 17 03 03 00 43 2e 93 7e 11 ef 4a c7 + 40 e5 38 ad 36 00 5f c4 a4 69 32 fc 32 25 d0 5f 82 aa 1b 36 e3 + 0e fa f9 7d 90 e6 df fc 60 2d cb 50 1a 59 a8 fc c4 9c 4b f2 e5 + f0 a2 1c 00 47 c2 ab f3 32 54 0d d0 32 e1 67 c2 95 5d + """) + +t = TLS13(serverEncAppData, tls_session=t.tls_session.mirror()) +assert(t.deciphered_len == 51) +assert(len(t.inner.msg) == 1) +assert(t.inner.type == 23) +m = t.inner.msg[0] +assert(isinstance(m, TLSApplicationData)) +assert(m.data == payload) + += Decrypt a TLS 1.3 session - Decrypt client Alert +from scapy.layers.tls.record import TLSAlert +# Value from RFC8448, section 3 +clientEncAlert = clean(""" + 17 03 03 00 13 c9 87 27 60 65 56 66 + b7 4d 7f f1 15 3e fd 6d b6 d0 b0 e3 + """) + +t = TLS13(clientEncAlert, tls_session=t.tls_session.mirror()) +assert(t.deciphered_len == 3) +assert(len(t.inner.msg) == 1) +assert(t.inner.type == 21) +m = t.inner.msg[0] +assert(isinstance(m, TLSAlert)) +assert(m.level == 1) +assert(m.descr == 0) += Decrypt a TLS 1.3 session - Decrypt server Alert +# Value from RFC8448, section 3 +serverEncAlert = clean(""" + 17 03 03 00 13 b5 8f d6 71 66 eb f5 + 99 d2 47 20 cf be 7e fa 7a 88 64 a9 + """) +t = TLS13(serverEncAlert, tls_session=t.tls_session.mirror()) +assert(t.deciphered_len == 3) +assert(len(t.inner.msg) == 1) +assert(t.inner.type == 21) +m = t.inner.msg[0] +assert(isinstance(m, TLSAlert)) +assert(m.level == 1) +assert(m.descr == 0) \ No newline at end of file From 5f5a9d4a92bb0aae21b6a15dd683bd3fe926a73e Mon Sep 17 00:00:00 2001 From: rperez Date: Mon, 15 Jul 2019 14:38:46 +0200 Subject: [PATCH 2/9] fix for tls13.uts --- scapy/layers/tls/extensions.py | 16 +++++++++++++++- scapy/layers/tls/handshake.py | 4 ++++ scapy/layers/tls/record_tls13.py | 6 ++++++ test/tls13.uts | 11 +++++------ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py index 25c22a2dfee..119ee49e278 100644 --- a/scapy/layers/tls/extensions.py +++ b/scapy/layers/tls/extensions.py @@ -519,6 +519,18 @@ class TLS_Ext_EarlyDataIndication(TLS_Ext_Unknown): ShortField("len", None)] +class TLS_Ext_EarlyDataIndicationTicket(TLS_Ext_Unknown): + name = "TLS Extension - Ticket Early Data Info" + fields_desc = [ShortEnumField("type", 0x2a, _tls_ext), + ShortField("len", None), + IntField("max_early_data_size", 0)] + + +_tls_ext_early_data_cls = {1: TLS_Ext_EarlyDataIndication, + 4: TLS_Ext_EarlyDataIndicationTicket, + 8: TLS_Ext_EarlyDataIndication} + + class TLS_Ext_SupportedVersions(TLS_Ext_Unknown): name = "TLS Extension - Supported Versions (dummy class)" fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), @@ -741,7 +753,7 @@ def i2m(self, pkt, i): def m2i(self, pkt, m): res = [] - while len(m) > 4: + while len(m) >= 4: t = struct.unpack("!H", m[:2])[0] tmp_len = struct.unpack("!H", m[2:4])[0] cls = _tls_ext_cls.get(t, TLS_Ext_Unknown) @@ -760,6 +772,8 @@ def m2i(self, pkt, m): cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 elif cls is TLS_Ext_SupportedVersions: cls = _tls_ext_supported_version_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 + elif cls is TLS_Ext_EarlyDataIndication: + cls = _tls_ext_early_data_cls.get(pkt.msgtype, TLS_Ext_Unknown) res.append(cls(m[:tmp_len + 4], tls_session=pkt.tls_session)) m = m[tmp_len + 4:] return res diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index ad64d66f5ac..aca76c5c10f 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -538,6 +538,10 @@ def tls_session_update(self, msg_str): tls_version=s.tls_version) s.triggered_prcs_commit = True + if s.tls13_early_secret is None: + # In case the connState was not pre-initialized, we could not + # compute the early secrets at the ClientHello, so we do it here. + s.compute_tls13_early_secrets() s.compute_tls13_handshake_secrets() if connection_end == "server": shts = s.tls13_derived_secrets["server_handshake_traffic_secret"] diff --git a/scapy/layers/tls/record_tls13.py b/scapy/layers/tls/record_tls13.py index 4f944c51ec5..7e2211092e9 100644 --- a/scapy/layers/tls/record_tls13.py +++ b/scapy/layers/tls/record_tls13.py @@ -130,6 +130,12 @@ def pre_dissect(self, s): """ Decrypt, verify and decompress the message. """ + # We commit the pending read state if it has been triggered. + if self.tls_session.triggered_prcs_commit: + if self.tls_session.prcs is not None: + self.tls_session.rcs = self.tls_session.prcs + self.tls_session.prcs = None + self.tls_session.triggered_prcs_commit = False if len(s) < 5: raise Exception("Invalid record: header is too short.") diff --git a/test/tls13.uts b/test/tls13.uts index 256e45c8297..64d6867c622 100644 --- a/test/tls13.uts +++ b/test/tls13.uts @@ -204,11 +204,11 @@ assert(t3.len == 674) = Reading TLS 1.3 session - TLS13 Record __getitem__ TLS13 in t3 -= Reading TLS 1.3 session - TLS13 ApplicationData -from scapy.layers.tls.record_tls13 import TLSInnerPlaintext -TLSInnerPlaintext in t3 -assert(len(t3.auth_tag) == 16) -assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') +#= Reading TLS 1.3 session - TLS13 ApplicationData +#from scapy.layers.tls.record_tls13 import TLSInnerPlaintext +#TLSInnerPlaintext in t3 +#assert(len(t3.auth_tag) == 16) +#assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') + Decrypt a TLS 1.3 session @@ -513,7 +513,6 @@ server_ap_traffic_iv = clean(""" cf 78 2b 88 dd 83 54 9a ad f1 e9 84 """) -# wcs instead of pwcs ? assert(t.tls_session.rcs.cipher.key == client_ap_traffic_key) assert(t.tls_session.rcs.cipher.fixed_iv == client_ap_traffic_iv) assert(t.tls_session.wcs.cipher.key == server_ap_traffic_key) From b029c68a7f43ec81e36103bc7ea316bdad516766 Mon Sep 17 00:00:00 2001 From: rperez Date: Mon, 15 Jul 2019 15:48:53 +0200 Subject: [PATCH 3/9] fix missing import in tls.uts --- scapy/layers/tls/crypto/cipher_aead.py | 9 ++++--- scapy/layers/tls/handshake.py | 10 +++---- test/tls.uts | 37 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py index 8ec0caaeff6..1de7398d62e 100644 --- a/scapy/layers/tls/crypto/cipher_aead.py +++ b/scapy/layers/tls/crypto/cipher_aead.py @@ -330,7 +330,8 @@ 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) + res = self._cipher.encrypt(self._get_nonce(seq_num), P, A, + tag_length=self.tag_len) else: res = self._cipher.encrypt(self._get_nonce(seq_num), P, A) return res @@ -349,18 +350,20 @@ def auth_decrypt(self, A, C, seq_num): if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce(seq_num) + self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: - decryptor.finalize_with_tag(mac) + decryptor.finalize() except InvalidTag: raise AEADTagError(P, mac) else: 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 + P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A, # noqa: E501 + tag_length=self.tag_len) else: if (conf.crypto_valid_advanced and isinstance(self, Cipher_CHACHA20_POLY1305)): diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index aca76c5c10f..59d2ca5230d 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(_TLSHandshake): +class TLS13ClientHello(TLSClientHello): """ TLS ClientHello, with abilities to handle extensions. @@ -376,7 +376,7 @@ def tls_session_update(self, msg_str): ############################################################################### -class TLSServerHello(_TLSHandshake): +class TLSServerHello(TLSClientHello): """ TLS ServerHello, with abilities to handle extensions. @@ -473,7 +473,7 @@ def tls_session_update(self, msg_str): tls_version=self.version) -class TLS13ServerHello(_TLSHandshake): +class TLS13ServerHello(TLSClientHello): """ TLS 1.3 ServerHello """ name = "TLS 1.3 Handshake - Server Hello" fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), @@ -496,7 +496,7 @@ class TLS13ServerHello(_TLSHandshake): def post_build(self, pkt, pay): if self.random_bytes is None: pkt = pkt[:6] + randstring(32) + pkt[6 + 32:] - return super(TLS13ServerHello, self).post_build(pkt, pay) + return super(TLSClientHello, self).post_build(pkt, pay) def tls_session_update(self, msg_str): """ @@ -505,7 +505,7 @@ def tls_session_update(self, msg_str): cipher suite (if recognized), and finally we instantiate the write and read connection states. """ - super(TLS13ServerHello, self).tls_session_update(msg_str) + super(TLSClientHello, self).tls_session_update(msg_str) s = self.tls_session if self.ext: diff --git a/test/tls.uts b/test/tls.uts index 67b7fff85d5..764e0b7c2bf 100644 --- a/test/tls.uts +++ b/test/tls.uts @@ -502,6 +502,7 @@ def _all_aes_cbc_tests(): _all_aes_cbc_tests() +from scapy.layers.tls.crypto.pkcs1 import (pkcs_os2ip, pkcs_i2osp) = Crypto - AES cipher in GCM mode, auth_encrypt() and auth_decrypt() checks #https://tools.ietf.org/html/draft-mcgrew-gcm-test-01 @@ -773,6 +774,7 @@ p7_data = b"\x17\x03\x03\x01\xf6\x00\x00\x00\x00\x00\x00\x00\x01?\x04iy\x00\x04 = Reading TLS test session - TLS parsing (no encryption) does not throw any error +from scapy.layers.tls.record import TLS # We will need to distinguish between connection ends. See next XXX below. t1 = TLS(p1_ch) t2 = TLS(p2_sh, tls_session=t1.tls_session.mirror()) @@ -792,6 +794,7 @@ len(t1.msg) == 1 = Reading TLS test session - TLS Record __getitem__ +from scapy.layers.tls.handshake import TLSClientHello TLSClientHello in t1 = Reading TLS test session - ClientHello @@ -811,6 +814,12 @@ assert(ch.comp == [0]) = Reading TLS test session - ClientHello extensions +from scapy.layers.tls.extensions import (TLS_Ext_ServerName, +TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, +TLS_Ext_SupportedPointFormat, TLS_Ext_SessionTicket, TLS_Ext_NPN, +TLS_Ext_ALPN, TLS_Ext_SignatureAlgorithms, TLS_Ext_CSR, +OCSPStatusRequest) + assert(ch.extlen == 146) ext = ch.ext assert(len(ext) == 9) @@ -843,6 +852,8 @@ ext[8].sig_algs[-1] == 0x0202 = Reading TLS test session - ServerHello +from scapy.layers.tls.handshake import TLSServerHello + assert(TLSServerHello in t2) sh = t2.msg[0] assert(isinstance(sh, TLSServerHello)) @@ -854,6 +865,7 @@ sh.ext[-1].protocols[-1].protocol == b"http/1.1" = Reading TLS test session - Certificate +from scapy.layers.tls.cert import Cert cert = t3.msg[0] assert(cert.certslen == 2670) assert(len(cert.certs) == 2) @@ -871,6 +883,7 @@ isinstance(t4.payload.payload.payload, NoPayload) = Reading TLS test session - CertificateStatus +from scapy.layers.tls.handshake import TLSCertificateStatus assert(isinstance(cert_stat, TLSCertificateStatus)) assert(cert_stat.responselen == 471) cert_stat.response[0].responseStatus == 0 @@ -878,6 +891,8 @@ cert_stat.response[0].responseStatus == 0 = Reading TLS test session - ServerKeyExchange +from scapy.layers.tls.handshake import TLSServerKeyExchange +from scapy.layers.tls.keyexchange import ServerECDHNamedCurveParams assert(isinstance(ske, TLSServerKeyExchange)) p = ske.params assert(isinstance(p, ServerECDHNamedCurveParams)) @@ -888,6 +903,7 @@ ske.sig.sig_val[:4] == b'y\x8aQ\x11' and ske.sig.sig_val[-4:] == b'`15\xef' = Reading TLS test session - ServerHelloDone +from scapy.layers.tls.handshake import TLSServerHelloDone assert(isinstance(shd, TLSServerHelloDone)) shd.msglen == 0 @@ -914,6 +930,8 @@ t5 = TLS(p5_cke_ccs_fin, tls_session=t4.tls_session.mirror()) = Reading TLS test session - ClientKeyExchange +from scapy.layers.tls.handshake import TLSClientKeyExchange +from scapy.layers.tls.keyexchange import ClientECDiffieHellmanPublic cke = t5.msg[0] ccs = t5.payload.msg[0] rec_fin = t5.payload.payload @@ -927,6 +945,7 @@ assert(k.ecdh_Yc[:4] == b'\x04\xd2\x07\xce' and k.ecdh_Yc[-4:] == b'\xdc\x86[\xe = Reading TLS test session - ChangeCipherSpec +from scapy.layers.tls.record import TLSChangeCipherSpec assert(isinstance(ccs, TLSChangeCipherSpec)) ccs.msgtype == 1 @@ -944,6 +963,7 @@ fin.load == b'\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-' = Reading TLS test session - Ticket, CCS & Finished +from scapy.layers.tls.handshake import TLSNewSessionTicket t6 = TLS(p6_tick_ccs_fin, tls_session=t5.tls_session.mirror()) tick = t6.msg[0] assert(isinstance(tick, TLSNewSessionTicket)) @@ -1019,6 +1039,7 @@ ccs = t6.payload.msg[0] assert isinstance(ccs, TLSChangeCipherSpec) = Verify TLSFinished +from scapy.layers.tls.handshake import TLSFinished cfin = t6.payload.payload.msg[0] assert isinstance(cfin, TLSFinished) @@ -1036,6 +1057,8 @@ assert (t6.payload.payload.mac == b'\x10\xce\xca2\xb4\xc3m\xf1\x16c\xdb\xfc\x08\ + Read a vulnerable TLS session = Reading TLS vulnerable session - Decrypt data from using a compromised server key +from scapy.layers.tls.cert import PrivKeyRSA +from scapy.layers.tls.record import TLSApplicationData import os tmp = "/test/tls/pki/srv_key.pem" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp @@ -1065,6 +1088,8 @@ t.getlayer(2).msg[0].data == b"To boldly go where no man has gone before...\n" + Build TLS packets = Building packets - Various default records +from scapy.layers.tls.handshake import TLSCertificate +from scapy.layers.tls.record import TLSAlert raw(TLS()) raw(TLSClientHello()) raw(TLSServerHello()) @@ -1077,6 +1102,16 @@ raw(TLSApplicationData()) == b"" = Building packets - ClientHello with automatic length computation +from scapy.layers.tls.crypto.suites import (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, +TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, +TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA) + +from scapy.layers.tls.extensions import (ServerName, TLS_Ext_SupportedEllipticCurves, +ProtocolName) + ch = TLSClientHello() ch.msgtype = 'client_hello' ch.version = 'TLS 1.2' @@ -1140,6 +1175,7 @@ assert a.haslayer(Raw) = Building packets - Perform dummy session update +from scapy.layers.tls.handshake import TLSHelloRequest assert not TLSHelloRequest().tls_session_update(None) @@ -1170,6 +1206,7 @@ assert TLSServerHello in pkt ############################################################################### = Test tlsSession +from scapy.layers.tls.session import tlsSession s = tlsSession(ipsrc="216.58.201.227", ipdst="127.0.0.1", sport=443, dport=443, sid=1) assert s.__repr__() == "216.58.201.227:443 > 127.0.0.1:443" assert s == s From 3bbeef73295998b97d5918f00ac5237f6dd9a495 Mon Sep 17 00:00:00 2001 From: rperez Date: Mon, 15 Jul 2019 17:34:06 +0200 Subject: [PATCH 4/9] Remove trailing whitespaces --- test/tls.uts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tls.uts b/test/tls.uts index 764e0b7c2bf..fc5a90783b9 100644 --- a/test/tls.uts +++ b/test/tls.uts @@ -815,10 +815,10 @@ assert(ch.comp == [0]) = Reading TLS test session - ClientHello extensions from scapy.layers.tls.extensions import (TLS_Ext_ServerName, -TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, +TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, TLS_Ext_SupportedPointFormat, TLS_Ext_SessionTicket, TLS_Ext_NPN, TLS_Ext_ALPN, TLS_Ext_SignatureAlgorithms, TLS_Ext_CSR, -OCSPStatusRequest) +OCSPStatusRequest) assert(ch.extlen == 146) ext = ch.ext @@ -1105,8 +1105,8 @@ raw(TLSApplicationData()) == b"" from scapy.layers.tls.crypto.suites import (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, -TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, +TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA) from scapy.layers.tls.extensions import (ServerName, TLS_Ext_SupportedEllipticCurves, From dac39f3a195c0bbb6423a490ee0f08ac69b5e92b Mon Sep 17 00:00:00 2001 From: rperez Date: Mon, 15 Jul 2019 17:57:52 +0200 Subject: [PATCH 5/9] Remove unused code --- scapy/layers/tls/cert.py | 5 +---- scapy/layers/tls/crypto/cipher_aead.py | 4 +--- scapy/layers/tls/extensions.py | 2 -- scapy/layers/tls/handshake.py | 8 ++------ scapy/layers/tls/keyexchange.py | 6 +----- scapy/layers/tls/session.py | 4 ---- 6 files changed, 5 insertions(+), 24 deletions(-) diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py index f57c682ba7c..dafca992801 100644 --- a/scapy/layers/tls/cert.py +++ b/scapy/layers/tls/cert.py @@ -673,10 +673,7 @@ def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None): return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None): - if isinstance(self.pubKey, PubKeyECDSA): - return self.pubKey.verify(msg, sig) - else: - return self.pubKey.verify(msg, sig, t=t, h=h, mgf=mgf, L=L) + return self.pubKey.verify(msg, sig, t=t, h=h, mgf=mgf, L=L) def remainingDays(self, now=None): """ diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py index 1de7398d62e..faf35b98b78 100644 --- a/scapy/layers/tls/crypto/cipher_aead.py +++ b/scapy/layers/tls/crypto/cipher_aead.py @@ -312,8 +312,7 @@ def _get_nonce(self, seq_num): def auth_encrypt(self, P, A, seq_num): """ Encrypt the data, and append the computed authentication code. - TLS 1.3 does not use additional data, but we leave this option to the - user nonetheless. + The additional data for TLS 1.3 is the record header. Note that the cipher's authentication tag must be None when encrypting. """ @@ -339,7 +338,6 @@ def auth_encrypt(self, P, A, seq_num): def auth_decrypt(self, A, C, seq_num): """ Decrypt the data and verify the authentication code (in this order). - Note that TLS 1.3 is not supposed to use any additional data A. If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py index 119ee49e278..e95364ea1c5 100644 --- a/scapy/layers/tls/extensions.py +++ b/scapy/layers/tls/extensions.py @@ -49,13 +49,11 @@ 0x17: "extended_master_secret", # RFC 7627 0x1c: "record_size_limit", # RFC 8449 0x23: "session_ticket", # RFC 5077 - # 0x28: "key_share", 0x29: "pre_shared_key", 0x2a: "early_data_indication", 0x2b: "supported_versions", 0x2c: "cookie", 0x2d: "psk_key_exchange_modes", - # 0x2e: "ticket_early_data_info", 0x2f: "certificate_authorities", 0x30: "oid_filters", 0x31: "post_handshake_auth", diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 59d2ca5230d..1be6eb00e84 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -307,13 +307,9 @@ def tls_session_update(self, msg_str): class TLS13ClientHello(TLSClientHello): """ - TLS ClientHello, with abilities to handle extensions. + TLS 1.3 ClientHello, with abilities to handle extensions. - The Random structure follows the RFC 5246: while it is 32-byte long, - many implementations use the first 4 bytes as a gmt_unix_time, and then - the remaining 28 byts should be completely random. This was designed in - order to (sort of) mitigate broken RNGs. If you prefer to show the full - 32 random bytes without any GMT time, just comment in/out the lines below. + The Random structure is 32 random bytes without any GMT time """ name = "TLS 1.3 Handshake - Client Hello" fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type), diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py index d6170499a56..4f4ac0070eb 100644 --- a/scapy/layers/tls/keyexchange.py +++ b/scapy/layers/tls/keyexchange.py @@ -184,8 +184,6 @@ def _update_sig(self, m, key): h, sig = _tls_hash_sig[self.sig_alg].split('+') if sig.endswith('pss'): t = "pss" - elif sig.endswith('ecdsa'): - t = "" else: t = "pkcs" self.sig_val = key.sign(m, t=t, h=h) @@ -198,9 +196,7 @@ def _verify_sig(self, m, cert): if self.sig_val: if self.sig_alg: h, sig = _tls_hash_sig[self.sig_alg].split('+') - if sig.endswith('ecdsa'): - return cert.verify(m, self.sig_val, t="") - elif sig.endswith('pss'): + if sig.endswith('pss'): t = "pss" else: t = "pkcs" diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index 8fabdd72020..57e94cdd82f 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -431,15 +431,11 @@ def __init__(self, # These attributes should only be used with TLS 1.3 connections. self.tls13_psk_secret = None - self.tls13_psk_mode = None self.tls13_early_secret = None self.tls13_dhe_secret = None self.tls13_handshake_secret = None self.tls13_master_secret = None self.tls13_derived_secrets = {} - self.post_handshake_auth = False - self.tls13_ticket_ciphersuite = None - self.tls13_retry = False # Handshake messages needed for Finished computation/validation. # No record layer headers, no HelloRequests, no ChangeCipherSpecs. From c65bd91afde41886af4bc6641b40c6a8cf93409c Mon Sep 17 00:00:00 2001 From: rperez Date: Tue, 16 Jul 2019 09:45:27 +0200 Subject: [PATCH 6/9] Fix errors detected during checks --- scapy/layers/tls/extensions.py | 5 -- scapy/layers/tls/handshake.py | 16 +++--- scapy/layers/tls/keyexchange.py | 2 +- scapy/layers/tls/session.py | 6 +- test/tls13.uts | 99 ++++++++++++++++++++------------- 5 files changed, 74 insertions(+), 54 deletions(-) diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py index e95364ea1c5..b488d06f48c 100644 --- a/scapy/layers/tls/extensions.py +++ b/scapy/layers/tls/extensions.py @@ -553,11 +553,6 @@ class TLS_Ext_SupportedVersion_SH(TLS_Ext_Unknown): ShortField("len", None), ShortEnumField("version", None, _tls_version)] - def post_dissection(self, r): - if isinstance(r, TLS_Ext_SupportedVersion_SH): - self.tls_session.tls_version = r.version - return super(TLS_Ext_SupportedVersion_SH, self).post_dissection(r) - _tls_ext_supported_version_cls = {1: TLS_Ext_SupportedVersion_CH, 2: TLS_Ext_SupportedVersion_SH} diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 1be6eb00e84..51aa8fc8755 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -372,7 +372,7 @@ def tls_session_update(self, msg_str): ############################################################################### -class TLSServerHello(TLSClientHello): +class TLSServerHello(_TLSHandshake): """ TLS ServerHello, with abilities to handle extensions. @@ -419,7 +419,7 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs): def post_build(self, p, pay): if self.random_bytes is None: p = p[:10] + randstring(28) + p[10 + 28:] - return super(TLSClientHello, self).post_build(p, pay) + return super(TLSServerHello, self).post_build(p, pay) def tls_session_update(self, msg_str): """ @@ -431,7 +431,7 @@ def tls_session_update(self, msg_str): negotiation when we learn the session keys, and eventually they are committed once a ChangeCipherSpec has been sent/received. """ - super(TLSClientHello, self).tls_session_update(msg_str) + super(TLSServerHello, self).tls_session_update(msg_str) self.tls_session.tls_version = self.version self.random_bytes = msg_str[10:38] @@ -469,7 +469,7 @@ def tls_session_update(self, msg_str): tls_version=self.version) -class TLS13ServerHello(TLSClientHello): +class TLS13ServerHello(_TLSHandshake): """ TLS 1.3 ServerHello """ name = "TLS 1.3 Handshake - Server Hello" fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), @@ -489,10 +489,10 @@ class TLS13ServerHello(TLSClientHello): length_from=lambda pkt: (pkt.msglen - 38))] - def post_build(self, pkt, pay): + def post_build(self, p, pay): if self.random_bytes is None: - pkt = pkt[:6] + randstring(32) + pkt[6 + 32:] - return super(TLSClientHello, self).post_build(pkt, pay) + p = p[:6] + randstring(32) + p[6 + 32:] + return super(TLS13ServerHello, self).post_build(p, pay) def tls_session_update(self, msg_str): """ @@ -501,7 +501,7 @@ def tls_session_update(self, msg_str): cipher suite (if recognized), and finally we instantiate the write and read connection states. """ - super(TLSClientHello, self).tls_session_update(msg_str) + super(TLS13ServerHello, self).tls_session_update(msg_str) s = self.tls_session if self.ext: diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py index 4f4ac0070eb..5c0709ddb13 100644 --- a/scapy/layers/tls/keyexchange.py +++ b/scapy/layers/tls/keyexchange.py @@ -156,7 +156,7 @@ class _TLSSignature(_GenericTLSSessionInheritance): #XXX 'sig_alg' should be set in __init__ depending on the context. """ name = "TLS Digital Signature" - fields_desc = [SigAndHashAlgField("sig_alg", 0x0804, _tls_hash_sig), + fields_desc = [SigAndHashAlgField("sig_alg", 0x0401, _tls_hash_sig), SigLenField("sig_len", None, fmt="!H", length_of="sig_val"), SigValField("sig_val", None, diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index 57e94cdd82f..76e8ffa9fcd 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -615,7 +615,8 @@ def compute_tls13_handshake_secrets(self): elif self.pwcs: hkdf = self.pwcs.hkdf else: - raise + warning("No HKDF. This is abnormal.") + return if self.tls13_early_secret is None: self.tls13_early_secret = hkdf.extract(None, @@ -643,6 +644,9 @@ def compute_tls13_traffic_secrets(self): hkdf = self.prcs.hkdf elif self.pwcs and self.pwcs.hkdf: hkdf = self.pwcs.hkdf + else: + warning("No HKDF. This is abnormal.") + return tmp = hkdf.derive_secret(self.tls13_handshake_secret, b"derived", diff --git a/test/tls13.uts b/test/tls13.uts index 64d6867c622..828d1e77e8d 100644 --- a/test/tls13.uts +++ b/test/tls13.uts @@ -1,4 +1,4 @@ - Tests for TLS 1.3 +% Tests for TLS 1.3 # # Try me with : # bash test/run_tests -t test/tls13.uts -F @@ -16,7 +16,7 @@ = Reading test session - Loading unparsed TLS 1.3 records import binascii def clean(s): - return binascii.unhexlify(''.join(c for c in s if c.isalnum())) + return binascii.unhexlify(''.join(c for c in s if c.isalnum())) clientHello = clean(""" 16 03 01 00 c4 01 00 00 c0 03 03 cb @@ -204,15 +204,16 @@ assert(t3.len == 674) = Reading TLS 1.3 session - TLS13 Record __getitem__ TLS13 in t3 -#= Reading TLS 1.3 session - TLS13 ApplicationData -#from scapy.layers.tls.record_tls13 import TLSInnerPlaintext -#TLSInnerPlaintext in t3 -#assert(len(t3.auth_tag) == 16) -#assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') += Reading TLS 1.3 session - TLS13 ApplicationData +from scapy.layers.tls.record_tls13 import TLSInnerPlaintext +TLSInnerPlaintext in t3 +assert(len(t3.auth_tag) == 16) +assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') + Decrypt a TLS 1.3 session + = Decrypt a TLS 1.3 session - Parse client Hello from scapy.layers.tls.extensions import TLS_Ext_SessionTicket # Values from RFC8448, section 3 @@ -251,19 +252,21 @@ assert(isinstance(e[8], TLS_Ext_RecordSizeLimit)) = Decrypt a TLS 1.3 session - Parse server Hello +~ crypto_advanced + from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip # Values from RFC8448, section 3 x25519_srv_priv = clean(""" - b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 - 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e - """) + b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e +""") x25519_srv_pub = clean(""" - c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 - 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f - """) + c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f +""") privkey = X25519PrivateKey.from_private_bytes(x25519_clt_priv) t.tls_session.tls13_client_privshares["x25519"] = privkey @@ -279,32 +282,35 @@ assert(e[0].server_share.group == 29) assert(e[0].server_share.key_exchange == x25519_srv_pub) assert(isinstance(e[1], TLS_Ext_SupportedVersion_SH)) + = Decrypt a TLS 1.3 session - Handshake traffic secret derivation +~ crypto_advanced + # Values from RFC8448, section 3 early_secret = clean(""" - 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c - e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a - """) + 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a +""") ecdhe_secret = clean(""" - 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d - 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d - """) + 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d +""") handshake_secret = clean(""" - 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b - 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac - """) + 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac +""") client_handshake_traffic_secret = clean(""" - b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e - 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 - """) + b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 +""") server_handshake_traffic_secret = clean(""" - b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d - 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 - """) + b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 +""") assert(len(t.tls_session.tls13_derived_secrets) == 5) assert(t.tls_session.tls13_early_secret is not None) @@ -319,22 +325,24 @@ assert(t.tls_session.tls13_derived_secrets['server_handshake_traffic_secret'] == = Decrypt a TLS 1.3 session - Server handshake traffic key calculation +~ crypto_advanced + # Values from RFC8448, section 3 server_hs_traffic_key = clean(""" - 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e - e4 03 bc - """) + 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e + e4 03 bc +""") server_hs_traffic_iv = clean(""" - 5d 31 3e b2 67 12 76 ee 13 00 0b 30 - """) + 5d 31 3e b2 67 12 76 ee 13 00 0b 30 +""") assert(t.tls_session.prcs.cipher.key == server_hs_traffic_key) assert(t.tls_session.prcs.cipher.fixed_iv == server_hs_traffic_iv) - - = Decrypt a TLS 1.3 session - Decrypt and parse server encrypted handshake +~ crypto_advanced + # Values from RFC8448, section 3 server_finished = clean(""" 88 63 e6 bf b0 42 0a 92 7f a2 7f 34 33 6a @@ -365,18 +373,20 @@ assert(isinstance(m[0].ext[2], TLS_Ext_ServerName)) assert(m[0].ext[2].len == 0) = Decrypt a TLS 1.3 session - Parse decrypted TLS13Certificate +~ crypto_advanced + from scapy.layers.tls.cert import Cert from scapy.layers.tls.handshake import (_ASN1CertAndExt, TLS13Certificate) assert(isinstance(m[1], TLS13Certificate)) -assert(m[1].msgtype, 11) +assert(m[1].msgtype == 11) assert(m[1].msglen == 441) assert(m[1].cert_req_ctxt_len == 0) assert(m[1].cert_req_ctxt == b'') assert(m[1].certslen == 437) assert(len(m[1].certs) == 1) assert(isinstance(m[1].certs[0], _ASN1CertAndExt)) -assert(m[1].certs[0].cert[0], 432) +assert(m[1].certs[0].cert[0] == 432) assert(isinstance(m[1].certs[0].cert[1], Cert)) assert(m[1].certs[0].cert[1].cA == False) assert(m[1].certs[0].cert[1].isSelfSigned() == True) @@ -392,6 +402,8 @@ assert(m[1].certs[0].cert[1].version == 3) = Decrypt a TLS 1.3 session - Parse decrypted TLSCertificateVerify +~ crypto_advanced + from scapy.layers.tls.handshake import TLSCertificateVerify from scapy.layers.tls.keyexchange import _TLSSignature assert(isinstance(m[2], TLSCertificateVerify)) @@ -404,6 +416,7 @@ assert(m[2].sig.sig_len == 128) assert(m[2].sig.sig_val == b"Zt|]\x88\xfa\x9b\xd2\xe5Z\xb0\x85\xa6\x10\x15\xb7!\x1f\x82L\xd4\x84\x14Z\xb3\xffR\xf1\xfd\xa8G{\x0bz\xbc\x90\xdbx\xe2\xd3:\\\x14\x1a\x07\x86S\xfak\xefx\x0c^\xa2H\xee\xaa\xa7\x85\xc4\xf3\x94\xca\xb6\xd3\x0b\xbe\x8dHY\xeeQ\x1f`)W\xb1T\x11\xac\x02vqE\x9eFD\\\x9e\xa5\x8c\x18\x1e\x81\x8e\x95\xb8\xc3\xfb\x0b\xf3'\x84\t\xd3\xbe\x15*=\xa5\x04>\x06=\xdae\xcd\xf5\xae\xa2\rS\xdf\xac\xd4/t\xf3") = Decrypt a TLS 1.3 session - Parse decrypted TLSFinished +~ crypto_advanced from scapy.layers.tls.handshake import TLSFinished # Values from RFC8448, section 3 server_finished = clean(""" @@ -417,7 +430,7 @@ assert(m[3].vdata == server_finished) = Decrypt a TLS 1.3 session - Client handshake traffic key calculation - +~ crypto_advanced # Values from RFC8448, section 3 client_hs_traffic_key = clean(""" db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 @@ -431,6 +444,7 @@ assert(t.tls_session.pwcs.cipher.key == client_hs_traffic_key) assert(t.tls_session.pwcs.cipher.fixed_iv == client_hs_traffic_iv) = Decrypt a TLS 1.3 session - Decrypt and parse client encrypted handshake +~ crypto_advanced # Values from RFC8448, section 3 client_finished = clean(""" a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 @@ -446,6 +460,7 @@ assert(isinstance(m[0], TLSFinished)) assert(m[0].vdata == client_finished) = Decrypt a TLS 1.3 session - Application traffic secret derivation +~ crypto_advanced # Values from RFC8448, section 3 master_secret = clean(""" 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a @@ -493,7 +508,7 @@ assert('resumption_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['resumption_secret'] == resumption_master_secret) = Decrypt a TLS 1.3 session - Application traffic keys calculation - +~ crypto_advanced # Values from RFC8448, section 3 client_ap_traffic_key = clean(""" 17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 @@ -519,6 +534,7 @@ assert(t.tls_session.wcs.cipher.key == server_ap_traffic_key) assert(t.tls_session.wcs.cipher.fixed_iv == server_ap_traffic_iv) = Decrypt a TLS 1.3 session - Decrypt and parse server NewSessionTicket +~ crypto_advanced from scapy.layers.tls.extensions import TLS_Ext_EarlyDataIndicationTicket # Value from RFC8448, section 3 serverEncTicket = clean(""" @@ -555,6 +571,7 @@ assert(m.ext[0].max_early_data_size == 1024) = Decrypt a TLS 1.3 session - Compute the PSK associated with the ticket +~ crypto_advanced from scapy.layers.tls.crypto.hkdf import TLS13_HKDF hash_len = t.tls_session.rcs.ciphersuite.hash_alg.hash_len hkdf = TLS13_HKDF(t.tls_session.rcs.ciphersuite.hash_alg.name.lower()) @@ -573,6 +590,7 @@ assert(hash_len == 32) assert(tls13_psk_secret == psk_resumption) = Decrypt a TLS 1.3 session - Decrypt and parse client Application Data +~ crypto_advanced from scapy.layers.tls.record import TLSApplicationData # Values from RFC8448, section 3 payload = clean(""" @@ -598,6 +616,7 @@ assert(isinstance(m, TLSApplicationData)) assert(m.data == payload) = Decrypt a TLS 1.3 session - Decrypt and parse server Application Data +~ crypto_advanced # Values from RFC8448, section 3 payload = clean(""" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e @@ -621,6 +640,7 @@ assert(isinstance(m, TLSApplicationData)) assert(m.data == payload) = Decrypt a TLS 1.3 session - Decrypt client Alert +~ crypto_advanced from scapy.layers.tls.record import TLSAlert # Value from RFC8448, section 3 clientEncAlert = clean(""" @@ -638,6 +658,7 @@ assert(m.level == 1) assert(m.descr == 0) = Decrypt a TLS 1.3 session - Decrypt server Alert +~ crypto_advanced # Value from RFC8448, section 3 serverEncAlert = clean(""" 17 03 03 00 13 b5 8f d6 71 66 eb f5 From c8d0bad0723cd786b5754dbd050da296912683fb Mon Sep 17 00:00:00 2001 From: rperez Date: Tue, 16 Jul 2019 15:45:40 +0200 Subject: [PATCH 7/9] Add missing crypto_advanced --- test/tls13.uts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tls13.uts b/test/tls13.uts index 828d1e77e8d..9dc7ea540e4 100644 --- a/test/tls13.uts +++ b/test/tls13.uts @@ -215,6 +215,8 @@ assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') = Decrypt a TLS 1.3 session - Parse client Hello +~ crypto_advanced + from scapy.layers.tls.extensions import TLS_Ext_SessionTicket # Values from RFC8448, section 3 x25519_clt_priv = clean(""" @@ -358,6 +360,8 @@ assert(t.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') m = t.inner.msg = Decrypt a TLS 1.3 session - Parse decrypted EncryptedExtension +~ crypto_advanced + from scapy.layers.tls.handshake import TLSEncryptedExtensions assert(isinstance(m[0], TLSEncryptedExtensions)) assert(m[0].msgtype == 8) From 4ffed7b4a354bc140c85b867c67b547376cecbae Mon Sep 17 00:00:00 2001 From: rperez Date: Tue, 16 Jul 2019 18:28:54 +0200 Subject: [PATCH 8/9] Apply requested change (add explanation in the docstring and remove parenthesis) --- scapy/layers/tls/session.py | 13 +++++++++++++ test/tls.uts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index 76e8ffa9fcd..6966d91ffca 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -560,6 +560,19 @@ def compute_sslv2_km_and_derive_keys(self): def compute_tls13_early_secrets(self, external=False): """ + This function computes the Early Secret, the binder_key, + the client_early_traffic_secret and the + early_exporter_master_secret (See RFC8446, section 7.1). + + The parameter external is used for the computation of the + binder_key : + - For external PSK provisioned outside out of TLS, the parameter + external must be True. + - For resumption PSK, the parameter external must be False. + + If no argument is specified, the label "res binder" will be + used by default. + Ciphers key and IV are updated accordingly for 0-RTT data. self.handshake_messages should be ClientHello only. """ diff --git a/test/tls.uts b/test/tls.uts index fc5a90783b9..d9d745bcfd2 100644 --- a/test/tls.uts +++ b/test/tls.uts @@ -502,7 +502,7 @@ def _all_aes_cbc_tests(): _all_aes_cbc_tests() -from scapy.layers.tls.crypto.pkcs1 import (pkcs_os2ip, pkcs_i2osp) +from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp = Crypto - AES cipher in GCM mode, auth_encrypt() and auth_decrypt() checks #https://tools.ietf.org/html/draft-mcgrew-gcm-test-01 From 2e0c3063639032de65a8329a36c95317d3f16736 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Wed, 24 Jul 2019 22:26:57 +0200 Subject: [PATCH 9/9] Remove small duplication --- scapy/layers/tls/handshake.py | 54 ++++++++++++++--------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 51aa8fc8755..19fa57139fa 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -469,25 +469,30 @@ def tls_session_update(self, msg_str): tls_version=self.version) +_tls_13_server_hello_fields = [ + ByteEnumField("msgtype", 2, _tls_handshake_type), + ThreeBytesField("msglen", None), + _TLSVersionField("version", 0x0303, _tls_version), + _TLSRandomBytesField("random_bytes", None, 32), + FieldLenField("sidlen", None, length_of="sid", fmt="B"), + _SessionIDField("sid", "", + length_from=lambda pkt: pkt.sidlen), + EnumField("cipher", None, _tls_cipher_suites), + _CompressionMethodsField("comp", [0], + _tls_compression_algs, + itemfmt="B", + length_from=lambda pkt: 1), + _ExtensionsLenField("extlen", None, length_of="ext"), + _ExtensionsField("ext", None, + length_from=lambda pkt: (pkt.msglen - + 38)) +] + + class TLS13ServerHello(_TLSHandshake): """ TLS 1.3 ServerHello """ name = "TLS 1.3 Handshake - Server Hello" - fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), - ThreeBytesField("msglen", None), - _TLSVersionField("version", 0x0303, _tls_version), - _TLSRandomBytesField("random_bytes", None, 32), - FieldLenField("sidlen", None, length_of="sid", fmt="B"), - _SessionIDField("sid", "", - length_from=lambda pkt: pkt.sidlen), - EnumField("cipher", None, _tls_cipher_suites), - _CompressionMethodsField("comp", [0], - _tls_compression_algs, - itemfmt="B", - length_from=lambda pkt: 1), - _ExtensionsLenField("extlen", None, length_of="ext"), - _ExtensionsField("ext", None, - length_from=lambda pkt: (pkt.msglen - - 38))] + fields_desc = _tls_13_server_hello_fields def post_build(self, p, pay): if self.random_bytes is None: @@ -554,22 +559,7 @@ def tls_session_update(self, msg_str): class TLS13HelloRetryRequest(_TLSHandshake): name = "TLS 1.3 Handshake - Hello Retry Request" - fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), - ThreeBytesField("msglen", None), - _TLSVersionField("version", 0x0303, _tls_version), - _TLSRandomBytesField("random_bytes", None, 32), - FieldLenField("sidlen", None, length_of="sid", fmt="B"), - _SessionIDField("sid", "", - length_from=lambda pkt: pkt.sidlen), - EnumField("cipher", None, _tls_cipher_suites), - _CompressionMethodsField("comp", [0], - _tls_compression_algs, - itemfmt="B", - length_from=lambda pkt: 1), - _ExtensionsLenField("extlen", None, length_of="ext"), - _ExtensionsField("ext", None, - length_from=lambda pkt: (pkt.msglen - - 38))] + fields_desc = _tls_13_server_hello_fields ###############################################################################