Skip to main content

tss_esapi/context/
general_esys_tr.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    constants::tss::TPM2_RH_UNASSIGNED,
5    context::handle_manager::HandleDropAction,
6    ffi::{to_owned_bytes, FfiSizeType},
7    handles::ObjectHandle,
8    handles::{handle_conversion::TryIntoNotNone, TpmHandle},
9    structures::Auth,
10    structures::Name,
11    tss2_esys::{
12        Esys_TR_Close, Esys_TR_Deserialize, Esys_TR_FromTPMPublic, Esys_TR_GetName,
13        Esys_TR_Serialize, Esys_TR_SetAuth,
14    },
15    Context, Error, Result, ReturnCode, WrapperErrorKind,
16};
17use log::error;
18use std::convert::{TryFrom, TryInto};
19use std::ptr::null_mut;
20use zeroize::Zeroize;
21
22impl Context {
23    /// Set the authentication value for a given object handle in the ESYS context.
24    ///
25    /// # Arguments
26    /// * `object_handle` - The [ObjectHandle] associated with an object for which the auth is to be set.
27    /// * `auth` -  The [Auth] that is to be set.
28    ///
29    /// ```rust
30    /// # use tss_esapi::{Context, TctiNameConf};
31    /// use tss_esapi::{handles::ObjectHandle, structures::Auth};
32    /// # // Create context
33    /// # let mut context =
34    /// #     Context::new(
35    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
36    /// #     ).expect("Failed to create Context");
37    ///
38    /// // Sets auth for Owner to empty string.
39    /// context
40    ///     .tr_set_auth(ObjectHandle::Owner, Auth::default())
41    ///     .expect("Failed to call tr_set_auth");
42    /// ```
43    pub fn tr_set_auth(&mut self, object_handle: ObjectHandle, auth: Auth) -> Result<()> {
44        let mut auth_value = auth.into();
45        ReturnCode::ensure_success(
46            unsafe { Esys_TR_SetAuth(self.mut_context(), object_handle.into(), &auth_value) },
47            |ret| {
48                auth_value.buffer.zeroize();
49                error!("Error when setting authentication value: {:#010X}", ret);
50            },
51        )
52    }
53
54    /// Retrieve the name of an object from the object handle.
55    ///
56    /// # Arguments
57    /// * `object_handle` - Handle to the object for which the 'name' shall be retrieved.
58    ///
59    /// # Returns
60    /// The objects name.
61    ///
62    /// # Example
63    /// ```rust
64    /// # use tss_esapi::{
65    /// #     Context, TctiNameConf, attributes::{SessionAttributes, NvIndexAttributes},
66    /// #     constants::SessionType, handles::NvIndexTpmHandle,
67    /// #     interface_types::{algorithm::HashingAlgorithm, reserved_handles::Provision},
68    /// #     structures::{SymmetricDefinition, NvPublic},
69    /// # };
70    /// # // Create context
71    /// # let mut context =
72    /// #     Context::new(
73    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
74    /// #     ).expect("Failed to create Context");
75    /// #
76    /// # let session = context
77    /// #     .start_auth_session(
78    /// #         None,
79    /// #         None,
80    /// #         None,
81    /// #         SessionType::Hmac,
82    /// #         SymmetricDefinition::AES_256_CFB,
83    /// #         HashingAlgorithm::Sha256,
84    /// #     )
85    /// #     .expect("Failed to create session")
86    /// #     .expect("Received invalid handle");
87    /// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
88    /// #     .with_decrypt(true)
89    /// #     .with_encrypt(true)
90    /// #     .build();
91    /// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
92    /// #     .expect("Failed to set attributes on session");
93    /// # context.set_sessions((Some(session), None, None));
94    /// #
95    /// # let nv_index = NvIndexTpmHandle::new(0x01500401)
96    /// #     .expect("Failed to create NV index tpm handle");
97    /// #
98    /// # // Create NV index attributes
99    /// # let owner_nv_index_attributes = NvIndexAttributes::builder()
100    /// #     .with_owner_write(true)
101    /// #     .with_owner_read(true)
102    /// #     .build()
103    /// #     .expect("Failed to create owner nv index attributes");
104    /// #
105    /// # // Create owner nv public.
106    /// # let owner_nv_public = NvPublic::builder()
107    /// #     .with_nv_index(nv_index)
108    /// #     .with_index_name_algorithm(HashingAlgorithm::Sha256)
109    /// #     .with_index_attributes(owner_nv_index_attributes)
110    /// #     .with_data_area_size(32)
111    /// #     .build()
112    /// #     .expect("Failed to build NvPublic for owner");
113    /// #
114    /// # // Define the NV space.
115    /// # let nv_index_handle = context
116    /// #     .nv_define_space(Provision::Owner, None, owner_nv_public)
117    /// #     .expect("Call to nv_define_space failed");
118    ///
119    /// // Get the name using tr_get_name
120    /// let tr_get_name_result = context.tr_get_name(nv_index_handle.into());
121    ///
122    /// // Get the name from the NV by calling nv_read_public
123    /// let nv_read_public_result = context.nv_read_public(nv_index_handle);
124    /// #
125    /// # context
126    /// #    .nv_undefine_space(Provision::Owner, nv_index_handle)
127    /// #    .expect("Call to nv_undefine_space failed");
128    /// #
129    /// // Process result by comparing the names
130    /// let (_public_area, expected_name) = nv_read_public_result.expect("Call to nv_read_public failed");
131    /// let actual_name = tr_get_name_result.expect("Call to tr_get_name failed");
132    /// assert_eq!(expected_name, actual_name);
133    /// ```
134    pub fn tr_get_name(&mut self, object_handle: ObjectHandle) -> Result<Name> {
135        let mut name_ptr = null_mut();
136        ReturnCode::ensure_success(
137            unsafe { Esys_TR_GetName(self.mut_context(), object_handle.into(), &mut name_ptr) },
138            |ret| {
139                error!("Error in getting name: {:#010X}", ret);
140            },
141        )?;
142        Name::try_from(Context::ffi_data_to_owned(name_ptr)?)
143    }
144
145    /// Used to construct an esys object from the resources inside the TPM.
146    ///
147    /// # Arguments
148    /// * `tpm_handle` - The TPM handle that references the TPM object for which
149    ///   the ESYS object is being created.
150    ///
151    /// # Returns
152    /// A handle to the ESYS object that was created from a TPM resource.
153    ///
154    /// # Example
155    /// ```rust
156    /// # use tss_esapi::{
157    /// #     Context, TctiNameConf, attributes::{SessionAttributes, NvIndexAttributes},
158    /// #     constants::SessionType,
159    /// #     interface_types::{algorithm::HashingAlgorithm, reserved_handles::Provision},
160    /// #     structures::{SymmetricDefinition, NvPublic},
161    /// # };
162    /// use tss_esapi::{
163    ///     handles::NvIndexTpmHandle,
164    /// };
165    /// # // Create context
166    /// # let mut context =
167    /// #     Context::new(
168    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
169    /// #     ).expect("Failed to create Context");
170    /// #
171    /// # let session = context
172    /// #     .start_auth_session(
173    /// #         None,
174    /// #         None,
175    /// #         None,
176    /// #         SessionType::Hmac,
177    /// #         SymmetricDefinition::AES_256_CFB,
178    /// #         HashingAlgorithm::Sha256,
179    /// #     )
180    /// #     .expect("Failed to create session")
181    /// #     .expect("Received invalid handle");
182    /// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
183    /// #     .with_decrypt(true)
184    /// #     .with_encrypt(true)
185    /// #     .build();
186    /// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
187    /// #     .expect("Failed to set attributes on session");
188    /// # context.set_sessions((Some(session), None, None));
189    /// #
190    /// let nv_index = NvIndexTpmHandle::new(0x01500402)
191    ///     .expect("Failed to create NV index tpm handle");
192    /// #
193    /// # // Create NV index attributes
194    /// # let owner_nv_index_attributes = NvIndexAttributes::builder()
195    /// #     .with_owner_write(true)
196    /// #     .with_owner_read(true)
197    /// #     .build()
198    /// #     .expect("Failed to create owner nv index attributes");
199    /// #
200    /// # // Create owner nv public.
201    /// # let owner_nv_public = NvPublic::builder()
202    /// #     .with_nv_index(nv_index)
203    /// #     .with_index_name_algorithm(HashingAlgorithm::Sha256)
204    /// #     .with_index_attributes(owner_nv_index_attributes)
205    /// #     .with_data_area_size(32)
206    /// #     .build()
207    /// #     .expect("Failed to build NvPublic for owner");
208    /// #
209    /// # // Define the NV space.
210    /// # let nv_index_handle = context
211    /// #     .nv_define_space(Provision::Owner, None, owner_nv_public)
212    /// #     .expect("Call to nv_define_space failed");
213    /// #
214    /// # // Retrieve the name of the NV space.
215    /// # let nv_read_public_result = context.nv_read_public(nv_index_handle);
216    /// #
217    /// # // Close the handle (remove all the metadata).
218    /// # let mut handle_to_be_closed = nv_index_handle.into();
219    /// # let tr_close_result = context
220    /// #     .tr_close(&mut handle_to_be_closed);
221    /// #
222    /// // Call function without session (session can be provided in order to
223    /// // verify that the public data read actually originates from this TPM).
224    /// let retrieved_handle = context.execute_without_session(|ctx| {
225    ///       ctx.tr_from_tpm_public(nv_index.into())
226    /// })
227    /// .expect("Call to tr_from_tpm_public failed.");
228    /// #
229    /// # // Use the retrieved handle to get the name of the object.
230    /// # let tr_get_name_result = context
231    /// #     .tr_get_name(retrieved_handle);
232    /// #
233    /// # context
234    /// #    .nv_undefine_space(Provision::Owner, retrieved_handle.into())
235    /// #    .expect("Call to nv_undefine_space failed");
236    /// #
237    /// # // Process results.
238    /// # tr_close_result.expect("Call to tr_close_result failed");
239    /// # let (_, expected_name) = nv_read_public_result.expect("Call to nv_read_public failed");
240    /// # let actual_name = tr_get_name_result.expect("Call to tr_get_name failed");
241    /// # assert_eq!(expected_name, actual_name);
242    /// ```
243    pub fn tr_from_tpm_public(&mut self, tpm_handle: TpmHandle) -> Result<ObjectHandle> {
244        let mut object = ObjectHandle::None.into();
245        ReturnCode::ensure_success(
246            unsafe {
247                Esys_TR_FromTPMPublic(
248                    self.mut_context(),
249                    tpm_handle.into(),
250                    self.optional_session_1(),
251                    self.optional_session_2(),
252                    self.optional_session_3(),
253                    &mut object,
254                )
255            },
256            |ret| {
257                error!(
258                    "Error when getting ESYS handle from TPM handle: {:#010X}",
259                    ret
260                );
261            },
262        )?;
263        self.handle_manager.add_handle(
264            object.into(),
265            if tpm_handle.may_be_flushed() {
266                HandleDropAction::Flush
267            } else {
268                HandleDropAction::Close
269            },
270        )?;
271        Ok(object.into())
272    }
273
274    /// Instructs the ESAPI to release the metadata and resources allocated for a specific ObjectHandle.
275    ///
276    /// This is useful for cleaning up handles for which the context cannot be flushed.
277    ///
278    /// # Arguments
279    /// * object_handle`- An [ObjectHandle] referring to an object for which all metadata and
280    ///   resources is going to be released.
281    ///
282    /// # Example
283    /// ```rust
284    /// # use tss_esapi::{
285    /// #     Context, TctiNameConf, attributes::{SessionAttributes, NvIndexAttributes},
286    /// #     constants::SessionType, handles::NvIndexTpmHandle,
287    /// #     interface_types::{algorithm::HashingAlgorithm, reserved_handles::Provision},
288    /// #     structures::{SymmetricDefinition, NvPublic},
289    /// # };
290    /// # // Create context
291    /// # let mut context =
292    /// #     Context::new(
293    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
294    /// #     ).expect("Failed to create Context");
295    /// #
296    /// # let session = context
297    /// #     .start_auth_session(
298    /// #         None,
299    /// #         None,
300    /// #         None,
301    /// #         SessionType::Hmac,
302    /// #         SymmetricDefinition::AES_256_CFB,
303    /// #         HashingAlgorithm::Sha256,
304    /// #     )
305    /// #     .expect("Failed to create session")
306    /// #     .expect("Received invalid handle");
307    /// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
308    /// #     .with_decrypt(true)
309    /// #     .with_encrypt(true)
310    /// #     .build();
311    /// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
312    /// #     .expect("Failed to set attributes on session");
313    /// # context.set_sessions((Some(session), None, None));
314    /// #
315    /// let nv_index = NvIndexTpmHandle::new(0x01500403)
316    ///     .expect("Failed to create NV index tpm handle");
317    /// #
318    /// # // Create NV index attributes
319    /// # let owner_nv_index_attributes = NvIndexAttributes::builder()
320    /// #     .with_owner_write(true)
321    /// #     .with_owner_read(true)
322    /// #     .build()
323    /// #     .expect("Failed to create owner nv index attributes");
324    /// #
325    /// # // Create owner nv public.
326    /// # let owner_nv_public = NvPublic::builder()
327    /// #     .with_nv_index(nv_index)
328    /// #     .with_index_name_algorithm(HashingAlgorithm::Sha256)
329    /// #     .with_index_attributes(owner_nv_index_attributes)
330    /// #     .with_data_area_size(32)
331    /// #     .build()
332    /// #     .expect("Failed to build NvPublic for owner");
333    /// #
334    /// # // Define the NV space.
335    /// # let nv_index_handle = context
336    /// #     .nv_define_space(Provision::Owner, None, owner_nv_public)
337    /// #     .expect("Call to nv_define_space failed");
338    /// #
339    /// # // Close the handle (remove all the metadata).
340    /// # let mut handle_to_be_closed = nv_index_handle.into();
341    /// let tr_close_result = context
342    ///     .tr_close(&mut handle_to_be_closed);
343    /// #
344    /// # // Use the retrieved handle to get the name of the object.
345    /// # let tr_get_name_result = context
346    /// #     .tr_get_name(nv_index_handle.into());
347    /// #
348    /// # // Call function without session (session can be provided in order to
349    /// # // verify that the public data read actually originates from this TPM).
350    /// # let retrieved_handle = context.execute_without_session(|ctx| {
351    /// #       ctx.tr_from_tpm_public(nv_index.into())
352    /// # })
353    /// # .expect("Call to tr_from_tpm_public failed.");
354    /// #
355    /// # context
356    /// #    .nv_undefine_space(Provision::Owner, retrieved_handle.into())
357    /// #    .expect("Call to nv_undefine_space failed");
358    /// #
359    /// // Process results.
360    /// tr_close_result.expect("Call to tr_close failed.");
361    /// # tr_get_name_result.expect_err("Calling tr_get_name with invalid handle did not result in an error.");
362    /// ```
363    pub fn tr_close(&mut self, object_handle: &mut ObjectHandle) -> Result<()> {
364        let mut rsrc_handle = object_handle.try_into_not_none()?;
365        ReturnCode::ensure_success(
366            unsafe { Esys_TR_Close(self.mut_context(), &mut rsrc_handle) },
367            |ret| {
368                error!("Error when closing an ESYS handle: {:#010X}", ret);
369            },
370        )?;
371
372        self.handle_manager.set_as_closed(*object_handle)?;
373        *object_handle = ObjectHandle::from(rsrc_handle);
374        Ok(())
375    }
376
377    #[cfg(has_esys_tr_get_tpm_handle)]
378    /// Retrieve the `TpmHandle` stored in the given object.
379    pub fn tr_get_tpm_handle(&mut self, object_handle: ObjectHandle) -> Result<TpmHandle> {
380        use crate::tss2_esys::Esys_TR_GetTpmHandle;
381        let mut tpm_handle = TPM2_RH_UNASSIGNED;
382        ReturnCode::ensure_success(
383            unsafe {
384                Esys_TR_GetTpmHandle(self.mut_context(), object_handle.into(), &mut tpm_handle)
385            },
386            |ret| {
387                error!(
388                    "Error when getting TPM handle from ESYS handle: {:#010X}",
389                    ret
390                );
391            },
392        )?;
393        TpmHandle::try_from(tpm_handle)
394    }
395
396    /// Serialize the metadata of the object identified by `handle` into a new buffer.
397    ///
398    /// This can subsequently be used to recreate the object in the future.
399    /// The object can only be recreated in a new context, if it was made persistent
400    /// with `evict_control`.
401    ///
402    /// # Arguments
403    /// * `handle` - A handle to the object which should be serialized.
404    ///
405    /// # Returns
406    /// A buffer that can be stored and later deserialized.
407    ///
408    /// # Errors
409    /// * if the TPM cannot serialize the handle, a TSS error is returned.
410    /// * if the buffer length cannot be converted to a `usize`, an `InvalidParam`
411    ///   wrapper error is returned.
412    ///
413    /// ```rust
414    /// # use tss_esapi::{
415    /// #     Context, TctiNameConf,
416    /// #     interface_types::reserved_handles::Hierarchy,
417    /// #     structures::HashScheme,
418    /// #     utils::create_unrestricted_signing_ecc_public,
419    /// #     interface_types::{
420    /// #         ecc::EccCurve,
421    /// #         algorithm::HashingAlgorithm,
422    /// #         session_handles::AuthSession,
423    /// #     },
424    /// #     structures::EccScheme,
425    /// # };
426    /// # let mut context =
427    /// #     Context::new(
428    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
429    /// #     ).expect("Failed to create Context");
430    /// # context.set_sessions((Some(AuthSession::Password), None, None));
431    /// # let public = create_unrestricted_signing_ecc_public(
432    /// #     EccScheme::EcDsa(HashScheme::new(HashingAlgorithm::Sha256)),
433    /// #     EccCurve::NistP256)
434    /// #     .unwrap();
435    /// let key_handle = context
436    ///     .create_primary(
437    ///         Hierarchy::Owner,
438    ///         public,
439    ///         None,
440    ///         None,
441    ///         None,
442    ///         None,
443    ///     ).unwrap()
444    ///     .key_handle;
445    /// let data = context.tr_serialize(key_handle.into()).unwrap();
446    /// ```
447    pub fn tr_serialize(&mut self, handle: ObjectHandle) -> Result<Vec<u8>> {
448        let mut len = 0;
449        let mut buffer: *mut u8 = null_mut();
450        ReturnCode::ensure_success(
451            unsafe { Esys_TR_Serialize(self.mut_context(), handle.into(), &mut buffer, &mut len) },
452            |ret| {
453                error!("Error while serializing handle: {}", ret);
454            },
455        )?;
456        Ok(to_owned_bytes(
457            buffer,
458            len.try_into().map_err(|e| {
459                error!("Failed to convert buffer len to usize: {}", e);
460                Error::local_error(WrapperErrorKind::InvalidParam)
461            })?,
462        ))
463    }
464
465    /// Deserialize the metadata from `buffer` into a new object.
466    ///
467    /// This can be used to restore an object from a context in the past.
468    ///
469    /// # Arguments
470    /// * `buffer` - The buffer containing the data to restore the object.
471    ///   It can be created using [`tr_serialize`](Self::tr_serialize).
472    ///
473    /// # Returns
474    /// A handle to the object that was created from the buffer.
475    ///
476    /// # Errors
477    /// * if the TPM cannot deserialize the buffer, a TSS error is returned.
478    /// * if the buffer length cannot be converted to a `usize`, an `InvalidParam`
479    ///   wrapper error is returned.
480    ///
481    /// ```rust
482    /// # use tss_esapi::{
483    /// #     Context, TctiNameConf,
484    /// #     interface_types::reserved_handles::Hierarchy,
485    /// #     structures::HashScheme,
486    /// #     utils::create_unrestricted_signing_ecc_public,
487    /// #     interface_types::{
488    /// #         ecc::EccCurve,
489    /// #         algorithm::HashingAlgorithm,
490    /// #         session_handles::AuthSession,
491    /// #     },
492    /// #     structures::EccScheme,
493    /// # };
494    /// # let mut context =
495    /// #     Context::new(
496    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
497    /// #     ).expect("Failed to create Context");
498    /// # context.set_sessions((Some(AuthSession::Password), None, None));
499    /// # let public = create_unrestricted_signing_ecc_public(
500    /// #     EccScheme::EcDsa(HashScheme::new(HashingAlgorithm::Sha256)),
501    /// #     EccCurve::NistP256)
502    /// #     .unwrap();
503    /// let key_handle = context
504    ///     .create_primary(
505    ///         Hierarchy::Owner,
506    ///         public,
507    ///         None,
508    ///         None,
509    ///         None,
510    ///         None,
511    ///     ).unwrap()
512    ///     .key_handle;
513    /// # context.set_sessions((None, None, None));
514    /// let public_key = context.read_public(key_handle).unwrap();
515    /// let data = context.tr_serialize(key_handle.into()).unwrap();
516    /// let new_handle = context.tr_deserialize(&data).unwrap();
517    /// assert_eq!(public_key, context.read_public(new_handle.into()).unwrap());
518    /// ```
519    pub fn tr_deserialize(&mut self, buffer: &[u8]) -> Result<ObjectHandle> {
520        let mut handle = TPM2_RH_UNASSIGNED;
521        let size = FfiSizeType::try_from(buffer.len())?;
522        ReturnCode::ensure_success(
523            unsafe {
524                Esys_TR_Deserialize(
525                    self.mut_context(),
526                    buffer.as_ptr(),
527                    size.into(),
528                    &mut handle,
529                )
530            },
531            |ret| {
532                error!("Error while deserializing buffer: {}", ret);
533            },
534        )?;
535        Ok(ObjectHandle::from(handle))
536    }
537}