Skip to main content

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