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