symmetriccipher/
symmetriccipher.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7extern crate crypto;
8extern crate rand;
9
10use crypto::{ symmetriccipher, buffer, aes, blockmodes };
11use crypto::buffer::{ ReadBuffer, WriteBuffer, BufferResult };
12
13use rand::{ Rng, OsRng };
14
15// Encrypt a buffer with the given key and iv using
16// AES-256/CBC/Pkcs encryption.
17fn encrypt(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
18
19    // Create an encryptor instance of the best performing
20    // type available for the platform.
21    let mut encryptor = aes::cbc_encryptor(
22            aes::KeySize::KeySize256,
23            key,
24            iv,
25            blockmodes::PkcsPadding);
26
27    // Each encryption operation encrypts some data from
28    // an input buffer into an output buffer. Those buffers
29    // must be instances of RefReaderBuffer and RefWriteBuffer
30    // (respectively) which keep track of how much data has been
31    // read from or written to them.
32    let mut final_result = Vec::<u8>::new();
33    let mut read_buffer = buffer::RefReadBuffer::new(data);
34    let mut buffer = [0; 4096];
35    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
36
37    // Each encryption operation will "make progress". "Making progress"
38    // is a bit loosely defined, but basically, at the end of each operation
39    // either BufferUnderflow or BufferOverflow will be returned (unless
40    // there was an error). If the return value is BufferUnderflow, it means
41    // that the operation ended while wanting more input data. If the return
42    // value is BufferOverflow, it means that the operation ended because it
43    // needed more space to output data. As long as the next call to the encryption
44    // operation provides the space that was requested (either more input data
45    // or more output space), the operation is guaranteed to get closer to
46    // completing the full operation - ie: "make progress".
47    //
48    // Here, we pass the data to encrypt to the enryptor along with a fixed-size
49    // output buffer. The 'true' flag indicates that the end of the data that
50    // is to be encrypted is included in the input buffer (which is true, since
51    // the input data includes all the data to encrypt). After each call, we copy
52    // any output data to our result Vec. If we get a BufferOverflow, we keep
53    // going in the loop since it means that there is more work to do. We can
54    // complete as soon as we get a BufferUnderflow since the encryptor is telling
55    // us that it stopped processing data due to not having any more data in the
56    // input buffer.
57    loop {
58        let result = try!(encryptor.encrypt(&mut read_buffer, &mut write_buffer, true));
59
60        // "write_buffer.take_read_buffer().take_remaining()" means:
61        // from the writable buffer, create a new readable buffer which
62        // contains all data that has been written, and then access all
63        // of that data as a slice.
64        final_result.extend(write_buffer.take_read_buffer().take_remaining().iter().map(|&i| i));
65
66        match result {
67            BufferResult::BufferUnderflow => break,
68            BufferResult::BufferOverflow => { }
69        }
70    }
71
72    Ok(final_result)
73}
74
75// Decrypts a buffer with the given key and iv using
76// AES-256/CBC/Pkcs encryption.
77//
78// This function is very similar to encrypt(), so, please reference
79// comments in that function. In non-example code, if desired, it is possible to
80// share much of the implementation using closures to hide the operation
81// being performed. However, such code would make this example less clear.
82fn decrypt(encrypted_data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
83    let mut decryptor = aes::cbc_decryptor(
84            aes::KeySize::KeySize256,
85            key,
86            iv,
87            blockmodes::PkcsPadding);
88
89    let mut final_result = Vec::<u8>::new();
90    let mut read_buffer = buffer::RefReadBuffer::new(encrypted_data);
91    let mut buffer = [0; 4096];
92    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
93
94    loop {
95        let result = try!(decryptor.decrypt(&mut read_buffer, &mut write_buffer, true));
96        final_result.extend(write_buffer.take_read_buffer().take_remaining().iter().map(|&i| i));
97        match result {
98            BufferResult::BufferUnderflow => break,
99            BufferResult::BufferOverflow => { }
100        }
101    }
102
103    Ok(final_result)
104}
105
106fn main() {
107    let message = "Hello World!";
108
109    let mut key: [u8; 32] = [0; 32];
110    let mut iv: [u8; 16] = [0; 16];
111
112    // In a real program, the key and iv may be determined
113    // using some other mechanism. If a password is to be used
114    // as a key, an algorithm like PBKDF2, Bcrypt, or Scrypt (all
115    // supported by Rust-Crypto!) would be a good choice to derive
116    // a password. For the purposes of this example, the key and
117    // iv are just random values.
118    let mut rng = OsRng::new().ok().unwrap();
119    rng.fill_bytes(&mut key);
120    rng.fill_bytes(&mut iv);
121
122    let encrypted_data = encrypt(message.as_bytes(), &key, &iv).ok().unwrap();
123    let decrypted_data = decrypt(&encrypted_data[..], &key, &iv).ok().unwrap();
124
125    assert!(message.as_bytes() == &decrypted_data[..]);
126}