Repository: google/OpenSK
File: src/ctap/crypto_wrapper.rs (Rust)

Code before:
// Copyright 2021 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 crate::ctap::status_code::Ctap2StatusCode;
use alloc::vec;
use alloc::vec::Vec;
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
use crypto::rng256::Rng256;

/// Wraps the AES256-CBC encryption to match what we need in CTAP.
pub fn aes256_cbc_encrypt(
    rng: &mut dyn Rng256,
    aes_enc_key: &crypto::aes256::EncryptionKey,
    plaintext: &[u8],
    has_iv: bool,
) -> Result<Vec<u8>, Ctap2StatusCode> {
    if plaintext.len() % 16 != 0 {
        return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
    }
    let iv = if has_iv {
        let random_bytes = rng.gen_uniform_u8x32();
        *array_ref!(random_bytes, 0, 16)
    } else {
        [0u8; 16]
    };
    let mut blocks = Vec::with_capacity(plaintext.len() / 16);
    // TODO(https://github.com/rust-lang/rust/issues/74985) Use array_chunks when stable.
    for block in plaintext.chunks_exact(16) {
        blocks.push(*array_ref!(block, 0, 16));
    }
    cbc_encrypt(aes_enc_key, iv, &mut blocks);
    let mut ciphertext = if has_iv { iv.to_vec() } else { vec![] };
    ciphertext.extend(blocks.iter().flatten());
    Ok(ciphertext)
}

/// Wraps the AES256-CBC decryption to match what we need in CTAP.
pub fn aes256_cbc_decrypt(
    aes_enc_key: &crypto::aes256::EncryptionKey,
    ciphertext: &[u8],

Review comment (refactor):
CBC always need an IV. So this boolean is actually NULL IV vs. random IV.
Why don't we create the IV in the caller and pass it as an arg? The API would be easier to understand.
It would also make the API more consistent with `aes256_cbc_decrypt`.

That being said, when we'll move to protected implementations, this abstraction will likely change.

Code after:
// Copyright 2021 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 crate::ctap::status_code::Ctap2StatusCode;
use alloc::vec;
use alloc::vec::Vec;
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
use crypto::rng256::Rng256;

/// Wraps the AES256-CBC encryption to match what we need in CTAP.
pub fn aes256_cbc_encrypt(
    rng: &mut dyn Rng256,
    aes_enc_key: &crypto::aes256::EncryptionKey,
    plaintext: &[u8],
    embeds_iv: bool,
) -> Result<Vec<u8>, Ctap2StatusCode> {
    if plaintext.len() % 16 != 0 {
        return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
    }
    let iv = if embeds_iv {
        let random_bytes = rng.gen_uniform_u8x32();
        *array_ref!(random_bytes, 0, 16)
    } else {
        [0u8; 16]
    };
    let mut blocks = Vec::with_capacity(plaintext.len() / 16);
    // TODO(https://github.com/rust-lang/rust/issues/74985) Use array_chunks when stable.
    for block in plaintext.chunks_exact(16) {
        blocks.push(*array_ref!(block, 0, 16));
    }
    cbc_encrypt(aes_enc_key, iv, &mut blocks);
    let mut ciphertext = if embeds_iv { iv.to_vec() } else { vec![] };
    ciphertext.extend(blocks.iter().flatten());
    Ok(ciphertext)
}

/// Wraps the AES256-CBC decryption to match what we need in CTAP.
pub fn aes256_cbc_decrypt(
    aes_enc_key: &crypto::aes256::EncryptionKey,
    ciphertext: &[u8],