• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 helpers, provided as out-of-context methods.
8"""
9
10from scapy.error import warning
11from scapy.fields import (ByteEnumField, ShortEnumField,
12                          FieldLenField, StrLenField)
13from scapy.packet import Packet
14
15from scapy.layers.tls.basefields import _tls_type, _tls_version
16
17
18class TLSPlaintext(Packet):
19    name = "TLS Plaintext"
20    fields_desc = [ ByteEnumField("type", None, _tls_type),
21                    ShortEnumField("version", None, _tls_version),
22                    FieldLenField("len", None, length_of="fragment",
23                                  fmt="!H"),
24                    StrLenField("fragment", "",
25                                length_from = lambda pkt: pkt.length) ]
26
27class TLSCompressed(TLSPlaintext):
28    name = "TLS Compressed"
29
30class TLSCiphertext(TLSPlaintext):
31    name = "TLS Ciphertext"
32
33
34def _tls_compress(alg, p):
35    """
36    Compress p (a TLSPlaintext instance) using compression algorithm instance
37    alg and return a TLSCompressed instance.
38    """
39    c = TLSCompressed()
40    c.type = p.type
41    c.version = p.version
42    c.fragment = alg.compress(p.fragment)
43    c.len = len(c.fragment)
44    return c
45
46def _tls_decompress(alg, c):
47    """
48    Decompress c (a TLSCompressed instance) using compression algorithm
49    instance alg and return a TLSPlaintext instance.
50    """
51    p = TLSPlaintext()
52    p.type = c.type
53    p.version = c.version
54    p.fragment = alg.decompress(c.fragment)
55    p.len = len(p.fragment)
56    return p
57
58def _tls_mac_add(alg, c, write_seq_num):
59    """
60    Compute the MAC using provided MAC alg instance over TLSCiphertext c using
61    current write sequence number write_seq_num. Computed MAC is then appended
62    to c.fragment and c.length is updated to reflect that change. It is the
63    caller responsability to increment the sequence number after the operation.
64    The function has no return value.
65    """
66    write_seq_num = struct.pack("!Q", write_seq_num)
67    h = alg.digest(write_seq_num + str(c))
68    c.fragment += h
69    c.len += alg.hash_len
70
71def _tls_mac_verify(alg, p, read_seq_num):
72    """
73    Verify if the MAC in provided message (message resulting from decryption
74    and padding removal) is valid. Current read sequence number is used in
75    the verification process.
76
77    If the MAC is valid:
78     - The function returns True
79     - The packet p is updated in the following way: trailing MAC value is
80       removed from p.fragment and length is updated accordingly.
81
82    In case of error, False is returned, and p may have been modified.
83
84    Also note that it is the caller's responsibility to update the read
85    sequence number after the operation.
86    """
87    h_size = alg.hash_len
88    if p.len < h_size:
89        return False
90    received_h = p.fragment[-h_size:]
91    p.len -= h_size
92    p.fragment = p.fragment[:-h_size]
93
94    read_seq_num = struct.pack("!Q", read_seq_num)
95    h = alg.digest(read_seq_num + str(p))
96    return h == received_h
97
98def _tls_add_pad(p, block_size):
99    """
100    Provided with cipher block size parameter and current TLSCompressed packet
101    p (after MAC addition), the function adds required, deterministic padding
102    to p.fragment before encryption step, as it is defined for TLS (i.e. not
103    SSL and its allowed random padding). The function has no return value.
104    """
105    padlen = block_size - ((p.len + 1) % block_size)
106    if padlen == block_size:
107        padlen =  0
108    padding = chr(padlen) * (padlen + 1)
109    p.len += len(padding)
110    p.fragment += padding
111
112def _tls_del_pad(p):
113    """
114    Provided with a just decrypted TLSCiphertext (now a TLSPlaintext instance)
115    p, the function removes the trailing padding found in p.fragment. It also
116    performs some sanity checks on the padding (length, content, ...). False
117    is returned if one of the check fails. Otherwise, True is returned,
118    indicating that p.fragment and p.len have been updated.
119    """
120
121    if p.len < 1:
122        warning("Message format is invalid (padding)")
123        return False
124
125    padlen = ord(p.fragment[-1]) + 1
126    if (p.len < padlen):
127        warning("Invalid padding length")
128        return False
129
130    if (p.fragment[-padlen:] != p.fragment[-1] * padlen):
131        warning("Padding content is invalid %s", repr(p.fragment[-padlen:]))
132        return False
133
134    p.fragment = p.fragment[:-padlen]
135    p.len -= padlen
136
137    return True
138
139def _tls_encrypt(alg, p):
140    """
141    Provided with an already MACed TLSCompressed packet, and a stream or block
142    cipher alg, the function converts it into a TLSCiphertext (i.e. encrypts it
143    and updates length). The function returns a newly created TLSCiphertext
144    instance.
145    """
146    c = TLSCiphertext()
147    c.type = p.type
148    c.version = p.version
149    c.fragment = alg.encrypt(p.fragment)
150    c.len = len(c.fragment)
151    return c
152
153def _tls_decrypt(alg, c):
154    """
155    Provided with a TLSCiphertext instance c, and a stream or block cipher alg,
156    the function decrypts c.fragment and returns a newly created TLSPlaintext.
157    """
158    p = TLSPlaintext()
159    p.type = c.type
160    p.version = c.version
161    p.fragment = alg.decrypt(c.fragment)
162    p.len = len(p.fragment)
163    return p
164
165def _tls_aead_auth_encrypt(alg, p, write_seq_num):
166    """
167    Provided with a TLSCompressed instance p, the function applies AEAD
168    cipher alg to p.fragment and builds a new TLSCiphertext instance. Unlike
169    for block and stream ciphers, for which the authentication step is done
170    separately, AEAD alg does it simultaneously: this is the reason why
171    write_seq_num is passed to the function, to be incorporated in
172    authenticated data. Note that it is the caller's responsibility to increment
173    write_seq_num afterwards.
174    """
175    P = str(p)
176    write_seq_num = struct.pack("!Q", write_seq_num)
177    A = write_seq_num + P[:5]
178
179    c = TLCCiphertext()
180    c.type = p.type
181    c.version = p.version
182    c.fragment = alg.auth_encrypt(P, A)
183    c.len = len(c.fragment)
184    return c
185
186def _tls_aead_auth_decrypt(alg, c, read_seq_num):
187    """
188    Provided with a TLSCiphertext instance c, the function applies AEAD
189    cipher alg auth_decrypt function to c.fragment (and additional data)
190    in order to authenticate the data and decrypt c.fragment. When those
191    steps succeed, the result is a newly created TLSCompressed instance.
192    On error, None is returned. Note that it is the caller's responsibility to
193    increment read_seq_num afterwards.
194    """
195    # 'Deduce' TLSCompressed length from TLSCiphertext length
196    # There is actually no guaranty of this equality, but this is defined as
197    # such in TLS 1.2 specifications, and it works for GCM and CCM at least.
198    l = p.len - alg.nonce_explicit_len - alg.tag_len
199    read_seq_num = struct.pack("!Q", read_seq_num)
200    A = read_seq_num + struct.pack('!BHH', p.type, p.version, l)
201
202    p = TLSCompressed()
203    p.type = c.type
204    p.version = c.version
205    p.len = l
206    p.fragment = alg.auth_decrypt(A, c.fragment)
207
208    if p.fragment is None: # Verification failed.
209        return None
210    return p
211
212