Skip to main content

parsec_service/providers/cryptoauthlib/
cipher.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use super::Provider;
4use crate::authenticators::ApplicationIdentity;
5use crate::key_info_managers::KeyIdentity;
6use log::error;
7use parsec_interface::operations::psa_algorithm::Cipher;
8use parsec_interface::operations::{psa_cipher_decrypt, psa_cipher_encrypt, psa_generate_random};
9use parsec_interface::requests::{ResponseStatus, Result};
10use std::convert::TryInto;
11
12const CIPHER_IV_SIZE: usize = 16;
13const CIPHER_CTR_SIZE: u8 = 4;
14
15impl Provider {
16    /// Check if given Cipher Algorithm need generated initialization vector to run
17    pub(super) fn algorithm_need_iv(&self, alg: &Cipher) -> bool {
18        !matches!(alg, Cipher::EcbNoPadding)
19    }
20
21    /// Convert Cipher Algorithm type from parsec to rust_cryptoauthlib type
22    pub(super) fn get_cipher_algorithm(
23        &self,
24        mut cipher_params: rust_cryptoauthlib::CipherParam,
25        alg: &Cipher,
26    ) -> Result<rust_cryptoauthlib::CipherAlgorithm> {
27        match alg {
28            Cipher::Cfb => Ok(rust_cryptoauthlib::CipherAlgorithm::Cfb(cipher_params)),
29            Cipher::Ctr => {
30                cipher_params.counter_size = Some(CIPHER_CTR_SIZE);
31                Ok(rust_cryptoauthlib::CipherAlgorithm::Ctr(cipher_params))
32            }
33            Cipher::Ofb => Ok(rust_cryptoauthlib::CipherAlgorithm::Ofb(cipher_params)),
34            Cipher::EcbNoPadding => Ok(rust_cryptoauthlib::CipherAlgorithm::Ecb(cipher_params)),
35            Cipher::CbcNoPadding => Ok(rust_cryptoauthlib::CipherAlgorithm::Cbc(cipher_params)),
36            Cipher::CbcPkcs7 => Ok(rust_cryptoauthlib::CipherAlgorithm::CbcPkcs7(cipher_params)),
37            _ => {
38                error!("Cipher encryption failed: given algorithm is not supported.");
39                Err(ResponseStatus::PsaErrorNotSupported)
40            }
41        }
42    }
43
44    /// Generate random non-zero initialization vector
45    pub fn generate_iv(&self) -> Result<Vec<u8>> {
46        let random_op = psa_generate_random::Operation {
47            size: CIPHER_IV_SIZE,
48        };
49        let random_bytes = self
50            .psa_generate_random_internal(random_op)?
51            .random_bytes
52            .to_vec();
53        Ok(random_bytes)
54    }
55
56    pub(super) fn psa_cipher_encrypt_internal(
57        &self,
58        application_identity: &ApplicationIdentity,
59        op: psa_cipher_encrypt::Operation,
60    ) -> Result<psa_cipher_encrypt::Result> {
61        let key_identity = KeyIdentity::new(
62            application_identity.clone(),
63            self.provider_identity.clone(),
64            op.key_name.clone(),
65        );
66        let key_attributes = self.key_info_store.get_key_attributes(&key_identity)?;
67        op.validate(key_attributes)?;
68
69        let mut cipher_param = rust_cryptoauthlib::CipherParam {
70            ..Default::default()
71        };
72        let mut generated_iv = vec![0u8; 0];
73        if self.algorithm_need_iv(&op.alg) {
74            generated_iv = self.generate_iv()?;
75            cipher_param.iv = Some(generated_iv[..].try_into()?);
76        }
77
78        let mut plaintext = op.plaintext.to_vec();
79        let key_id = self.key_info_store.get_key_id::<u8>(&key_identity)?;
80
81        let result = self.device.cipher_encrypt(
82            self.get_cipher_algorithm(cipher_param, &op.alg)?,
83            key_id,
84            &mut plaintext,
85        );
86        match result {
87            rust_cryptoauthlib::AtcaStatus::AtcaSuccess => {
88                generated_iv.append(&mut plaintext);
89                let ciphertext = zeroize::Zeroizing::new(generated_iv);
90                Ok(psa_cipher_encrypt::Result { ciphertext })
91            }
92            rust_cryptoauthlib::AtcaStatus::AtcaInvalidSize
93            | rust_cryptoauthlib::AtcaStatus::AtcaInvalidId
94            | rust_cryptoauthlib::AtcaStatus::AtcaBadParam => {
95                error!("Cipher encryption failed: given plaintext is invalid.");
96                Err(ResponseStatus::PsaErrorInvalidArgument)
97            }
98            _ => Err(ResponseStatus::PsaErrorGenericError),
99        }
100    }
101
102    pub(super) fn psa_cipher_decrypt_internal(
103        &self,
104        application_identity: &ApplicationIdentity,
105        op: psa_cipher_decrypt::Operation,
106    ) -> Result<psa_cipher_decrypt::Result> {
107        let key_identity = KeyIdentity::new(
108            application_identity.clone(),
109            self.provider_identity.clone(),
110            op.key_name.clone(),
111        );
112        let key_attributes = self.key_info_store.get_key_attributes(&key_identity)?;
113        op.validate(key_attributes)?;
114
115        let mut cipher_param = rust_cryptoauthlib::CipherParam {
116            ..Default::default()
117        };
118        let mut ciphertext = op.ciphertext.to_vec();
119
120        if self.algorithm_need_iv(&op.alg) {
121            if ciphertext.len() < CIPHER_IV_SIZE {
122                error!(
123                    "Cipher decryption failed: given ciphertext is too short to contain initialization vector."
124                );
125                return Err(ResponseStatus::PsaErrorInvalidArgument);
126            }
127            let mut iv = ciphertext;
128            ciphertext = iv.split_off(CIPHER_IV_SIZE);
129            cipher_param.iv = Some(iv[..].try_into()?);
130        }
131
132        let key_id = self.key_info_store.get_key_id::<u8>(&key_identity)?;
133
134        let result = self.device.cipher_decrypt(
135            self.get_cipher_algorithm(cipher_param, &op.alg)?,
136            key_id,
137            &mut ciphertext,
138        );
139
140        match result {
141            rust_cryptoauthlib::AtcaStatus::AtcaSuccess => {
142                let plaintext = zeroize::Zeroizing::new(ciphertext);
143                Ok(psa_cipher_decrypt::Result { plaintext })
144            }
145            rust_cryptoauthlib::AtcaStatus::AtcaInvalidSize
146            | rust_cryptoauthlib::AtcaStatus::AtcaInvalidId
147            | rust_cryptoauthlib::AtcaStatus::AtcaBadParam => {
148                error!("Cipher decryption failed: given ciphertext is invalid.");
149                Err(ResponseStatus::PsaErrorInvalidArgument)
150            }
151            _ => Err(ResponseStatus::PsaErrorGenericError),
152        }
153    }
154}