Skip to main content

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        reserved_handles::Hierarchy,
8    },
9    structures::{Digest, HashcheckTicket, InitialValue, MaxBuffer},
10    tss2_esys::{Esys_EncryptDecrypt2, Esys_HMAC, Esys_Hash},
11    Context, Result, ReturnCode,
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::reserved_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::from_bytes(
62    /// #     random_digest
63    /// #         .as_slice()
64    /// # )
65    /// # .expect("Failed to create primary key auth");
66    /// # // Create primary key
67    /// # let primary_key_handle = context.execute_with_session(Some(AuthSession::Password), |ctx| {
68    /// #     ctx.create_primary(
69    /// #         tss_esapi::interface_types::reserved_handles::Hierarchy::Owner,
70    /// #         tss_esapi::utils::create_restricted_decryption_rsa_public(
71    /// #             SymmetricDefinitionObject::AES_256_CFB,
72    /// #             RsaKeyBits::Rsa2048,
73    /// #             RsaExponent::default(),
74    /// #         )
75    /// #         .expect("Failed to create public for primary key"),
76    /// #         Some(primary_key_auth.clone()),
77    /// #         None,
78    /// #         None,
79    /// #         None,
80    /// #     )
81    /// #     .expect("Failed to create primary handle")
82    /// #     .key_handle
83    /// # });
84    /// # // Set auth for the primary key handle
85    /// # context
86    /// #     .tr_set_auth(primary_key_handle.into(), primary_key_auth)
87    /// #     .expect("Failed to set auth from primary key handle.");
88    /// # // Create symmetric key object attributes
89    /// # let symmetric_key_object_attributes = ObjectAttributesBuilder::new()
90    /// #     .with_user_with_auth(true)
91    /// #     .with_sign_encrypt(true)
92    /// #     .with_decrypt(true)
93    /// #     .build()
94    /// #     .expect("Failed to create object attributes for symmetric key");
95    /// # // Create public part for the symmetric key
96    /// # let symmetric_key_public = PublicBuilder::new()
97    /// #     .with_public_algorithm(PublicAlgorithm::SymCipher)
98    /// #     .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
99    /// #     .with_object_attributes(symmetric_key_object_attributes)
100    /// #     .with_symmetric_cipher_parameters(SymmetricCipherParameters::new(SymmetricDefinitionObject::AES_256_CFB))
101    /// #     .with_symmetric_cipher_unique_identifier(Default::default())
102    /// #     .build()
103    /// #     .expect("Failed to create public for symmetric key public");
104    /// # // Create auth for the symmetric key
105    /// # let mut random_digest = vec![0u8; 16];
106    /// # getrandom::getrandom(&mut random_digest).expect("get_rand call failed");
107    /// # let symmetric_key_auth = Auth::from_bytes(
108    /// #     random_digest
109    /// #         .as_slice()
110    /// # )
111    /// # .expect("Failed to create symmetric key auth");
112    /// # // Create symmetric key data
113    /// # let symmetric_key_value =
114    /// #     SensitiveData::try_from(vec![
115    /// #           1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
116    /// #           17, 18, 19, 20, 21, 22 ,23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
117    /// #           .expect("Failed to create sensitive data from data");
118    /// # // Create the symmetric key
119    /// # // if this fails with "tpm:parameter(2):inconsistent attributes" then the symmetric
120    /// # // cipher is probably not supported.
121    /// # let symmetric_key_creation_data =
122    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
123    /// #         ctx.create(
124    /// #             primary_key_handle,
125    /// #             symmetric_key_public,
126    /// #             Some(symmetric_key_auth.clone()),
127    /// #             Some(symmetric_key_value),
128    /// #             None,
129    /// #             None,
130    /// #         )
131    /// #         .expect("Failed to create symmetric key")
132    /// #     });
133    /// # // Load the symmetric key in order to get handle to it.
134    /// # let symmetric_key_handle =
135    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
136    /// #         ctx.load(
137    /// #             primary_key_handle,
138    /// #             symmetric_key_creation_data.out_private,
139    /// #             symmetric_key_creation_data.out_public,
140    /// #         )
141    /// #         .expect("Failed to load symmetric key")
142    /// #     });
143    /// # // Set auth for the handle to be able to use it.
144    /// # context
145    /// #     .tr_set_auth(symmetric_key_handle.into(), symmetric_key_auth)
146    /// #     .expect("Failed to set auth on symmetric key handle");
147    /// #
148    /// # // Create initial value to be used by the algorithm.
149    /// # let initial_value =
150    /// # InitialValue::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
151    /// #    .expect("Failed to create InitialValue from data");
152    /// # // Create data to be encrypted.
153    /// # let data = MaxBuffer::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16])
154    /// #    .expect("Failed to create MaxBuffer from data");
155    /// // Encrypt the data
156    /// let (encrypted_data, _initial_value_out) =
157    ///     context.execute_with_session(Some(AuthSession::Password), |ctx| {
158    ///         ctx.encrypt_decrypt_2(
159    ///             symmetric_key_handle, // Handle to a symmetric key
160    ///             false,                // false, indicates that the data should be encrypted.
161    ///             SymmetricMode::Cfb,   // The symmetric mode of the encryption.
162    ///             data.clone(),                // The data that is to be encrypted.
163    ///             initial_value.clone(),       // Initial value needed by the algorithm.
164    ///         )
165    ///         .expect("Call to encrypt_decrypt_2 failed when encrypting data")
166    ///     });
167    ///
168    /// assert_ne!(data.clone(), encrypted_data);
169    /// #
170    /// # let (decrypted_data, _) =
171    /// #     context.execute_with_session(Some(AuthSession::Password), |ctx| {
172    /// #         ctx.encrypt_decrypt_2(
173    /// #             symmetric_key_handle,
174    /// #             true,
175    /// #             SymmetricMode::Cfb,
176    /// #             encrypted_data,
177    /// #             initial_value,
178    /// #         )
179    /// #         .expect("Call to encrypt_decrypt_2 failed when decrypting data")
180    /// #     });
181    /// #
182    /// # debug_assert_eq!(data, decrypted_data);
183    /// ```
184    pub fn encrypt_decrypt_2(
185        &mut self,
186        key_handle: KeyHandle,
187        decrypt: bool,
188        mode: SymmetricMode,
189        in_data: MaxBuffer,
190        initial_value_in: InitialValue,
191    ) -> Result<(MaxBuffer, InitialValue)> {
192        let mut out_data_ptr = null_mut();
193        let mut iv_out_ptr = null_mut();
194        ReturnCode::ensure_success(
195            unsafe {
196                Esys_EncryptDecrypt2(
197                    self.mut_context(),
198                    key_handle.into(),
199                    self.required_session_1()?,
200                    self.optional_session_2(),
201                    self.optional_session_3(),
202                    &in_data.into(),
203                    decrypt.into(),
204                    mode.into(),
205                    &initial_value_in.into(),
206                    &mut out_data_ptr,
207                    &mut iv_out_ptr,
208                )
209            },
210            |ret| {
211                error!(
212                    "Error failed to perform encrypt or decrypt operations {:#010X}",
213                    ret
214                );
215            },
216        )?;
217        Ok((
218            MaxBuffer::try_from(Context::ffi_data_to_owned(out_data_ptr)?)?,
219            InitialValue::try_from(Context::ffi_data_to_owned(iv_out_ptr)?)?,
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, reserved_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        ReturnCode::ensure_success(
271            unsafe {
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!(hierarchy_is_esys_tr) {
280                        ObjectHandle::from(hierarchy).into()
281                    } else {
282                        TpmHandle::from(hierarchy).into()
283                    },
284                    &mut out_hash_ptr,
285                    &mut validation_ptr,
286                )
287            },
288            |ret| {
289                error!("Error failed to perform hash operation: {:#010X}", ret);
290            },
291        )?;
292        Ok((
293            Digest::try_from(Context::ffi_data_to_owned(out_hash_ptr)?)?,
294            HashcheckTicket::try_from(Context::ffi_data_to_owned(validation_ptr)?)?,
295        ))
296    }
297
298    /// Asks the TPM to compute an HMAC over buffer with the specified key
299    ///
300    /// # Example
301    ///
302    /// ```rust
303    /// # use tss_esapi::{
304    /// #     attributes::ObjectAttributesBuilder,
305    /// #     structures::{MaxBuffer, Ticket, PublicKeyedHashParameters, KeyedHashScheme, HmacScheme, PublicBuilder, Digest},
306    /// #     interface_types::{
307    /// #           reserved_handles::Hierarchy,
308    /// #           algorithm::{HashingAlgorithm, PublicAlgorithm},
309    /// #     },
310    /// #     Context, tcti_ldr::TctiNameConf,
311    /// # };
312    /// # use std::convert::TryFrom;
313    /// # // Create context
314    /// # let mut context =
315    /// #     Context::new(
316    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
317    /// #     ).expect("Failed to create Context");
318    /// // Create a key
319    /// let object_attributes = ObjectAttributesBuilder::new()
320    ///     .with_sign_encrypt(true)
321    ///     .with_sensitive_data_origin(true)
322    ///     .with_user_with_auth(true)
323    ///     .build()
324    ///     .expect("Failed to build object attributes");
325    /// let key_pub = PublicBuilder::new()
326    ///     .with_public_algorithm(PublicAlgorithm::KeyedHash)
327    ///     .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
328    ///     .with_object_attributes(object_attributes)
329    ///     .with_keyed_hash_parameters(PublicKeyedHashParameters::new(KeyedHashScheme::HMAC_SHA_256))
330    ///     .with_keyed_hash_unique_identifier(Digest::default())
331    ///     .build()
332    ///     .unwrap();
333    ///
334    /// let input_data = MaxBuffer::try_from("There is no spoon".as_bytes().to_vec())
335    ///     .expect("Failed to create buffer for input data.");
336    ///
337    /// let hmac = context.execute_with_nullauth_session(|ctx| {
338    ///     let key = ctx.create_primary(Hierarchy::Owner, key_pub, None, None, None, None).unwrap();
339    ///
340    ///     ctx.hmac(key.key_handle.into(), input_data, HashingAlgorithm::Sha256)
341    /// }).unwrap();
342    ///
343    /// ```
344    ///
345    /// # Errors
346    /// * if any of the public parameters is not compatible with the TPM,
347    ///   an `Err` containing the specific unmarshalling error will be returned.
348    pub fn hmac(
349        &mut self,
350        handle: ObjectHandle,
351        buffer: MaxBuffer,
352        alg_hash: HashingAlgorithm,
353    ) -> Result<Digest> {
354        let mut out_hmac_ptr = null_mut();
355        ReturnCode::ensure_success(
356            unsafe {
357                Esys_HMAC(
358                    self.mut_context(),
359                    handle.into(),
360                    self.required_session_1()?,
361                    self.optional_session_2(),
362                    self.optional_session_3(),
363                    &buffer.into(),
364                    alg_hash.into(),
365                    &mut out_hmac_ptr,
366                )
367            },
368            |ret| {
369                error!("Error in hmac: {:#010X}", ret);
370            },
371        )?;
372        Digest::try_from(Context::ffi_data_to_owned(out_hmac_ptr)?)
373    }
374
375    // Missing function: MAC
376}