• 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"""
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