Repository: google/OpenSK
File: libraries/crypto/src/cbc.rs (Rust)

Code before:
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::util::{xor_block_16, Block16};
use crate::aes256::{DecryptionKey, EncryptionKey};
use core::convert::TryInto;

pub fn cbc_encrypt(key: &EncryptionKey, mut iv: Block16, blocks: &mut [u8]) {
    for block in blocks.chunks_mut(16) {
        let block: &mut Block16 = block.try_into().unwrap();
        xor_block_16(block, &iv);
        key.encrypt_block(block);
        iv = *block;
    }
}

pub fn cbc_decrypt(key: &DecryptionKey, mut iv: Block16, blocks: &mut [u8]) {
    for block in blocks.chunks_mut(16) {
        let block: &mut Block16 = block.try_into().unwrap();
        let tmp = *block;
        key.decrypt_block(block);
        xor_block_16(block, &iv);
        iv = tmp;
    }
}

#[cfg(test)]
mod test {
    use super::super::aes256;
    use super::*;

    #[test]
    fn test_cbc_encrypt_decrypt() {
        // Test that cbc_decrypt is the inverse of cbc_encrypt for a bunch of block values.
        let enc_key = aes256::EncryptionKey::new(&[
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
            0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
            0x1c, 0x1d, 0x1e, 0x1f,
        ]);
        let dec_key = aes256::DecryptionKey::new(&enc_key);

Review comment (suggestion):
It's a bit unfortunate that the new API potentially adds runtime checks rather than checking lengths at compile time. At least Rust nightly now has `slice::array_chunks` which would be useful here (https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks).

Code after:
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::util::{xor_block_16, Block16};
use crate::aes256::{DecryptionKey, EncryptionKey};
use core::convert::TryInto;

/// Encrypts a byte slice.
///
/// # Panics
///
/// Panics if the byte slice is not a multiple of the block size (16 bytes).
pub fn cbc_encrypt(key: &EncryptionKey, mut iv: Block16, bytes: &mut [u8]) {
    for block in bytes.chunks_mut(16) {
        let block: &mut Block16 = block.try_into().unwrap();
        xor_block_16(block, &iv);
        key.encrypt_block(block);
        iv = *block;
    }
}

/// Decrypts a byte slice.
///
/// # Panics
///
/// Panics if the byte slice is not a multiple of the block size (16 bytes).
pub fn cbc_decrypt(key: &DecryptionKey, mut iv: Block16, bytes: &mut [u8]) {
    for block in bytes.chunks_mut(16) {
        let block: &mut Block16 = block.try_into().unwrap();
        let tmp = *block;
        key.decrypt_block(block);
        xor_block_16(block, &iv);
        iv = tmp;
    }
}

#[cfg(test)]
mod test {
    use super::super::aes256;
    use super::*;