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""" 8Block ciphers. 9""" 10 11import warnings 12 13from scapy.config import conf 14from scapy.layers.tls.crypto.common import CipherError 15 16if conf.crypto_valid: 17 from cryptography.utils import ( 18 CryptographyDeprecationWarning, 19 ) 20 from cryptography.hazmat.primitives.ciphers import ( 21 BlockCipherAlgorithm, 22 Cipher, 23 CipherAlgorithm, 24 algorithms, 25 modes, 26 ) 27 from cryptography.hazmat.backends.openssl.backend import backend 28 try: 29 # cryptography > 43.0 30 from cryptography.hazmat.decrepit.ciphers import ( 31 algorithms as decrepit_algorithms, 32 ) 33 except ImportError: 34 decrepit_algorithms = algorithms 35 36 37_tls_block_cipher_algs = {} 38 39 40class _BlockCipherMetaclass(type): 41 """ 42 Cipher classes are automatically registered through this metaclass. 43 Furthermore, their name attribute is extracted from their class name. 44 """ 45 def __new__(cls, ciph_name, bases, dct): 46 if ciph_name != "_BlockCipher": 47 dct["name"] = ciph_name[7:] # remove leading "Cipher_" 48 the_class = super(_BlockCipherMetaclass, cls).__new__(cls, ciph_name, 49 bases, dct) 50 if ciph_name != "_BlockCipher": 51 _tls_block_cipher_algs[ciph_name[7:]] = the_class 52 return the_class 53 54 55class _BlockCipher(metaclass=_BlockCipherMetaclass): 56 type = "block" 57 58 def __init__(self, key=None, iv=None): 59 self.ready = {"key": True, "iv": True} 60 if key is None: 61 self.ready["key"] = False 62 if hasattr(self, "expanded_key_len"): 63 key_len = self.expanded_key_len 64 else: 65 key_len = self.key_len 66 key = b"\0" * key_len 67 if not iv: 68 self.ready["iv"] = False 69 iv = b"\0" * self.block_size 70 71 # we use super() in order to avoid any deadlock with __setattr__ 72 super(_BlockCipher, self).__setattr__("key", key) 73 super(_BlockCipher, self).__setattr__("iv", iv) 74 75 self._cipher = Cipher(self.pc_cls(key), 76 self.pc_cls_mode(iv), 77 backend=backend) 78 79 def __setattr__(self, name, val): 80 if name == "key": 81 if self._cipher is not None: 82 self._cipher.algorithm.key = val 83 self.ready["key"] = True 84 elif name == "iv": 85 if self._cipher is not None: 86 self._cipher.mode._initialization_vector = val 87 self.ready["iv"] = True 88 super(_BlockCipher, self).__setattr__(name, val) 89 90 def encrypt(self, data): 91 """ 92 Encrypt the data. Also, update the cipher iv. This is needed for SSLv3 93 and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.post_build(). 94 """ 95 if False in self.ready.values(): 96 raise CipherError(data) 97 encryptor = self._cipher.encryptor() 98 tmp = encryptor.update(data) + encryptor.finalize() 99 self.iv = tmp[-self.block_size:] 100 return tmp 101 102 def decrypt(self, data): 103 """ 104 Decrypt the data. Also, update the cipher iv. This is needed for SSLv3 105 and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.pre_dissect(). 106 If we lack the key, we raise a CipherError which contains the input. 107 """ 108 if False in self.ready.values(): 109 raise CipherError(data) 110 decryptor = self._cipher.decryptor() 111 tmp = decryptor.update(data) + decryptor.finalize() 112 self.iv = data[-self.block_size:] 113 return tmp 114 115 def snapshot(self): 116 c = self.__class__(self.key, self.iv) 117 c.ready = self.ready.copy() 118 return c 119 120 121if conf.crypto_valid: 122 class Cipher_AES_128_CBC(_BlockCipher): 123 pc_cls = algorithms.AES 124 pc_cls_mode = modes.CBC 125 block_size = 16 126 key_len = 16 127 128 class Cipher_AES_256_CBC(Cipher_AES_128_CBC): 129 key_len = 32 130 131 class Cipher_CAMELLIA_128_CBC(_BlockCipher): 132 pc_cls = algorithms.Camellia 133 pc_cls_mode = modes.CBC 134 block_size = 16 135 key_len = 16 136 137 class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC): 138 key_len = 32 139 140 141# Mostly deprecated ciphers 142 143_sslv2_block_cipher_algs = {} 144 145if conf.crypto_valid: 146 class Cipher_DES_CBC(_BlockCipher): 147 pc_cls = decrepit_algorithms.TripleDES 148 pc_cls_mode = modes.CBC 149 block_size = 8 150 key_len = 8 151 152 class Cipher_DES40_CBC(Cipher_DES_CBC): 153 """ 154 This is an export cipher example. The key length has been weakened to 5 155 random bytes (i.e. 5 bytes will be extracted from the master_secret). 156 Yet, we still need to know the original length which will actually be 157 fed into the encryption algorithm. This is what expanded_key_len 158 is for, and it gets used in PRF.postprocess_key_for_export(). 159 We never define this attribute with non-export ciphers. 160 """ 161 expanded_key_len = 8 162 key_len = 5 163 164 class Cipher_3DES_EDE_CBC(_BlockCipher): 165 pc_cls = decrepit_algorithms.TripleDES 166 pc_cls_mode = modes.CBC 167 block_size = 8 168 key_len = 24 169 170 _sslv2_block_cipher_algs["DES_192_EDE3_CBC"] = Cipher_3DES_EDE_CBC 171 172 try: 173 with warnings.catch_warnings(): 174 # Hide deprecation warnings 175 warnings.filterwarnings("ignore", 176 category=CryptographyDeprecationWarning) 177 178 class Cipher_IDEA_CBC(_BlockCipher): 179 pc_cls = decrepit_algorithms.IDEA 180 pc_cls_mode = modes.CBC 181 block_size = 8 182 key_len = 16 183 184 class Cipher_SEED_CBC(_BlockCipher): 185 pc_cls = decrepit_algorithms.SEED 186 pc_cls_mode = modes.CBC 187 block_size = 16 188 key_len = 16 189 190 _sslv2_block_cipher_algs.update({ 191 "IDEA_128_CBC": Cipher_IDEA_CBC, 192 "DES_64_CBC": Cipher_DES_CBC, 193 }) 194 except AttributeError: 195 pass 196 197 198# We need some black magic for RC2, which is not registered by default 199# to the openssl backend of the cryptography library. 200# If the current version of openssl does not support rc2, the RC2 ciphers are 201# silently not declared, and the corresponding suites will have 'usable' False. 202 203if conf.crypto_valid: 204 try: 205 from cryptography.hazmat.decrepit.ciphers.algorithms import RC2 206 rc2_available = backend.cipher_supported( 207 RC2(b"0" * 16), modes.CBC(b"0" * 8) 208 ) 209 except ImportError: 210 # Legacy path for cryptography < 43.0.0 211 from cryptography.hazmat.backends.openssl.backend import ( 212 GetCipherByName 213 ) 214 _gcbn_format = "{cipher.name}-{mode.name}" 215 216 class RC2(BlockCipherAlgorithm, CipherAlgorithm): 217 name = "RC2" 218 block_size = 64 219 key_sizes = frozenset([128]) 220 221 def __init__(self, key): 222 self.key = algorithms._verify_key_size(self, key) 223 224 @property 225 def key_size(self): 226 return len(self.key) * 8 227 if GetCipherByName(_gcbn_format)(backend, RC2, modes.CBC) != \ 228 backend._ffi.NULL: 229 rc2_available = True 230 backend.register_cipher_adapter(RC2, 231 modes.CBC, 232 GetCipherByName(_gcbn_format)) 233 else: 234 rc2_available = False 235 236 if rc2_available: 237 class Cipher_RC2_CBC(_BlockCipher): 238 pc_cls = RC2 239 pc_cls_mode = modes.CBC 240 block_size = 8 241 key_len = 16 242 243 class Cipher_RC2_CBC_40(Cipher_RC2_CBC): 244 expanded_key_len = 16 245 key_len = 5 246 247 _sslv2_block_cipher_algs["RC2_128_CBC"] = Cipher_RC2_CBC 248 249 250_tls_block_cipher_algs.update(_sslv2_block_cipher_algs) 251