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 """ 7 TLS helpers, provided as out-of-context methods. 8 """ 9 10 from scapy.error import warning 11 from scapy.fields import (ByteEnumField, ShortEnumField, 12 FieldLenField, StrLenField) 13 from scapy.packet import Packet 14 15 from scapy.layers.tls.basefields import _tls_type, _tls_version 16 17 18 class 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 27 class TLSCompressed(TLSPlaintext): 28 name = "TLS Compressed" 29 30 class TLSCiphertext(TLSPlaintext): 31 name = "TLS Ciphertext" 32 33 34 def _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 46 def _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 58 def _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 71 def _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 98 def _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 112 def _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 139 def _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 153 def _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 165 def _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 186 def _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