lioness/
lib.rs

1// Copyright 2016 Jeffrey Burdges and David Stainton
2
3//! Lioness wide block cipher
4
5#[macro_use]
6extern crate arrayref;
7
8extern crate chacha;
9extern crate blake2;
10extern crate keystream;
11
12#[cfg(test)]
13extern crate rand;
14#[cfg(test)]
15extern crate hex;
16
17
18use keystream::KeyStream;
19use chacha::ChaCha;
20use blake2::{VarBlake2b, digest::{Input, VariableOutput}};
21
22pub mod error;
23pub use error::LionessError;
24pub mod util;
25pub use util::{xor, xor_assign};
26
27pub const DIGEST_RESULT_SIZE: usize = 32;
28pub const DIGEST_KEY_SIZE: usize = 64;
29pub const STREAM_CIPHER_KEY_SIZE: usize = 32;
30pub const RAW_KEY_SIZE: usize = 2*STREAM_CIPHER_KEY_SIZE + 2*DIGEST_KEY_SIZE;
31const CHACHA20_NONCE_SIZE: usize = 8;
32
33/// Adapt a given `crypto::digest::Digest` to Lioness.
34pub trait DigestLioness : Input+VariableOutput {
35    fn new_digest_lioness(k: &[u8]) -> Self;
36}
37
38impl DigestLioness for VarBlake2b {
39    fn new_digest_lioness(k: &[u8]) -> Self {
40        VarBlake2b::new_keyed(k,DIGEST_RESULT_SIZE)
41    }
42}
43
44
45/// Adapt a given `crypto::symmetriccipher::KeyStream`
46/// to lioness.
47pub trait StreamCipherLioness : KeyStream {
48    fn new_streamcipher_lioness(k: &[u8; STREAM_CIPHER_KEY_SIZE]) -> Self;
49}
50
51impl StreamCipherLioness for ChaCha {
52    fn new_streamcipher_lioness(k: &[u8; STREAM_CIPHER_KEY_SIZE]) -> ChaCha {
53        // here we intentionally initialize the ChaCha20 stream cipher with
54        // an 8 byte zero nonce which is sufficient in the context of Lioness
55        ChaCha::new_chacha20(k, &[0u8;CHACHA20_NONCE_SIZE])
56    }
57}
58
59
60/// Lioness implemented generically over a Digest and StreamCipher
61pub struct Lioness<H,SC>
62where H: DigestLioness+Input+VariableOutput, 
63      SC: StreamCipherLioness+KeyStream {
64    k1: [u8; STREAM_CIPHER_KEY_SIZE],
65    k2: [u8; DIGEST_KEY_SIZE],
66    k3: [u8; STREAM_CIPHER_KEY_SIZE],
67    k4: [u8; DIGEST_KEY_SIZE],
68    h: std::marker::PhantomData<H>,
69    sc: std::marker::PhantomData<SC>,
70}
71
72impl<H,SC> Lioness<H,SC>
73where H: DigestLioness+Input+VariableOutput,
74      SC: StreamCipherLioness+KeyStream
75{
76    /// encrypt a block
77    ///
78    /// # Arguments
79    ///
80    /// * `block` - a mutable byte slice of data to encrypt
81    ///
82    /// # Errors
83    ///
84    /// * `LionessError::BlockSizeError` - returned if block size is too small
85    ///
86    /// # Example
87    ///
88    /// ```
89    /// extern crate blake2;
90    /// extern crate chacha;
91    /// extern crate lioness;
92    /// extern crate hex;
93    /// use self::lioness::{Lioness, RAW_KEY_SIZE};
94    /// use chacha::ChaCha;
95    /// use blake2::VarBlake2b;
96    /// # #[macro_use] extern crate arrayref; fn main() {
97    ///
98    /// let key = hex::decode(
99    ///     "e98e0e3f28311995e8448e6dc1de73159e800c8184a7846418347f4490f063e372\
100    ///      6eebda84e02f2cc218bd6c6e9a9b801e8d8899e8f5b6dcd23bf7ca7f11641c584cd9568f045e9\
101    ///      ad92c59275f67b9bed7f02bb23e28c0b8e56fbb634d60a6d1eae7145e53a4442dda40ae37b2e2\
102    ///      e1f97ae495c8ce0166605d4f1ea91f139159229f208c69362095d8d8e00d7b4c9ca5603dc8b87\
103    ///      50b0eb500670858ca7983a8760be307ff3e5c05f22799cb60d7c57fe3fc8b980aa65e89e3ac0a\
104    ///      c147af7deb").unwrap();
105    ///
106    /// const PLAINTEXT: &'static [u8] = b"Open, secure and reliable
107    /// connectivity is necessary (although not sufficient) to
108    /// excercise the human rights such as freedom of expression and
109    /// freedom of association [FOC], as defined in the Universal
110    /// Declaration of Human Rights [UDHR]. The purpose of the
111    /// Internet to be a global network of networks that provides
112    /// unfettered connectivity to all users and for any content
113    /// [RFC1958]. This objective of stimulating global connectivity
114    /// contributes to the Internet's role as an enabler of human
115    /// rights.";
116    ///
117    /// let mut block: Vec<u8> = PLAINTEXT.to_owned();
118    /// let cipher = Lioness::<VarBlake2b,ChaCha>::new_raw(array_ref!(key, 0, RAW_KEY_SIZE));
119    /// cipher.encrypt(&mut block).unwrap();
120    /// }
121    /// ```
122    pub fn encrypt(&self, block: &mut [u8]) -> Result<(), LionessError> {
123        debug_assert!(DIGEST_RESULT_SIZE == STREAM_CIPHER_KEY_SIZE);
124        // let mut hr = [0u8; DIGEST_RESULT_SIZE];
125        let mut k = [0u8; STREAM_CIPHER_KEY_SIZE];
126        let keylen = std::mem::size_of_val(&k);
127        debug_assert!(keylen == 32);
128
129        let blocklen = block.len();
130        if blocklen <= keylen {
131            return Err(LionessError::BlockSizeError)
132        }
133
134        let (left,right) : (&mut [u8],&mut [u8]) = block.split_at_mut(keylen);
135
136        // R = R ^ S(L ^ K1)
137        xor(left, &self.k1, &mut k);
138        let mut sc = SC::new_streamcipher_lioness(&k);
139        sc.xor_read(right);
140
141        // L = L ^ H(K2, R)
142        let mut h = H::new_digest_lioness(&self.k2);
143        h.input(&mut *right);
144        h.variable_result(|hr| xor_assign(left,&hr));
145
146        // R = R ^ S(L ^ K3)
147        xor(left, &self.k3, &mut k);
148        let mut sc = SC::new_streamcipher_lioness(&k);
149        sc.xor_read(right);
150
151        // L = L ^ H(K4, R)
152        let mut h = H::new_digest_lioness(&self.k4);
153        h.input(&mut *right);
154        h.variable_result(|hr| xor_assign(left,&hr));
155
156        Ok(())
157    }
158
159    /// decrypt a block
160    ///
161    /// # Arguments
162    ///
163    /// * `block` - a mutable byte slice of data to decrypt
164    ///
165    /// # Errors
166    ///
167    /// * `LionessError::BlockSizeError` - returned if block size is too small
168    ///
169    pub fn decrypt(&self, block: &mut [u8]) -> Result<(), LionessError> {
170        debug_assert!(DIGEST_RESULT_SIZE == STREAM_CIPHER_KEY_SIZE);
171        // let mut hr = [0u8; DIGEST_RESULT_SIZE];
172        let mut k = [0u8; STREAM_CIPHER_KEY_SIZE];
173        let keylen = std::mem::size_of_val(&k);
174        debug_assert!(keylen == 32);
175
176        let blocklen = block.len();
177	    if blocklen <= keylen {
178            return Err(LionessError::BlockSizeError)
179        }
180
181        let (left,right) : (&mut [u8],&mut [u8]) = block.split_at_mut(keylen);
182
183        // L = L ^ H(K4, R)
184        let mut h = H::new_digest_lioness(&self.k4);
185        h.input(&mut *right);
186        h.variable_result(|hr| xor_assign(left,&hr));
187
188        // R = R ^ S(L ^ K3)
189        xor(left, &self.k3, &mut k);
190        let mut sc = SC::new_streamcipher_lioness(&k);
191        sc.xor_read(right);
192
193        // L = L ^ H(K2, R)
194        let mut h = H::new_digest_lioness(&self.k2);
195        h.input(&mut *right);
196        h.variable_result(|hr| xor_assign(left,&hr));
197
198        // R = R ^ S(L ^ K1)
199        xor(left, &self.k1, &mut k);
200        let mut sc = SC::new_streamcipher_lioness(&k);
201        sc.xor_read(right);
202
203        Ok(())
204    }
205
206    /// Given a key, create a new Lioness cipher
207    pub fn new_raw(key: &[u8; 2*STREAM_CIPHER_KEY_SIZE + 2*DIGEST_KEY_SIZE]) -> Lioness<H,SC> {
208        let (_k1,_k2,_k3,_k4) = array_refs![key,STREAM_CIPHER_KEY_SIZE,DIGEST_KEY_SIZE,STREAM_CIPHER_KEY_SIZE,DIGEST_KEY_SIZE];
209        Lioness {
210            k1: *_k1,
211            k2: *_k2,
212            k3: *_k3,
213            k4: *_k4,
214            h: std::marker::PhantomData,
215            sc: std::marker::PhantomData,
216        }
217    }
218}
219
220pub type LionessDefault = Lioness<VarBlake2b,ChaCha>;
221
222
223#[cfg(test)]
224mod tests {
225    use rand::prelude::*;
226    use super::*;
227
228    struct Test {
229        input: Vec<u8>,
230        output: Vec<u8>,
231        key: Vec<u8>,
232    }
233
234    #[test]
235    fn simple_encrypt_decrypt_test() {
236        const TEST_PLAINTEXT: &'static [u8] = b"Hello there world, I'm just a test string";
237        let key = thread_rng().gen_iter::<u8>().take(RAW_KEY_SIZE).collect::<Vec<u8>>();
238        let l = Lioness::<VarBlake2b,ChaCha>::new_raw(array_ref!(key,0,RAW_KEY_SIZE));
239        //let l = LionessDefault::new_raw(array_ref!(key,0,RAW_KEY_SIZE));
240        let mut v: Vec<u8> = TEST_PLAINTEXT.to_owned();
241        assert_eq!(v,TEST_PLAINTEXT);
242        l.encrypt(&mut v).unwrap();
243        assert_eq!(v.len(),TEST_PLAINTEXT.len());
244        l.decrypt(&mut v).unwrap();
245        assert_eq!(v,TEST_PLAINTEXT);
246        l.decrypt(&mut v).unwrap();
247        assert_eq!(v.len(),TEST_PLAINTEXT.len());
248        l.encrypt(&mut v).unwrap();
249        assert_eq!(v,TEST_PLAINTEXT);
250    }
251
252    fn test_cipher(tests: &[Test]) {
253        for t in tests {
254            let cipher = Lioness::<VarBlake2b,ChaCha>::new_raw(array_ref!(t.key.as_slice(), 0, RAW_KEY_SIZE));
255            let mut block: Vec<u8> = t.input.as_slice().to_owned();
256            cipher.encrypt(&mut block).unwrap();
257            let want: Vec<u8> = t.output.as_slice().to_owned();
258            assert_eq!(want, block)
259        }
260    }
261
262    #[test]
263    fn chach20_blake2b_lioness_vectors_test() {
264        let key = hex::decode(
265            "0f2c69732932c99e56fa50fbb2763ad77ee221fc5d9e6c08f89fc577a7467f1ee34003440ee2bfbf\
266             aac60912b0e547fbe9a6a9292db70bc718c6f2773ab198ac8f255378f7ea799e1d4b8596079173b6\
267             e443c416f13195f1976acc03d53a4b8581b609df3b7029d5b487051d5ae4189129c045edc8822e1f\
268             52e30251e4b322b3f6d6e8bb0ddb0578dcba41603abf5e51848c84d2082d293f30a645faf4df028e\
269             e2c40853ea33e40b55fca902371dc00dc1e0e77161bd097a59e8368bf99174d9").unwrap();
270        let input = hex::decode(
271            "5ac4cb9674a8908915a2b1aaf2043271612531911a26186bd5c811951330753a0e3259f3fcf52f\
272             b86e666ab4d96243e57d73b976611f928d44ad137799016861576ca0a6b8a8a9e2ea02db71e71c\
273             9654e476ca845c44456eba62f11f004b222756e485185c446c30b7a05cf9acd53b3131227b428d\
274             a5c9601664d45ae5c2387956307961a0680894844190605dce0c86e597105884e151eb8a005eda\
275             08ff5891a6b40bae299cddad979063a9a356f3477feabb9cc7bd80a1e2d6a419fcd8af9e98f7b1\
276             93c71bd6056d7634b8c2b8f85920f314554104659e52d9266ddbc2ac40c1b875f6b00225f832cf\
277             310e139ad8cc2568608f0323534fa15a84280e776e7e1167a001f6e18c49f3cd02c19837da47ac\
278             091219ee2fdb4458836db20cbd362bb65add9b40f2817f666caf19787abc2013737eea8c7552d7\
279             55a29beba5da31956f75fe7628221fe8d0a75da5bee39af956a2246c5a339560dcf029eb76d191\
280             963354b70142df29ec69930977ce2f0e491513664ce83a8fa75f3e698530cf9dafbdb90b19745e\
281             9257d03d7320c6d306f529eda242cb3f6f452a943f6e1c04eb02cbb0368d79e49a2b42ac3ff7cd\
282             9a5686bfdb90a29322016bbcef5c733f451a9f4ea7c534116158eb611796d47b83ffe7cd6e6c11\
283             d56e2d26c7a386853212a2f92efeabc74e8fe69e3d374d7b033d0ec9862221435b14ad534217ad\
284             7da50bc236").unwrap();
285        let output = hex::decode(
286            "9eb45ca2ca4d0b6ff05a749511aad1357aa64caf9ce547c7388fe24fd1300fe856bb5c396869a\
287             cd21c45805e6a7c8a1b7f71cc5f0ea9dd0c4ecd4bba9a7a4853bc352bc9f6562e9907973f91fb\
288             cf7c710f5a89abc8eb4489b90e8111cbf85ffd595d603268ddceb40e39e747a4e7bd5c965585b\
289             6964e180bd6ccb9d0fad210c7f7dd6f90cf6db9bda70d41d3922cedec5ea147ef318de5f34e6f\
290             e5bd646859a9d4171b973b6b58c8d7f94bc9eb293c197f3408a51e3626196e3f6bca625cef90f\
291             a7a3e3713bdaebdda82f48db1a97c9ed5c48bc419dbc3d1f9ef43d1b17dd83c966bde9d9360b7\
292             cdac0871844c27921dcf3bb7edce9fb24661a41a8f92c8502925f062e9cd2f77c561e5825eae2\
293             11657652330bc64cd63b18d1014975f167f8b68d6e702dd3d3547971662238216cc5b07517cc9\
294             0aaa49a61ee423861cdc49c0e1f64e086007095a00f8adb0314fd85c88158001202edf2ed43c2\
295             01176d6141e469dd89430352a927ee22a41c62c8cfdfd5d592e76793e58a9c63b7fe6dad335d7\
296             acec90727675854d7708358115794e013bb4fdb504c44e21ce500f764fac211e8de20b81ca55f\
297             c778ace024d2a40045241e71b023ceb519c8c28285c333b9f90f5e2cde21ca6744e43f89d0054\
298             5dd34df072c7214f6cbd2123c4b0613614609961dd855d6d611c3018e4df3550b4e93f33f7c3e\
299             8b2c890ca0405c957aa277d").unwrap();
300
301        test_cipher(&[ Test {key,input,output} ]);
302    }
303} // tests