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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! A Rust implementation of the Cipher Block Chaining (CBC) mode of the SPECK cipher. //! //! Don't use this unless you know what you are doing, as practical attacks exist against //! CBC mode in certain cases. //! //! # Example //! //! ```rust //! # extern crate rand; //! # extern crate pkcs7; //! # extern crate speck_cbc; //! # extern crate byteorder; //! # //! # fn example() -> std::io::Result<()> { //! use rand::{Rng, OsRng}; //! use byteorder::NetworkEndian; //! //! // This should probably be derived from an exchanged key or a password. //! let key = [0u8; speck_cbc::BLOCK_SIZE]; //! //! let input = b"This is a test."; //! let mut buffer: Vec<u8> = input.to_vec(); //! //! // Sender. //! let mut iv = [0u8; speck_cbc::BLOCK_SIZE]; //! OsRng::new()?.fill_bytes(&mut iv); //! pkcs7::pad(&mut buffer, speck_cbc::BLOCK_SIZE as u8); //! speck_cbc::encrypt::<NetworkEndian>(&mut buffer, &key, &iv); //! //! // Message authenticity needs to be provided between `encrypt` and `decrypt`. //! //! // Receiver. //! speck_cbc::decrypt::<NetworkEndian>(&mut buffer, &key, &iv); //! pkcs7::un_pad(&mut buffer); //! //! assert_eq!(buffer.as_slice(), input); //! # Ok(()) //! # } //! # fn main() { example().unwrap() } //! ``` #![feature(i128_type, const_fn)] #![no_std] #![forbid(unsafe_code)] extern crate byteorder; extern crate speck; use speck::Key; use byteorder::ByteOrder; /// The size of SPECK's blocks and keys. pub const BLOCK_SIZE: usize = core::mem::size_of::<u128>(); /// Encrypt data with SPECK-CBC. /// /// `Endian` is the endianness of `key`, `iv`, and the blocks. /// /// `key` and `iv`'s length must be `BLOCK_SIZE`. /// /// `buffer`'s length must be a multiple of `BLOCK_SIZE`. pub fn encrypt<Endian: ByteOrder>(buffer: &mut [u8], key: &[u8], iv: &[u8]) { // Made sure that the input is padded. assert_eq!(buffer.len() % BLOCK_SIZE, 0); // Make sure that the key length is correct. assert_eq!(key.len(), BLOCK_SIZE); // Make sure that the IV length is correct. assert_eq!(iv.len(), BLOCK_SIZE); // Precompute the key schedule. let key_schedule = Key::new(Endian::read_u128(key)); // Keep the last ciphertext around to avoid a possible byte swap. let mut last_block_ciphertext = 0; for (block_index, block_buffer) in buffer.chunks_mut(BLOCK_SIZE).enumerate() { // Read block plaintext from input. let block_plaintext = Endian::read_u128(block_buffer); // XOR the plaintext with the appropriate data. let block_xor = block_plaintext ^ if block_index == 0 { Endian::read_u128(iv) } else { last_block_ciphertext }; // Perform the actual encryption. let block_ciphertext = key_schedule.encrypt_block(block_xor); // Update the last ciphertext. last_block_ciphertext = block_ciphertext; // Write the encrypted block to output. Endian::write_u128(block_buffer, block_ciphertext); } } /// Decrypt data with SPECK-CBC. /// /// `Endian` is the endianness of `key`, `iv`, and the blocks. /// /// `key` and `iv`'s length must be `BLOCK_SIZE`. /// /// `buffer`'s length must be a multiple of `BLOCK_SIZE`. pub fn decrypt<Endian: ByteOrder>(buffer: &mut [u8], key: &[u8], iv: &[u8]) { // Made sure that the input is padded. assert_eq!(buffer.len() % BLOCK_SIZE, 0); // Make sure that the key length is correct. assert_eq!(key.len(), BLOCK_SIZE); // Make sure that the IV length is correct. assert_eq!(iv.len(), BLOCK_SIZE); // Precompute the key schedule. let key_schedule = Key::new(Endian::read_u128(key)); // Keep the last ciphertext around to avoid a possible byte swap. let mut last_block_ciphertext = 0; for (block_index, block_buffer) in buffer.chunks_mut(BLOCK_SIZE).enumerate() { // Read block ciphertext from input. let block_ciphertext = Endian::read_u128(block_buffer); // Perform the actual decryption. let block_xor = key_schedule.decrypt_block(block_ciphertext); // XOR the plaintext with the appropriate data. let block_plaintext = block_xor ^ if block_index == 0 { Endian::read_u128(iv) } else { last_block_ciphertext }; // Update the last ciphertext. last_block_ciphertext = block_ciphertext; // Write the decrypted block to output. Endian::write_u128(block_buffer, block_plaintext); } }