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.common 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 22 23class _StreamCipherMetaclass(type): 24 """ 25 Cipher classes are automatically registered through this metaclass. 26 Furthermore, their name attribute is extracted from their class name. 27 """ 28 def __new__(cls, ciph_name, bases, dct): 29 if ciph_name != "_StreamCipher": 30 dct["name"] = ciph_name[7:] # remove leading "Cipher_" 31 the_class = super(_StreamCipherMetaclass, cls).__new__(cls, ciph_name, 32 bases, dct) 33 if ciph_name != "_StreamCipher": 34 _tls_stream_cipher_algs[ciph_name[7:]] = the_class 35 return the_class 36 37 38class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)): 39 type = "stream" 40 41 def __init__(self, key=None): 42 """ 43 Note that we have to keep the encryption/decryption state in unique 44 encryptor and decryptor objects. This differs from _BlockCipher. 45 46 In order to do connection state snapshots, we need to be able to 47 recreate past cipher contexts. This is why we feed _enc_updated_with 48 and _dec_updated_with every time encrypt() or decrypt() is called. 49 """ 50 self.ready = {"key": True} 51 if key is None: 52 self.ready["key"] = False 53 if hasattr(self, "expanded_key_len"): 54 tmp_len = self.expanded_key_len 55 else: 56 tmp_len = self.key_len 57 key = b"\0" * tmp_len 58 59 # we use super() in order to avoid any deadlock with __setattr__ 60 super(_StreamCipher, self).__setattr__("key", key) 61 62 self._cipher = Cipher(self.pc_cls(key), 63 mode=None, 64 backend=default_backend()) 65 self.encryptor = self._cipher.encryptor() 66 self.decryptor = self._cipher.decryptor() 67 self._enc_updated_with = b"" 68 self._dec_updated_with = b"" 69 70 def __setattr__(self, name, val): 71 """ 72 We have to keep the encryptor/decryptor for a long time, 73 however they have to be updated every time the key is changed. 74 """ 75 if name == "key": 76 if self._cipher is not None: 77 self._cipher.algorithm.key = val 78 self.encryptor = self._cipher.encryptor() 79 self.decryptor = self._cipher.decryptor() 80 self.ready["key"] = True 81 super(_StreamCipher, self).__setattr__(name, val) 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