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