1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
//! Stream ciphers

use crypto::cipher::{CipherType, CipherCategory, CipherResult};
use crypto::openssl;
use crypto::table;
use crypto::CryptoMode;
use crypto::rc4_md5;
use crypto::dummy;
use crypto::crypto::CryptoCipher;

use bytes::BufMut;

/// Basic operation of Cipher, which is a Symmetric Cipher.
///
/// The `update` method could be called multiple times, and the `finalize` method will
/// encrypt the last block
pub trait StreamCipher {
    fn update<B: BufMut>(&mut self, data: &[u8], out: &mut B) -> CipherResult<()>;
    fn finalize<B: BufMut>(&mut self, out: &mut B) -> CipherResult<()>;
    fn buffer_size(&self, data: &[u8]) -> usize;
}

macro_rules! define_stream_ciphers {
    ($($name:ident => $cipher:ty,)+) => {
        /// Variant cipher which contains all possible ciphers
        pub enum StreamCipherVariant {
            $(
                $name($cipher),
            )+
        }

        impl StreamCipherVariant {
            /// Creates from an actual cipher
            pub fn new<C>(cipher: C) -> StreamCipherVariant
                where StreamCipherVariant: From<C>
            {
                From::from(cipher)
            }
        }

        impl StreamCipher for StreamCipherVariant {
            fn update<B: BufMut>(&mut self, data: &[u8], out: &mut B) -> CipherResult<()> {
                match *self {
                    $(
                        StreamCipherVariant::$name(ref mut cipher) => cipher.update(data, out),
                    )+
                }
            }

            fn finalize<B: BufMut>(&mut self, out: &mut B) -> CipherResult<()> {
                match *self {
                    $(
                        StreamCipherVariant::$name(ref mut cipher) => cipher.finalize(out),
                    )+
                }
            }

            fn buffer_size(&self, data: &[u8]) -> usize {
                match *self {
                    $(
                        StreamCipherVariant::$name(ref cipher) => cipher.buffer_size(data),
                    )+
                }
            }
        }

        $(
            impl From<$cipher> for StreamCipherVariant {
                fn from(cipher: $cipher) -> StreamCipherVariant {
                    StreamCipherVariant::$name(cipher)
                }
            }
        )+
    }
}

define_stream_ciphers! {
    TableCipher => table::TableCipher,
    DummyCipher => dummy::DummyCipher,
    Rc4Md5Cipher => rc4_md5::Rc4Md5Cipher,
    OpenSSLCipher => openssl::OpenSSLCipher,
    CryptoCipher => CryptoCipher,
}

/// Generate a specific Cipher with key and initialize vector
pub fn new_stream(t: CipherType, key: &[u8], iv: &[u8], mode: CryptoMode) -> StreamCipherVariant {
    assert!(t.category() == CipherCategory::Stream,
            "only allow initializing with stream cipher");

    match t {
        CipherType::Table => StreamCipherVariant::new(table::TableCipher::new(key, mode)),
        CipherType::Dummy => StreamCipherVariant::new(dummy::DummyCipher),

        CipherType::ChaCha20 |
        CipherType::Salsa20 => StreamCipherVariant::new(CryptoCipher::new(t, key, iv)),

        CipherType::Rc4Md5 => StreamCipherVariant::new(rc4_md5::Rc4Md5Cipher::new(key, iv, mode)),

        _ => StreamCipherVariant::new(openssl::OpenSSLCipher::new(t, key, iv, mode)),
    }
}