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}