tss_esapi/context/tpm_commands/
symmetric_primitives.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    handles::{KeyHandle, ObjectHandle, TpmHandle},
5    interface_types::{
6        algorithm::{HashingAlgorithm, SymmetricMode},
7        resource_handles::Hierarchy,
8    },
9    structures::{Digest, HashcheckTicket, InitialValue, MaxBuffer},
10    tss2_esys::{Esys_EncryptDecrypt2, Esys_HMAC, Esys_Hash},
11    Context, Error, Result,
12};
13use log::error;
14use std::convert::TryFrom;
15use std::ptr::null_mut;
16
17impl Context {
18    // Missing function: EncryptDecrypt, deprecated use EncryptDecrypt2 instead.
19
20    /// Performs symmetric encryption or decryption of the data using
21    /// the key associated with the `key_handle`
22    ///
23    /// # Arguments
24    /// * `key_handle` -  A [KeyHandle] to the key to be used.
25    /// * `decrypt` - A boolean indicating if the data should be decrypted or encrypted.
26    ///               If set to true the data will be decrypted else encrypted.
27    /// * `mode` - The [SymmetricMode] to be used.
28    /// * `in_data` - The data that is going to be decrypted or encrypted.
29    /// * `initial_value_in` - An initial value as required by the algorithm.
30    ///
31    /// # Example
32    /// ```rust
33    /// # use tss_esapi::{
34    /// #     constants::AlgorithmIdentifier,
35    /// #     attributes::ObjectAttributesBuilder,
36    /// #     Context, tcti_ldr::TctiNameConf, Result,
37    /// #     structures::{
38    /// #         Auth, InitialValue, MaxBuffer, SensitiveData, RsaExponent, SymmetricDefinitionObject,
39    /// #         SymmetricCipherParameters, PublicBuilder,
40    /// #     },
41    /// #     interface_types::{
42    /// #         algorithm::{PublicAlgorithm, HashingAlgorithm},
43    /// #         key_bits::RsaKeyBits,
44    /// #     },
45    /// # };
46    /// use tss_esapi::interface_types::session_handles::AuthSession;
47    /// use tss_esapi::interface_types::algorithm::SymmetricMode;
48    /// # use std::convert::TryFrom;
49    /// # // Create context
50    /// # let mut context =
51    /// #     Context::new(
52    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
53    /// #     ).expect("Failed to create Context");
54    /// # // Set auth for owner
55    /// # context
56    /// #     .tr_set_auth(tss_esapi::interface_types::resource_handles::Hierarchy::Owner.into(), Auth::default())
57    /// #     .expect("Failed to set auth to empty for owner");
58    /// # // Create primary key auth
59    /// # let mut random_digest = vec![0u8; 16];
60    /// # getrandom::getrandom(&mut random_digest).expect("get_rand call failed");
61    /// # let primary_key_auth = Auth::try_from(
62    /// #     random_digest
63    /// # )
64    /// # .expect("Failed to create primary key auth");
65    /// # // Create primary key
66    /// # let primary_key_handle = context.execute_with_session(Some(AuthSession::Password), |ctx| {
67    /// #     ctx.create_primary(
68    /// #         tss_esapi::interface_types::resource_handles::Hierarchy::Owner,
69    /// #         tss_esapi::utils::create_restricted_decryption_rsa_public(
70    /// #             SymmetricDefinitionObject::AES_256_CFB,
71    /// #             RsaKeyBits::Rsa2048,
72    /// #             RsaExponent::default(),
73    /// #         )
74    /// #         .expect("Failed to create public for primary key"),
75    /// #         Some(primary_key_auth.clone()),
76    /// #         None,
77    /// #         None,
78    /// #         None,
79    /// #     )
80    /// #     .expect("Failed to create primary handle")
81    /// #     .key_handle
82    /// # });
83    /// # // Set auth for the primary key handle
84    /// # context
85    /// #     .tr_set_auth(primary_key_handle.into(), primary_key_auth)
86    /// #     .expect("Failed to set auth from primary key handle.");
87    /// # // Create symmetric key object attributes
88    /// # let symmetric_key_object_attributes = ObjectAttributesBuilder::new()
89    /// #     .with_user_with_auth(true)
90    /// #     .with_sign_encrypt(true)
91    /// #     .with_decrypt(true)
92    /// #     .build()
93    /// #     .expect("Failed to create object attributes for symmetric key");
94    /// # // Create public part for the symmetric key
95    /// # let symmetric_key_public = PublicBuilder::new()
96    /// #     .with_public_algorithm(PublicAlgorithm::SymCipher)
97    /// #     .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
98    /// #     .with_object_attributes(symmetric_key_object_attributes)
99    /// #     .with_symmetric_cipher_parameters(SymmetricCipherParameters::new(SymmetricDefinitionObject::AES_256_CFB))
100    /// #     .with_symmetric_cipher_unique_identifier(Default::default())
101    /// #     .build()
102    /// #     .expect("Failed to create public for symmetric key public");
103    /// # // Create auth for the symmetric key
104    /// # let mut random_digest = vec![0u8; 16];
105    /// # getrandom::getrandom(&mut random_digest).expect("get_rand call failed");
106    /// # let symmetric_key_auth = Auth::try_from(
107    /// #     random_digest
108    /// # )
109    /// # .expect("Failed to create symmetric key auth");
110    /// # // Create symmetric key data
111    /// # let symmetric_key_value =
112    /// #     SensitiveData::try_from(vec![
113    /// #           1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
114    /// #           17, 18, 19, 20, 21, 22 ,23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
115    /// #           .expect("Failed to create sensitive data from data");
116    /// # // Create the symmetric key
117    /// # // if this fails with "tpm:parameter(2):inconsistent attributes" then the symmetric
118    /// # // cipher is probably not supported.
119    /// # let symmetric_key_creation_data =
120    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
121    /// #         ctx.create(
122    /// #             primary_key_handle,
123    /// #             symmetric_key_public,
124    /// #             Some(symmetric_key_auth.clone()),
125    /// #             Some(symmetric_key_value),
126    /// #             None,
127    /// #             None,
128    /// #         )
129    /// #         .expect("Failed to create symmetric key")
130    /// #     });
131    /// # // Load the symmetric key in order to get handle to it.
132    /// # let symmetric_key_handle =
133    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
134    /// #         ctx.load(
135    /// #             primary_key_handle,
136    /// #             symmetric_key_creation_data.out_private,
137    /// #             symmetric_key_creation_data.out_public,
138    /// #         )
139    /// #         .expect("Failed to load symmetric key")
140    /// #     });
141    /// # // Set auth for the handle to be able to use it.
142    /// # context
143    /// #     .tr_set_auth(symmetric_key_handle.into(), symmetric_key_auth)
144    /// #     .expect("Failed to set auth on symmetric key handle");
145    /// #
146    /// # // Create initial value to be used by the algorithm.
147    /// # let initial_value =
148    /// # InitialValue::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
149    /// #    .expect("Failed to create InitialValue from data");
150    /// # // Create data to be encrypted.
151    /// # let data = MaxBuffer::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16])
152    /// #    .expect("Failed to create MaxBuffer from data");
153    /// // Encrypt the data
154    /// let (encrypted_data, _initial_value_out) =
155    ///     context.execute_with_session(Some(AuthSession::Password), |ctx| {
156    ///         ctx.encrypt_decrypt_2(
157    ///             symmetric_key_handle, // Handle to a symmetric key
158    ///             false,                // false, indicates that the data should be encrypted.
159    ///             SymmetricMode::Cfb,   // The symmetric mode of the encryption.
160    ///             data.clone(),                // The data that is to be encrypted.
161    ///             initial_value.clone(),       // Initial value needed by the algorithm.
162    ///         )
163    ///         .expect("Call to encrypt_decrypt_2 failed when encrypting data")
164    ///     });
165    ///
166    /// assert_ne!(data.clone(), encrypted_data);
167    /// #
168    /// # let (decrypted_data, _) =
169    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
170    /// #         ctx.encrypt_decrypt_2(
171    /// #             symmetric_key_handle,
172    /// #             true,
173    /// #             SymmetricMode::Cfb,
174    /// #             encrypted_data,
175    /// #             initial_value,
176    /// #         )
177    /// #         .expect("Call to encrypt_decrypt_2 failed when decrypting data")
178    /// #     });
179    /// #
180    /// # debug_assert_eq!(data, decrypted_data);
181    /// ```
182    pub fn encrypt_decrypt_2(
183        &mut self,
184        key_handle: KeyHandle,
185        decrypt: bool,
186        mode: SymmetricMode,
187        in_data: MaxBuffer,
188        initial_value_in: InitialValue,
189    ) -> Result<(MaxBuffer, InitialValue)> {
190        let mut out_data_ptr = null_mut();
191        let mut iv_out_ptr = null_mut();
192        let ret = unsafe {
193            Esys_EncryptDecrypt2(
194                self.mut_context(),
195                key_handle.into(),
196                self.required_session_1()?,
197                self.optional_session_2(),
198                self.optional_session_3(),
199                &in_data.into(),
200                decrypt.into(),
201                mode.into(),
202                &initial_value_in.into(),
203                &mut out_data_ptr,
204                &mut iv_out_ptr,
205            )
206        };
207
208        let ret = Error::from_tss_rc(ret);
209        if ret.is_success() {
210            Ok((
211                MaxBuffer::try_from(Context::ffi_data_to_owned(out_data_ptr))?,
212                InitialValue::try_from(Context::ffi_data_to_owned(iv_out_ptr))?,
213            ))
214        } else {
215            error!(
216                "Error failed to perform encrypt or decrypt operations {}",
217                ret
218            );
219            Err(ret)
220        }
221    }
222
223    /// Hashes the provided data using the specified algorithm.
224    ///
225    /// # Details
226    /// Performs the specified hash operation on a data buffer and return
227    /// the result. The HashCheckTicket indicates if the hash can be used in
228    /// a signing operation that uses restricted signing key.
229    ///
230    /// # Example
231    ///
232    /// ```rust
233    /// # use tss_esapi::{Context, tcti_ldr::TctiNameConf,
234    /// #     structures::{MaxBuffer, Ticket},
235    /// #     interface_types::{algorithm::HashingAlgorithm, resource_handles::Hierarchy},
236    /// # };
237    /// # use std::convert::TryFrom;
238    /// # // Create context
239    /// # let mut context =
240    /// #     Context::new(
241    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
242    /// #     ).expect("Failed to create Context");
243    /// let input_data = MaxBuffer::try_from("There is no spoon".as_bytes().to_vec())
244    ///     .expect("Failed to create buffer for input data.");
245    /// let expected_hashed_data: [u8; 32] = [
246    ///     0x6b, 0x38, 0x4d, 0x2b, 0xfb, 0x0e, 0x0d, 0xfb, 0x64, 0x89, 0xdb, 0xf4, 0xf8, 0xe9,
247    ///     0xe5, 0x2f, 0x71, 0xee, 0xb1, 0x0d, 0x06, 0x4c, 0x56, 0x59, 0x70, 0xcd, 0xd9, 0x44,
248    ///     0x43, 0x18, 0x5d, 0xc1,
249    /// ];
250    /// let expected_hierarchy = Hierarchy::Owner;
251    /// let (actual_hashed_data, ticket) = context
252    ///     .hash(
253    ///         input_data,
254    ///         HashingAlgorithm::Sha256,
255    ///         expected_hierarchy,
256    ///     )
257    ///     .expect("Call to hash failed.");
258    /// assert_eq!(expected_hashed_data.len(), actual_hashed_data.len());
259    /// assert_eq!(&expected_hashed_data[..], &actual_hashed_data[..]);
260    /// assert_eq!(ticket.hierarchy(), expected_hierarchy);
261    /// ```
262    pub fn hash(
263        &mut self,
264        data: MaxBuffer,
265        hashing_algorithm: HashingAlgorithm,
266        hierarchy: Hierarchy,
267    ) -> Result<(Digest, HashcheckTicket)> {
268        let mut out_hash_ptr = null_mut();
269        let mut validation_ptr = null_mut();
270        let ret = unsafe {
271            #[allow(unexpected_cfgs)]
272            Esys_Hash(
273                self.mut_context(),
274                self.optional_session_1(),
275                self.optional_session_2(),
276                self.optional_session_3(),
277                &data.into(),
278                hashing_algorithm.into(),
279                if cfg!(tpm2_tss_version = "2") {
280                    TpmHandle::from(hierarchy).into()
281                } else {
282                    ObjectHandle::from(hierarchy).into()
283                },
284                &mut out_hash_ptr,
285                &mut validation_ptr,
286            )
287        };
288        let ret = Error::from_tss_rc(ret);
289        if ret.is_success() {
290            Ok((
291                Digest::try_from(Context::ffi_data_to_owned(out_hash_ptr))?,
292                HashcheckTicket::try_from(Context::ffi_data_to_owned(validation_ptr))?,
293            ))
294        } else {
295            error!("Error failed to perform hash operation: {}", ret);
296            Err(ret)
297        }
298    }
299
300    /// Asks the TPM to compute an HMAC over buffer with the specified key
301    ///
302    /// # Example
303    ///
304    /// ```rust
305    /// # use tss_esapi::{
306    /// #     attributes::ObjectAttributesBuilder,
307    /// #     structures::{MaxBuffer, Ticket, PublicKeyedHashParameters, KeyedHashScheme, HmacScheme, PublicBuilder, Digest},
308    /// #     interface_types::{
309    /// #           resource_handles::Hierarchy,
310    /// #           algorithm::{HashingAlgorithm, PublicAlgorithm},
311    /// #     },
312    /// #     Context, tcti_ldr::TctiNameConf,
313    /// # };
314    /// # use std::convert::TryFrom;
315    /// # // Create context
316    /// # let mut context =
317    /// #     Context::new(
318    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
319    /// #     ).expect("Failed to create Context");
320    /// // Create a key
321    /// let object_attributes = ObjectAttributesBuilder::new()
322    ///     .with_sign_encrypt(true)
323    ///     .with_sensitive_data_origin(true)
324    ///     .with_user_with_auth(true)
325    ///     .build()
326    ///     .expect("Failed to build object attributes");
327    /// let key_pub = PublicBuilder::new()
328    ///     .with_public_algorithm(PublicAlgorithm::KeyedHash)
329    ///     .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
330    ///     .with_object_attributes(object_attributes)
331    ///     .with_keyed_hash_parameters(PublicKeyedHashParameters::new(KeyedHashScheme::HMAC_SHA_256))
332    ///     .with_keyed_hash_unique_identifier(Digest::default())
333    ///     .build()
334    ///     .unwrap();
335    ///
336    /// let input_data = MaxBuffer::try_from("There is no spoon".as_bytes().to_vec())
337    ///     .expect("Failed to create buffer for input data.");
338    ///
339    /// let hmac = context.execute_with_nullauth_session(|ctx| {
340    ///     let key = ctx.create_primary(Hierarchy::Owner, key_pub, None, None, None, None).unwrap();
341    ///
342    ///     ctx.hmac(key.key_handle.into(), input_data, HashingAlgorithm::Sha256)
343    /// }).unwrap();
344    ///
345    /// ```
346    ///
347    /// # Errors
348    /// * if any of the public parameters is not compatible with the TPM,
349    ///   an `Err` containing the specific unmarshalling error will be returned.
350    pub fn hmac(
351        &mut self,
352        handle: ObjectHandle,
353        buffer: MaxBuffer,
354        alg_hash: HashingAlgorithm,
355    ) -> Result<Digest> {
356        let mut out_hmac_ptr = null_mut();
357        let ret = unsafe {
358            Esys_HMAC(
359                self.mut_context(),
360                handle.into(),
361                self.required_session_1()?,
362                self.optional_session_2(),
363                self.optional_session_3(),
364                &buffer.into(),
365                alg_hash.into(),
366                &mut out_hmac_ptr,
367            )
368        };
369        let ret = Error::from_tss_rc(ret);
370
371        if ret.is_success() {
372            Digest::try_from(Context::ffi_data_to_owned(out_hmac_ptr))
373        } else {
374            error!("Error in hmac: {}", ret);
375            Err(ret)
376        }
377    }
378
379    // Missing function: MAC
380}