• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
5#               2015, 2016, 2017 Maxence Tury
6
7"""
8TLS base fields, used for record parsing/building. As several operations depend
9upon the TLS version or ciphersuite, the packet has to provide a TLS context.
10"""
11import struct
12
13from scapy.fields import ByteField, ShortEnumField, ShortField, StrField
14from scapy.compat import orb
15
16_tls_type = {20: "change_cipher_spec",
17             21: "alert",
18             22: "handshake",
19             23: "application_data"}
20
21_tls_version = {0x0002: "SSLv2",
22                0x0200: "SSLv2",
23                0x0300: "SSLv3",
24                0x0301: "TLS 1.0",
25                0x0302: "TLS 1.1",
26                0x0303: "TLS 1.2",
27                0x7f12: "TLS 1.3-d18",
28                0x7f13: "TLS 1.3-d19",
29                0x0304: "TLS 1.3"}
30
31_tls_version_options = {"sslv2": 0x0002,
32                        "sslv3": 0x0300,
33                        "tls1": 0x0301,
34                        "tls10": 0x0301,
35                        "tls11": 0x0302,
36                        "tls12": 0x0303,
37                        "tls13-d18": 0x7f12,
38                        "tls13-d19": 0x7f13,
39                        "tls13": 0x0304}
40
41
42def _tls13_version_filter(version, legacy_version):
43    if version < 0x0304:
44        return version
45    else:
46        return legacy_version
47
48
49class _TLSClientVersionField(ShortEnumField):
50    """
51    We use the advertised_tls_version if it has been defined,
52    and the legacy 0x0303 for TLS 1.3 packets.
53    """
54
55    def i2h(self, pkt, x):
56        if x is None:
57            v = pkt.tls_session.advertised_tls_version
58            if v:
59                return _tls13_version_filter(v, 0x0303)
60            return ""
61        return x
62
63    def i2m(self, pkt, x):
64        if x is None:
65            v = pkt.tls_session.advertised_tls_version
66            if v:
67                return _tls13_version_filter(v, 0x0303)
68            return b""
69        return x
70
71
72class _TLSVersionField(ShortEnumField):
73    """
74    We use the tls_version if it has been defined, else the advertised version.
75    Also, the legacy 0x0301 is used for TLS 1.3 packets.
76    """
77
78    def i2h(self, pkt, x):
79        if x is None:
80            v = pkt.tls_session.tls_version
81            if v:
82                return _tls13_version_filter(v, 0x0301)
83            else:
84                adv_v = pkt.tls_session.advertised_tls_version
85                return _tls13_version_filter(adv_v, 0x0301)
86        return x
87
88    def i2m(self, pkt, x):
89        if x is None:
90            v = pkt.tls_session.tls_version
91            if v:
92                return _tls13_version_filter(v, 0x0301)
93            else:
94                adv_v = pkt.tls_session.advertised_tls_version
95                return _tls13_version_filter(adv_v, 0x0301)
96        return x
97
98
99class _TLSLengthField(ShortField):
100    def i2repr(self, pkt, x):
101        s = super(_TLSLengthField, self).i2repr(pkt, x)
102        if pkt.deciphered_len is not None:
103            dx = pkt.deciphered_len
104            ds = super(_TLSLengthField, self).i2repr(pkt, dx)
105            s += "    [deciphered_len= %s]" % ds
106        return s
107
108
109class _TLSIVField(StrField):
110    """
111    As stated in Section 6.2.3.2. RFC 4346, TLS 1.1 implements an explicit IV
112    mechanism. For that reason, the behavior of the field is dependent on the
113    TLS version found in the packet if available or otherwise (on build, if
114    not overloaded, it is provided by the session). The size of the IV and
115    its value are obviously provided by the session. As a side note, for the
116    first packets exchanged by peers, NULL being the default enc alg, it is
117    empty (except if forced to a specific value). Also note that the field is
118    kept empty (unless forced to a specific value) when the cipher is a stream
119    cipher (and NULL is considered a stream cipher).
120    """
121
122    def i2len(self, pkt, i):
123        if i is not None:
124            return len(i)
125        tmp_len = 0
126        cipher_type = pkt.tls_session.rcs.cipher.type
127        if cipher_type == "block":
128            if pkt.tls_session.tls_version >= 0x0302:
129                tmp_len = pkt.tls_session.rcs.cipher.block_size
130        elif cipher_type == "aead":
131            tmp_len = pkt.tls_session.rcs.cipher.nonce_explicit_len
132        return tmp_len
133
134    def i2m(self, pkt, x):
135        return x or b""
136
137    def addfield(self, pkt, s, val):
138        return s + self.i2m(pkt, val)
139
140    def getfield(self, pkt, s):
141        tmp_len = 0
142        cipher_type = pkt.tls_session.rcs.cipher.type
143        if cipher_type == "block":
144            if pkt.tls_session.tls_version >= 0x0302:
145                tmp_len = pkt.tls_session.rcs.cipher.block_size
146        elif cipher_type == "aead":
147            tmp_len = pkt.tls_session.rcs.cipher.nonce_explicit_len
148        return s[tmp_len:], self.m2i(pkt, s[:tmp_len])
149
150    def i2repr(self, pkt, x):
151        return repr(self.i2m(pkt, x))
152
153
154class _TLSMACField(StrField):
155    def i2len(self, pkt, i):
156        if i is not None:
157            return len(i)
158        return pkt.tls_session.wcs.mac_len
159
160    def i2m(self, pkt, x):
161        if x is None:
162            return b""
163        return x
164
165    def addfield(self, pkt, s, val):
166        # We add nothing here. This is done in .post_build() if needed.
167        return s
168
169    def getfield(self, pkt, s):
170        if (
171            pkt.tls_session.rcs.cipher.type != "aead" and
172            False in pkt.tls_session.rcs.cipher.ready.values()
173        ):
174            # XXX Find a more proper way to handle the still-encrypted case
175            return s, b""
176        tmp_len = pkt.tls_session.rcs.mac_len
177        return s[tmp_len:], self.m2i(pkt, s[:tmp_len])
178
179    def i2repr(self, pkt, x):
180        # XXX Provide status when dissection has been performed successfully?
181        return repr(self.i2m(pkt, x))
182
183
184class _TLSPadField(StrField):
185    def i2len(self, pkt, i):
186        if i is not None:
187            return len(i)
188        return 0
189
190    def i2m(self, pkt, x):
191        if x is None:
192            return b""
193        return x
194
195    def addfield(self, pkt, s, val):
196        # We add nothing here. This is done in .post_build() if needed.
197        return s
198
199    def getfield(self, pkt, s):
200        if pkt.tls_session.consider_read_padding():
201            # This should work with SSLv3 and also TLS versions.
202            # Note that we need to retrieve pkt.padlen beforehand,
203            # because it's possible that the padding is followed by some data
204            # from another TLS record (hence the last byte from s would not be
205            # the last byte from the current record padding).
206            tmp_len = orb(s[pkt.padlen - 1])
207            return s[tmp_len:], self.m2i(pkt, s[:tmp_len])
208        return s, None
209
210    def i2repr(self, pkt, x):
211        # XXX Provide status when dissection has been performed successfully?
212        return repr(self.i2m(pkt, x))
213
214
215class _TLSPadLenField(ByteField):
216    def addfield(self, pkt, s, val):
217        # We add nothing here. This is done in .post_build() if needed.
218        return s
219
220    def getfield(self, pkt, s):
221        if pkt.tls_session.consider_read_padding():
222            return ByteField.getfield(self, pkt, s)
223        return s, None
224
225
226# SSLv2 fields
227
228class _SSLv2LengthField(_TLSLengthField):
229    def i2repr(self, pkt, x):
230        s = super(_SSLv2LengthField, self).i2repr(pkt, x)
231        if pkt.with_padding:
232            x |= 0x8000
233        # elif pkt.with_escape:      #XXX no complete support for 'escape' yet
234        #   x |= 0x4000
235            s += "    [with padding: %s]" % hex(x)
236        return s
237
238    def getfield(self, pkt, s):
239        msglen = struct.unpack('!H', s[:2])[0]
240        pkt.with_padding = (msglen & 0x8000) == 0
241        if pkt.with_padding:
242            msglen_clean = msglen & 0x3fff
243        else:
244            msglen_clean = msglen & 0x7fff
245        return s[2:], msglen_clean
246
247
248class _SSLv2MACField(_TLSMACField):
249    pass
250
251
252class _SSLv2PadField(_TLSPadField):
253    def getfield(self, pkt, s):
254        if pkt.padlen is not None:
255            tmp_len = pkt.padlen
256            return s[tmp_len:], self.m2i(pkt, s[:tmp_len])
257        return s, None
258
259
260class _SSLv2PadLenField(_TLSPadLenField):
261    def getfield(self, pkt, s):
262        if pkt.with_padding:
263            return ByteField.getfield(self, pkt, s)
264        return s, None
265