tss_esapi/context/tpm_commands/
context_management.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    context::handle_manager::HandleDropAction,
5    handles::{handle_conversion::TryIntoNotNone, AuthHandle, ObjectHandle, PersistentTpmHandle},
6    interface_types::{dynamic_handles::Persistent, resource_handles::Provision},
7    tss2_esys::{Esys_ContextLoad, Esys_ContextSave, Esys_EvictControl, Esys_FlushContext},
8    utils::TpmsContext,
9    Context, Error, Result,
10};
11use log::error;
12use std::convert::{TryFrom, TryInto};
13use std::ptr::null_mut;
14
15impl Context {
16    /// Save the context of an object from the TPM and return it.
17    ///
18    /// # Errors
19    /// * if conversion from `TPMS_CONTEXT` to `TpmsContext` fails, a `WrongParamSize` error will
20    ///   be returned
21    pub fn context_save(&mut self, handle: ObjectHandle) -> Result<TpmsContext> {
22        let mut context_ptr = null_mut();
23        let ret = unsafe { Esys_ContextSave(self.mut_context(), handle.into(), &mut context_ptr) };
24        let ret = Error::from_tss_rc(ret);
25        if ret.is_success() {
26            TpmsContext::try_from(Context::ffi_data_to_owned(context_ptr))
27        } else {
28            error!("Error in saving context: {}", ret);
29            Err(ret)
30        }
31    }
32
33    /// Load a previously saved context into the TPM and return the object handle.
34    ///
35    /// # Errors
36    /// * if conversion from `TpmsContext` to the native `TPMS_CONTEXT` fails, a `WrongParamSize`
37    ///   error will be returned
38    pub fn context_load(&mut self, context: TpmsContext) -> Result<ObjectHandle> {
39        let mut loaded_handle = ObjectHandle::None.into();
40        let ret = unsafe {
41            Esys_ContextLoad(self.mut_context(), &context.try_into()?, &mut loaded_handle)
42        };
43        let ret = Error::from_tss_rc(ret);
44        if ret.is_success() {
45            self.handle_manager
46                .add_handle(loaded_handle.into(), HandleDropAction::Flush)?;
47            Ok(loaded_handle.into())
48        } else {
49            error!("Error in loading context: {}", ret);
50            Err(ret)
51        }
52    }
53
54    /// Flush the context of an object from the TPM.
55    ///
56    /// # Example
57    ///
58    /// ```rust
59    /// # use tss_esapi::{
60    /// #     Context, tcti_ldr::TctiNameConf, structures::Auth,
61    /// #     constants::{
62    /// #         tss::{TPMA_SESSION_DECRYPT, TPMA_SESSION_ENCRYPT},
63    /// #         SessionType,
64    /// #     },
65    /// #     interface_types::{
66    /// #         resource_handles::Hierarchy,
67    /// #         algorithm::{HashingAlgorithm, RsaSchemeAlgorithm},
68    /// #         key_bits::RsaKeyBits,
69    /// #     },
70    /// #     utils::create_unrestricted_signing_rsa_public,
71    /// #     attributes::SessionAttributesBuilder,
72    /// #     structures::{SymmetricDefinition, RsaExponent, RsaScheme},
73    /// # };
74    /// # use std::convert::TryFrom;
75    /// # use std::str::FromStr;
76    /// # // Create context
77    /// # let mut context =
78    /// #     Context::new(
79    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
80    /// #     ).expect("Failed to create Context");
81    ///
82    /// // Create session for a key
83    /// let session = context
84    ///     .start_auth_session(
85    ///         None,
86    ///         None,
87    ///         None,
88    ///         SessionType::Hmac,
89    ///         SymmetricDefinition::AES_256_CFB,
90    ///         HashingAlgorithm::Sha256,
91    ///     )
92    ///     .expect("Failed to create session")
93    ///     .expect("Received invalid handle");
94    /// let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new()
95    ///     .with_decrypt(true)
96    ///     .with_encrypt(true)
97    ///     .build();
98    /// context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
99    ///     .expect("Failed to set attributes on session");
100    ///
101    /// // Create public area for a rsa key
102    /// let public_area = create_unrestricted_signing_rsa_public(
103    ///         RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
104    ///             .expect("Failed to create RSA scheme"),
105    ///         RsaKeyBits::Rsa2048,
106    ///         RsaExponent::default(),
107    ///     )
108    ///     .expect("Failed to create rsa public area");
109    ///
110    /// // Execute context methods using the session
111    /// context.execute_with_session(Some(session), |ctx| {
112    ///     let mut random_digest = vec![0u8; 16];
113    ///     getrandom::getrandom(&mut random_digest).expect("Call to getrandom failed");
114    ///     let key_auth = Auth::try_from(random_digest).expect("Failed to create Auth");
115    ///     let key_handle = ctx
116    ///         .create_primary(
117    ///             Hierarchy::Owner,
118    ///             public_area,
119    ///             Some(key_auth),
120    ///             None,
121    ///             None,
122    ///             None,
123    ///         )
124    ///         .expect("Failed to create primary key")
125    ///         .key_handle;
126    ///
127    ///         // Flush the context of the key.
128    ///         ctx.flush_context(key_handle.into()).expect("Call to flush_context failed");
129    ///         assert!(ctx.read_public(key_handle).is_err());
130    /// })
131    /// ```
132    pub fn flush_context(&mut self, handle: ObjectHandle) -> Result<()> {
133        let ret = unsafe { Esys_FlushContext(self.mut_context(), handle.try_into_not_none()?) };
134        let ret = Error::from_tss_rc(ret);
135        if ret.is_success() {
136            self.handle_manager.set_as_flushed(handle)?;
137            Ok(())
138        } else {
139            error!("Error in flushing context: {}", ret);
140            Err(ret)
141        }
142    }
143
144    /// Evicts persistent objects or allows certain transient objects
145    /// to be made persistent.
146    ///
147    /// # Details
148    /// In order to be able to perform this action an authorization
149    /// session is required.
150    ///
151    /// # Arguments
152    /// * `auth` - An a handle used for authorization that is limited to the ones
153    ///            specified in [Provision].
154    /// * `object_handle` - The handle of a loaded object.
155    /// * `persistent` - If the `object_handle` is transient object then this
156    ///                  then this will become the persistent handle of that
157    ///                  object. If the `object_handle` refers to a persistent
158    ///                  object then this shall be the persistent handle of that
159    ///                  object.
160    ///
161    /// # Returns
162    /// If the input `object_handle` was transient object then it will be made
163    /// persistent and the returned [ObjectHandle] will refer to the persistent
164    /// object.
165    ///
166    /// If the input `object_handle` refers to a persistent object the returned
167    /// value will be ObjectHandle::None and the input `object_handle` will not
168    /// be valid after this call is made.
169    ///
170    /// # Example
171    ///
172    /// Make transient object persistent:
173    /// ```rust
174    /// # use tss_esapi::{
175    /// #     Context, tcti_ldr::TctiNameConf, Result,
176    /// #     constants::{
177    /// #         SessionType, CapabilityType,
178    /// #         tss::TPM2_PERSISTENT_FIRST,
179    /// #     },
180    /// #     handles::PcrHandle,
181    /// #     structures::{Digest, CapabilityData, Auth, RsaExponent, SymmetricDefinitionObject},
182    /// #     interface_types::{
183    /// #       resource_handles::Hierarchy,
184    /// #       key_bits::RsaKeyBits,
185    /// #     },
186    /// #     handles::{ObjectHandle, TpmHandle, PersistentTpmHandle},
187    /// #     utils::create_restricted_decryption_rsa_public,
188    /// #     tss2_esys::TPM2_HANDLE,
189    /// # };
190    /// # use std::{env, str::FromStr, convert::TryFrom};
191    /// # // Create context
192    /// # let mut context =
193    /// #     Context::new(
194    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
195    /// #     ).expect("Failed to create Context");
196    /// # // Create persistent TPM handle with
197    /// # let persistent_tpm_handle =
198    /// #    PersistentTpmHandle::new(u32::from_be_bytes([0x81, 0x00, 0x00, 0x01]))
199    /// #        .expect("Failed to create Persistent TPM handle");
200    /// # // -----> REMOVE ANY PREVIOUS HANDLES <---------------
201    /// # let mut property = TPM2_PERSISTENT_FIRST;
202    /// # while let Ok((capability_data, more_data_available)) =
203    /// #     context.get_capability(CapabilityType::Handles, property, 1)
204    /// # {
205    /// #     if let CapabilityData::Handles(persistent_handles) = capability_data {
206    /// #         if let Some(&retrieved_persistent_handle) = persistent_handles.first() {
207    /// #             if retrieved_persistent_handle == persistent_tpm_handle.into() {
208    /// #                 let handle = context
209    /// #                     .tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
210    /// #                     .expect("Failed to retrieve handle from TPM");
211    /// #                 context.execute_with_session(Some(tss_esapi::interface_types::session_handles::AuthSession::Password), |ctx| {
212    /// #                     ctx
213    /// #                           .evict_control(
214    /// #                               tss_esapi::interface_types::resource_handles::Provision::Owner,
215    /// #                               handle,
216    /// #                               tss_esapi::interface_types::dynamic_handles::Persistent::Persistent(persistent_tpm_handle),
217    /// #                           )
218    /// #                           .expect("Failed to evict persitent handle")
219    /// #                 });
220    /// #                 break;
221    /// #             }
222    /// #             if more_data_available {
223    /// #                 property = TPM2_HANDLE::from(retrieved_persistent_handle) + 1;
224    /// #             }
225    /// #         }
226    /// #     }
227    /// #     if !more_data_available {
228    /// #         break;
229    /// #     }
230    /// # }
231    /// # let transient_object_handle = context.execute_with_session(Some(tss_esapi::interface_types::session_handles::AuthSession::Password), |ctx| {
232    /// #    // Create primary key handle
233    /// #    let auth_value_primary = Auth::try_from(vec![1, 2, 3, 4, 5])
234    /// #        .expect("Failed to crate auth value for primary key");
235    /// #    ctx
236    /// #        .create_primary(
237    /// #            Hierarchy::Owner,
238    /// #            create_restricted_decryption_rsa_public(
239    /// #               SymmetricDefinitionObject::AES_256_CFB,
240    /// #               RsaKeyBits::Rsa2048,
241    /// #               RsaExponent::default(),
242    /// #            ).expect("Failed to Public structure for key"),
243    /// #            Some(auth_value_primary),
244    /// #            None,
245    /// #            None,
246    /// #            None,
247    /// #        )
248    /// #        .map(|v| ObjectHandle::from(v.key_handle))
249    /// #        .expect("Failed to create primary key")
250    /// # });
251    /// use tss_esapi::{
252    ///     interface_types::{
253    ///         resource_handles::Provision,
254    ///         dynamic_handles::Persistent,
255    ///         session_handles::AuthSession,
256    ///     },
257    /// };
258    /// // Create interface type Persistent by using the persistent tpm handle.
259    /// let persistent = Persistent::Persistent(persistent_tpm_handle);
260    /// // Make transient_object_handle persistent.
261    /// // An authorization session is required!
262    /// let mut persistent_object_handle = context.execute_with_session(Some(AuthSession::Password), |ctx| {
263    ///     ctx
264    ///         .evict_control(Provision::Owner, transient_object_handle.into(), persistent)
265    ///         .expect("Failed to make the transient_object_handle handle persistent")
266    /// });
267    /// # assert_ne!(persistent_object_handle, ObjectHandle::Null);
268    /// # assert_ne!(persistent_object_handle, ObjectHandle::None);
269    /// # // Flush out the transient_object_handle
270    /// # context
271    /// #     .flush_context(ObjectHandle::from(transient_object_handle))
272    /// #     .expect("Failed to flush context");
273    /// # // Close the persistent_handle returned by evict_control
274    /// # context
275    /// #     .tr_close(&mut persistent_object_handle)
276    /// #     .expect("Failed to close persistent handle");
277    /// # // Retrieve the handle from the tpm again.
278    /// # let retireved_persistent_handle = context.execute_without_session(|ctx| {
279    /// #     ctx.tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
280    /// #         .expect("Failed to load the persistent handle")
281    /// # });
282    /// # // Evict the persitent handle from the tpm
283    /// # let _ = context.execute_with_session(Some(AuthSession::Password), |ctx| {
284    /// #   ctx
285    /// #       .evict_control(Provision::Owner, retireved_persistent_handle, persistent)
286    /// #       .expect("Failed to evict persistent handle")
287    /// # });
288    /// # assert_ne!(retireved_persistent_handle, ObjectHandle::None);
289    /// ```
290    ///
291    /// Make persistent object transient
292    /// ```rust
293    /// # use tss_esapi::{
294    /// #     Context, tcti_ldr::TctiNameConf, Result,
295    /// #     constants::{
296    /// #         SessionType, CapabilityType,
297    /// #         tss::TPM2_PERSISTENT_FIRST,
298    /// #     },
299    /// #     handles::PcrHandle,
300    /// #     structures::{Digest, CapabilityData, Auth, RsaExponent, SymmetricDefinitionObject},
301    /// #     interface_types::{
302    /// #       resource_handles::Hierarchy,
303    /// #       key_bits::RsaKeyBits,
304    /// #     },
305    /// #     handles::{ObjectHandle, TpmHandle, PersistentTpmHandle},
306    /// #     utils::create_restricted_decryption_rsa_public,
307    /// #     tss2_esys::TPM2_HANDLE,
308    /// # };
309    /// # use std::{env, str::FromStr, convert::TryFrom};
310    /// # // Create context
311    /// # let mut context =
312    /// #     Context::new(
313    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
314    /// #     ).expect("Failed to create Context");
315    /// # // Create persistent TPM handle with
316    /// # let persistent_tpm_handle =
317    /// #    PersistentTpmHandle::new(u32::from_be_bytes([0x81, 0x00, 0x00, 0x01]))
318    /// #        .expect("Failed to create Persistent TPM handle");
319    /// # // -----> REMOVE ANY PREVIOUS HANDLES <---------------
320    /// # let mut property = TPM2_PERSISTENT_FIRST;
321    /// # while let Ok((capability_data, more_data_available)) =
322    /// #     context.get_capability(CapabilityType::Handles, property, 1)
323    /// # {
324    /// #     if let CapabilityData::Handles(persistent_handles) = capability_data {
325    /// #         if let Some(&retrieved_persistent_handle) = persistent_handles.first() {
326    /// #             if retrieved_persistent_handle == persistent_tpm_handle.into() {
327    /// #                 let handle = context
328    /// #                     .tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
329    /// #                     .expect("Failed to retrieve handle from TPM");
330    /// #                 context.execute_with_session(Some(tss_esapi::interface_types::session_handles::AuthSession::Password), |ctx| {
331    /// #                     ctx
332    /// #                           .evict_control(
333    /// #                               tss_esapi::interface_types::resource_handles::Provision::Owner,
334    /// #                               handle,
335    /// #                               tss_esapi::interface_types::dynamic_handles::Persistent::Persistent(persistent_tpm_handle),
336    /// #                           )
337    /// #                           .expect("Failed to evict persitent handle")
338    /// #                 });
339    /// #                 break;
340    /// #             }
341    /// #             if more_data_available {
342    /// #                 property = TPM2_HANDLE::from(retrieved_persistent_handle) + 1;
343    /// #             }
344    /// #         }
345    /// #     }
346    /// #     if !more_data_available {
347    /// #         break;
348    /// #     }
349    /// # }
350    /// # let transient_object_handle = context.execute_with_session(Some(tss_esapi::interface_types::session_handles::AuthSession::Password), |ctx| {
351    /// #    // Create primary key handle
352    /// #    let auth_value_primary = Auth::try_from(vec![1, 2, 3, 4, 5])
353    /// #        .expect("Failed to crate auth value for primary key");
354    /// #    ctx
355    /// #        .create_primary(
356    /// #            Hierarchy::Owner,
357    /// #            create_restricted_decryption_rsa_public(
358    /// #               SymmetricDefinitionObject::AES_256_CFB,
359    /// #               RsaKeyBits::Rsa2048,
360    /// #               RsaExponent::default(),
361    /// #            ).expect("Failed to Public structure for key"),
362    /// #            Some(auth_value_primary),
363    /// #            None,
364    /// #            None,
365    /// #            None,
366    /// #        )
367    /// #        .map(|v| ObjectHandle::from(v.key_handle))
368    /// #        .expect("Failed to create primary key")
369    /// # });
370    /// use tss_esapi::{
371    ///     interface_types::{
372    ///         resource_handles::Provision,
373    ///         dynamic_handles::Persistent,
374    ///         session_handles::AuthSession,
375    ///     },
376    /// };
377    /// // Create interface type Persistent by using the persistent tpm handle.
378    /// let persistent = Persistent::Persistent(persistent_tpm_handle);
379    /// # // Evict control to make transient_object_handle persistent.
380    /// # // An authorization session is required!
381    /// # let mut persistent_object_handle = context.execute_with_session(Some(AuthSession::Password), |ctx| {
382    /// #   ctx
383    /// #       .evict_control(Provision::Owner, transient_object_handle.into(), persistent)
384    /// #       .expect("Failed to make the transient_object_handle handle persistent")
385    /// # });
386    /// # assert_ne!(persistent_object_handle, ObjectHandle::Null);
387    /// # assert_ne!(persistent_object_handle, ObjectHandle::None);
388    /// # // Flush out the transient_object_handle
389    /// # context
390    /// #     .flush_context(ObjectHandle::from(transient_object_handle))
391    /// #     .expect("Failed to flush context");
392    /// # // Close the persistent_handle returned by evict_control
393    /// # context
394    /// #     .tr_close(&mut persistent_object_handle)
395    /// #     .expect("Failed to close persistent handle");
396    /// # // Retrieve the handle from the tpm again.
397    /// # let retrieved_persistent_handle = context.execute_without_session(|ctx| {
398    /// #     ctx.tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
399    /// #         .expect("Failed to load the persistent handle")
400    /// # });
401    /// // Evict the persitent handle from the tpm
402    /// // An authorization session is required!
403    /// let _ = context.execute_with_session(Some(AuthSession::Password), |ctx| {
404    ///     ctx
405    ///         .evict_control(Provision::Owner, retrieved_persistent_handle, persistent)
406    ///         .expect("Failed to evict persistent handle")
407    /// });
408    /// # assert_ne!(retrieved_persistent_handle, ObjectHandle::None);
409    /// ```
410    pub fn evict_control(
411        &mut self,
412        auth: Provision,
413        object_handle: ObjectHandle,
414        persistent: Persistent,
415    ) -> Result<ObjectHandle> {
416        let mut new_object_handle = ObjectHandle::None.into();
417        let ret = unsafe {
418            Esys_EvictControl(
419                self.mut_context(),
420                AuthHandle::from(auth).into(),
421                object_handle.into(),
422                self.required_session_1()?,
423                self.optional_session_2(),
424                self.optional_session_3(),
425                PersistentTpmHandle::from(persistent).into(),
426                &mut new_object_handle,
427            )
428        };
429        let ret = Error::from_tss_rc(ret);
430        if ret.is_success() {
431            let new_object_handle = ObjectHandle::from(new_object_handle);
432            // If you look at the specification and see that it says ESYS_TR_NULL
433            // then that is an error in the spec. ESYS_TR_NULL was renamed to
434            // ESYS_TR NONE.
435            if new_object_handle.is_none() {
436                self.handle_manager.set_as_closed(object_handle)?;
437            } else {
438                self.handle_manager
439                    .add_handle(new_object_handle, HandleDropAction::Close)?;
440            }
441            Ok(new_object_handle)
442        } else {
443            error!("Error in evict control: {}", ret);
444            Err(ret)
445        }
446    }
447}