• 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"""
7Stream ciphers.
8"""
9
10from __future__ import absolute_import
11from scapy.config import conf
12from scapy.layers.tls.crypto.ciphers import CipherError
13import scapy.modules.six as six
14
15if conf.crypto_valid:
16    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
17    from cryptography.hazmat.backends import default_backend
18
19
20_tls_stream_cipher_algs = {}
21
22class _StreamCipherMetaclass(type):
23    """
24    Cipher classes are automatically registered through this metaclass.
25    Furthermore, their name attribute is extracted from their class name.
26    """
27    def __new__(cls, ciph_name, bases, dct):
28        if ciph_name != "_StreamCipher":
29            dct["name"] = ciph_name[7:]     # remove leading "Cipher_"
30        the_class = super(_StreamCipherMetaclass, cls).__new__(cls, ciph_name,
31                                                               bases, dct)
32        if ciph_name != "_StreamCipher":
33            _tls_stream_cipher_algs[ciph_name[7:]] = the_class
34        return the_class
35
36
37class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)):
38    type = "stream"
39
40    def __init__(self, key=None):
41        """
42        Note that we have to keep the encryption/decryption state in unique
43        encryptor and decryptor objects. This differs from _BlockCipher.
44
45        In order to do connection state snapshots, we need to be able to
46        recreate past cipher contexts. This is why we feed _enc_updated_with
47        and _dec_updated_with every time encrypt() or decrypt() is called.
48        """
49        self.ready = {"key": True}
50        if key is None:
51            self.ready["key"] = False
52            if hasattr(self, "expanded_key_len"):
53                l = self.expanded_key_len
54            else:
55                l = self.key_len
56            key = b"\0" * l
57
58        # we use super() in order to avoid any deadlock with __setattr__
59        super(_StreamCipher, self).__setattr__("key", key)
60
61        self._cipher = Cipher(self.pc_cls(key),
62                              mode=None,
63                              backend=default_backend())
64        self.encryptor = self._cipher.encryptor()
65        self.decryptor = self._cipher.decryptor()
66        self._enc_updated_with = b""
67        self._dec_updated_with = b""
68
69    def __setattr__(self, name, val):
70        """
71        We have to keep the encryptor/decryptor for a long time,
72        however they have to be updated every time the key is changed.
73        """
74        if name == "key":
75            if self._cipher is not None:
76                self._cipher.algorithm.key = val
77                self.encryptor = self._cipher.encryptor()
78                self.decryptor = self._cipher.decryptor()
79            self.ready["key"] = True
80        super(_StreamCipher, self).__setattr__(name, val)
81
82
83    def encrypt(self, data):
84        if False in six.itervalues(self.ready):
85            raise CipherError(data)
86        self._enc_updated_with += data
87        return self.encryptor.update(data)
88
89    def decrypt(self, data):
90        if False in six.itervalues(self.ready):
91            raise CipherError(data)
92        self._dec_updated_with += data
93        return self.decryptor.update(data)
94
95    def snapshot(self):
96        c = self.__class__(self.key)
97        c.ready = self.ready.copy()
98        c.encryptor.update(self._enc_updated_with)
99        c.decryptor.update(self._dec_updated_with)
100        c._enc_updated_with = self._enc_updated_with
101        c._dec_updated_with = self._dec_updated_with
102        return c
103
104
105if conf.crypto_valid:
106    class Cipher_RC4_128(_StreamCipher):
107        pc_cls = algorithms.ARC4
108        key_len = 16
109
110    class Cipher_RC4_40(Cipher_RC4_128):
111        expanded_key_len = 16
112        key_len = 5
113
114
115class Cipher_NULL(_StreamCipher):
116    key_len = 0
117
118    def __init__(self, key=None):
119        self.ready = {"key": True}
120        self._cipher = None
121        # we use super() in order to avoid any deadlock with __setattr__
122        super(Cipher_NULL, self).__setattr__("key", key)
123
124    def snapshot(self):
125        c = self.__class__(self.key)
126        c.ready = self.ready.copy()
127        return c
128
129    def encrypt(self, data):
130        return data
131
132    def decrypt(self, data):
133        return data
134
135