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);
    }
}