Home | History | Annotate | Download | only in crypto
      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 """
      7 Stream ciphers.
      8 """
      9 
     10 from __future__ import absolute_import
     11 from scapy.config import conf
     12 from scapy.layers.tls.crypto.ciphers import CipherError
     13 import scapy.modules.six as six
     14 
     15 if 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 class _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 
     37 class _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 
    105 if 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 
    115 class 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