Skip to main content

tss_esapi/abstraction/transient/
mod.rs

1// Copyright 2019 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Module for abstracting resource handle management
5//!
6//! This module presents an abstraction over the TPM functionality exposed through the core
7//! `Context` structure. The abstraction works by hiding resource handle management from the
8//! client.
9use crate::{
10    attributes::{ObjectAttributesBuilder, SessionAttributesBuilder},
11    constants::{SessionType, TpmFormatZeroError},
12    error::{TpmFormatZeroResponseCode, TpmResponseCode},
13    handles::{KeyHandle, ObjectHandle, SessionHandle},
14    interface_types::{
15        algorithm::{HashingAlgorithm, PublicAlgorithm},
16        ecc::EccCurve,
17        key_bits::RsaKeyBits,
18        reserved_handles::Hierarchy,
19    },
20    structures::{
21        Auth, CreateKeyResult, Data, Digest, EccPoint, EccScheme, Name, Public, PublicBuilder,
22        PublicEccParametersBuilder, PublicKeyRsa, PublicRsaParametersBuilder, RsaExponent,
23        RsaScheme, SavedTpmContext, Signature, SignatureScheme, SymmetricDefinitionObject,
24        VerifiedTicket,
25    },
26    tcti_ldr::TctiNameConf,
27    utils::{create_restricted_decryption_rsa_public, PublicKey},
28    Context, Error, Result, ReturnCode, WrapperErrorKind as ErrorKind,
29};
30
31use log::error;
32use std::collections::HashMap;
33use std::convert::{AsMut, AsRef, TryFrom, TryInto};
34use zeroize::Zeroize;
35
36mod key_attestation;
37
38pub use key_attestation::MakeCredParams;
39
40/// Parameters for the kinds of keys supported by the context
41#[derive(Debug, Clone, Copy)]
42pub enum KeyParams {
43    Rsa {
44        /// Size of key in bits
45        ///
46        /// Can only be one of: 1024, 2048, 3072 or 4096
47        size: RsaKeyBits,
48        /// Asymmetric scheme to be used with the key
49        scheme: RsaScheme,
50        /// Public exponent of the key
51        ///
52        /// If set to 0, it will default to 2^16 - 1.
53        ///
54        /// (Note that the default value for [`RsaExponent`] is 0)
55        pub_exponent: RsaExponent,
56    },
57    Ecc {
58        /// Curve that the key will be based on
59        curve: EccCurve,
60        /// Asymmetric scheme to be used with the key
61        scheme: EccScheme,
62    },
63}
64
65/// Structure representing a key created or stored in the TPM
66///
67/// # Details
68/// The `public` field represents the public part of the key in plain text,
69/// while `private` is the encrypted version of the private key.
70///
71/// For information on public key formats, see the documentation of [`PublicKey`].
72/// The private part of the key should be treated as an opaque binary blob.
73///
74/// This object can be serialized and deserialized
75/// using serde if the `serde` feature is enabled.
76///
77/// # Warning
78///
79/// If the Owner hierarchy is cleared, any key material generated
80/// prior to that event will become unusable.
81#[derive(Debug, Clone, Zeroize)]
82#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
83pub struct KeyMaterial {
84    public: PublicKey,
85    private: Vec<u8>,
86}
87
88impl KeyMaterial {
89    /// Get a reference to the public part of the key
90    pub fn public(&self) -> &PublicKey {
91        &self.public
92    }
93
94    /// Get a reference to the private part of the key
95    pub fn private(&self) -> &[u8] {
96        &self.private
97    }
98}
99
100/// Structure containing all the defining elements of a TPM key
101///
102/// - `material` identifies the numeric value of the key object
103/// - `params` identifies the algorithm to use on the key and other relevant
104///   parameters
105/// - `auth` identifies the optional authentication value to be used with the
106///   key
107#[derive(Debug, Clone)]
108pub struct ObjectWrapper {
109    pub material: KeyMaterial,
110    pub params: KeyParams,
111    pub auth: Option<Auth>,
112}
113
114/// Structure offering an abstracted programming experience.
115///
116/// The `TransientKeyContext` makes use of a root key from which the other, client-controlled
117/// keys are derived.
118///
119/// This abstraction makes public key cryptography more accessible, focusing on asymmetric
120/// encryption and signatures in particular, by allowing users to offload object and session management.
121#[allow(clippy::module_name_repetitions)]
122#[derive(Debug)]
123pub struct TransientKeyContext {
124    context: Context,
125    root_key_handle: KeyHandle,
126}
127
128impl TransientKeyContext {
129    /// Create a new key.
130    ///
131    /// A key is created as a descendant of the context root key, with the given parameters.
132    ///
133    /// If successful, the result contains the [KeyMaterial] of the key and a vector of
134    /// bytes forming the authentication value for said key.
135    ///
136    /// The following key attributes are always **set**: `fixed_tpm`, `fixed_parent`, `sensitive_data_origin`,
137    /// `user_with_auth`. The `restricted` attribute is **not set**. See section 8.3 in the Structures
138    /// spec for a detailed description of these attributes.
139    ///
140    /// # Constraints
141    /// * `auth_size` must be at most 32
142    ///
143    /// # Errors
144    /// * if the authentication size is larger than 32 a `WrongParamSize` wrapper error is returned
145    /// * if there is an error when obtaining random numbers from the local machine
146    pub fn create_key(
147        &mut self,
148        key_params: KeyParams,
149        auth_size: usize,
150    ) -> Result<(KeyMaterial, Option<Auth>)> {
151        if auth_size > 32 {
152            return Err(Error::local_error(ErrorKind::WrongParamSize));
153        }
154        let key_auth = if auth_size > 0 {
155            self.set_session_attrs()?;
156            let mut random_bytes = vec![0u8; auth_size];
157            getrandom::getrandom(&mut random_bytes).map_err(|_| {
158                log::error!("Failed to obtain a random authvalue for key creation");
159                Error::WrapperError(ErrorKind::InternalError)
160            })?;
161            Some(Auth::from_bytes(random_bytes.as_slice())?)
162        } else {
163            None
164        };
165
166        self.set_session_attrs()?;
167        let CreateKeyResult {
168            out_private,
169            out_public,
170            ..
171        } = self.context.create(
172            self.root_key_handle,
173            TransientKeyContext::get_public_from_params(key_params, None)?,
174            key_auth.clone(),
175            None,
176            None,
177            None,
178        )?;
179
180        let key_material = KeyMaterial {
181            public: out_public.try_into()?,
182            private: out_private.as_bytes().to_vec(),
183        };
184        Ok((key_material, key_auth))
185    }
186
187    /// Load the public part of a key.
188    ///
189    /// Returns the appropriate key material after verifying that the key can be loaded.
190    pub fn load_external_public_key(
191        &mut self,
192        public_key: PublicKey,
193        params: KeyParams,
194    ) -> Result<KeyMaterial> {
195        let public = TransientKeyContext::get_public_from_params(params, Some(public_key.clone()))?;
196        self.set_session_attrs()?;
197        let key_handle = self.context.load_external(None, public, Hierarchy::Owner)?;
198        self.context.flush_context(key_handle.into())?;
199        Ok(KeyMaterial {
200            public: public_key,
201            private: vec![],
202        })
203    }
204
205    /// Encrypt a message with an existing key.
206    ///
207    /// Takes the key as a set of parameters (`key_material`, `key_params`, `key_auth`), encrypts the message
208    /// and returns the ciphertext. A label can also be provided which will be associated with the ciphertext.
209    ///
210    /// Note: the data passed as `label` MUST end in a `0x00` byte.
211    pub fn rsa_encrypt(
212        &mut self,
213        key_material: KeyMaterial,
214        key_params: KeyParams,
215        key_auth: Option<Auth>,
216        message: PublicKeyRsa,
217        label: Option<Data>,
218    ) -> Result<PublicKeyRsa> {
219        let key_handle = self.load_key(key_params, key_material, key_auth)?;
220        let decrypt_scheme = if let KeyParams::Rsa { scheme, .. } = key_params {
221            scheme.try_into()?
222        } else {
223            return Err(Error::local_error(ErrorKind::InvalidParam));
224        };
225
226        self.set_session_attrs()?;
227        let ciphertext = self
228            .context
229            .rsa_encrypt(
230                key_handle,
231                message,
232                decrypt_scheme,
233                label.unwrap_or_default(),
234            )
235            .or_else(|e| {
236                self.context.flush_context(key_handle.into())?;
237                Err(e)
238            })?;
239
240        self.context.flush_context(key_handle.into())?;
241
242        Ok(ciphertext)
243    }
244
245    /// Decrypt ciphertext with an existing key.
246    ///
247    /// Takes the key as a set of parameters (`key_material`, `key_params`, `key_auth`), decrypts the ciphertext
248    /// and returns the plaintext. A label which was associated with the ciphertext can also be provided.
249    ///
250    /// Note: the data passed as `label` MUST end in a `0x00` byte.
251    pub fn rsa_decrypt(
252        &mut self,
253        key_material: KeyMaterial,
254        key_params: KeyParams,
255        key_auth: Option<Auth>,
256        ciphertext: PublicKeyRsa,
257        label: Option<Data>,
258    ) -> Result<PublicKeyRsa> {
259        let key_handle = self.load_key(key_params, key_material, key_auth)?;
260        let decrypt_scheme = if let KeyParams::Rsa { scheme, .. } = key_params {
261            scheme.try_into()?
262        } else {
263            return Err(Error::local_error(ErrorKind::InvalidParam));
264        };
265
266        self.set_session_attrs()?;
267        let plaintext = self
268            .context
269            .rsa_decrypt(
270                key_handle,
271                ciphertext,
272                decrypt_scheme,
273                label.unwrap_or_default(),
274            )
275            .or_else(|e| {
276                self.context.flush_context(key_handle.into())?;
277                Err(e)
278            })?;
279
280        self.context.flush_context(key_handle.into())?;
281
282        Ok(plaintext)
283    }
284
285    /// Sign a digest with an existing key.
286    ///
287    /// Takes the key as a set of parameters (`key_material`, `key_params`, `key_auth`), signs and returns the signature.
288    pub fn sign(
289        &mut self,
290        key_material: KeyMaterial,
291        key_params: KeyParams,
292        key_auth: Option<Auth>,
293        digest: Digest,
294    ) -> Result<Signature> {
295        let key_handle = self.load_key(key_params, key_material, key_auth)?;
296
297        self.set_session_attrs()?;
298        let signature = self
299            .context
300            .sign(key_handle, digest, SignatureScheme::Null, None)
301            .or_else(|e| {
302                self.context.flush_context(key_handle.into())?;
303                Err(e)
304            })?;
305        self.context.flush_context(key_handle.into())?;
306        Ok(signature)
307    }
308
309    /// Verify a signature against a digest.
310    ///
311    /// Given a digest, a key and a signature, this method returns a `Verified` ticket if the
312    /// verification was successful.
313    ///
314    /// # Errors
315    /// * if the verification fails (i.e. the signature is invalid), a TPM error is returned
316    pub fn verify_signature(
317        &mut self,
318        key_material: KeyMaterial,
319        key_params: KeyParams,
320        digest: Digest,
321        signature: Signature,
322    ) -> Result<VerifiedTicket> {
323        let key_handle = self.load_key(key_params, key_material, None)?;
324
325        self.set_session_attrs()?;
326        let verified = self
327            .context
328            .verify_signature(key_handle, digest, signature)
329            .or_else(|e| {
330                self.context.flush_context(key_handle.into())?;
331                Err(e)
332            })?;
333        self.context.flush_context(key_handle.into())?;
334        Ok(verified)
335    }
336
337    /// Perform a migration from the previous version of the TransientKeyContext.
338    ///
339    /// The original version of the TransientKeyContext used contexts of keys for
340    /// persistence. This method allows a key persisted in this way to be migrated
341    /// to the new format.
342    ///
343    /// The method determines on its own whether the loaded key was a keypair or
344    /// just a public key.
345    pub fn migrate_key_from_ctx(
346        &mut self,
347        context: SavedTpmContext,
348        auth: Option<Auth>,
349    ) -> Result<KeyMaterial> {
350        self.set_session_attrs()?;
351        let key_handle = self.context.context_load(context).map(KeyHandle::from)?;
352        if let Some(key_auth_value) = auth.clone() {
353            self.context
354                .tr_set_auth(key_handle.into(), key_auth_value)
355                .or_else(|e| {
356                    self.context.flush_context(key_handle.into())?;
357                    Err(e)
358                })?;
359        }
360
361        let (public, _, _) = self.context.read_public(key_handle).or_else(|e| {
362            self.context.flush_context(key_handle.into())?;
363            Err(e)
364        })?;
365        let private = self
366            .context
367            .object_change_auth(
368                key_handle.into(),
369                self.root_key_handle.into(),
370                auth.unwrap_or_default(),
371            )
372            .or_else(|e| {
373                if let Error::TssError(ReturnCode::Tpm(TpmResponseCode::FormatZero(
374                    TpmFormatZeroResponseCode::Error(error),
375                ))) = e
376                {
377                    // If we get `AuthUnavailable` it means the private part of the key has not been
378                    // loaded, and this is thus a public key
379                    if error.error_number() == TpmFormatZeroError::AuthUnavailable {
380                        return Ok(Default::default());
381                    }
382                }
383                error!("Getting private part of key failed.");
384                self.context.flush_context(key_handle.into())?;
385                Err(e)
386            })?;
387
388        let key_material = KeyMaterial {
389            public: public.try_into()?,
390            private: private.as_bytes().to_vec(),
391        };
392
393        self.context.flush_context(key_handle.into())?;
394        Ok(key_material)
395    }
396
397    /// Gets the name of the root key of the TransientKeyContext
398    pub fn get_root_key_name(&mut self) -> Result<Name> {
399        let obj_handle: ObjectHandle = self.root_key_handle.into();
400        self.context.tr_get_name(obj_handle)
401    }
402
403    /// Sets the encrypt and decrypt flags on the main session used by the context.
404    ///
405    /// # Errors
406    /// * if `Context::set_session_attr` returns an error, that error is propagated through
407    fn set_session_attrs(&mut self) -> Result<()> {
408        if let (Some(session), _, _) = self.context.sessions() {
409            let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new()
410                .with_decrypt(true)
411                .with_encrypt(true)
412                .build();
413            self.context.tr_sess_set_attributes(
414                session,
415                session_attributes,
416                session_attributes_mask,
417            )?;
418        }
419        Ok(())
420    }
421
422    /// Given the parameters for an asymmetric key, return its [Public] structure
423    ///
424    /// The public part of the key can optionally be inserted in the structure.
425    ///
426    /// # Errors
427    /// * if the public key and the parameters don't match, `InconsistentParams` is returned
428    fn get_public_from_params(params: KeyParams, pub_key: Option<PublicKey>) -> Result<Public> {
429        let decrypt_flag = matches!(
430            params,
431            KeyParams::Rsa {
432                scheme: RsaScheme::RsaEs,
433                ..
434            } | KeyParams::Rsa {
435                scheme: RsaScheme::Oaep(..),
436                ..
437            }
438        );
439        let object_attributes = ObjectAttributesBuilder::new()
440            .with_fixed_tpm(true)
441            .with_fixed_parent(true)
442            .with_sensitive_data_origin(true)
443            .with_user_with_auth(true)
444            .with_decrypt(decrypt_flag)
445            .with_sign_encrypt(true)
446            .with_restricted(false)
447            .build()?;
448
449        let mut pub_builder = PublicBuilder::new()
450            .with_public_algorithm(match params {
451                KeyParams::Ecc { .. } => PublicAlgorithm::Ecc,
452                KeyParams::Rsa { .. } => PublicAlgorithm::Rsa,
453            })
454            .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
455            .with_object_attributes(object_attributes);
456        match params {
457            KeyParams::Rsa {
458                size,
459                scheme,
460                pub_exponent,
461            } => {
462                let unique = pub_key
463                    .map(|pub_key| {
464                        if let PublicKey::Rsa(val) = pub_key {
465                            PublicKeyRsa::try_from(val)
466                        } else {
467                            Err(Error::local_error(ErrorKind::InconsistentParams))
468                        }
469                    })
470                    .transpose()?
471                    .unwrap_or_default();
472                pub_builder = pub_builder
473                    .with_rsa_parameters(
474                        PublicRsaParametersBuilder::new()
475                            .with_scheme(match scheme {
476                                RsaScheme::RsaSsa { .. } | RsaScheme::RsaPss { .. } => scheme,
477                                _ => RsaScheme::Null,
478                            })
479                            .with_key_bits(size)
480                            .with_exponent(pub_exponent)
481                            .with_is_signing_key(true)
482                            .with_is_decryption_key(decrypt_flag)
483                            .with_restricted(false)
484                            .build()?,
485                    )
486                    .with_rsa_unique_identifier(unique);
487            }
488            KeyParams::Ecc { scheme, curve } => {
489                let unique = pub_key
490                    .map(|pub_key| {
491                        if let PublicKey::Ecc { x, y } = pub_key {
492                            Ok(EccPoint::new(x.try_into()?, y.try_into()?))
493                        } else {
494                            Err(Error::local_error(ErrorKind::InconsistentParams))
495                        }
496                    })
497                    .transpose()?
498                    .unwrap_or_default();
499                pub_builder = pub_builder
500                    .with_ecc_parameters(
501                        PublicEccParametersBuilder::new_unrestricted_signing_key(scheme, curve)
502                            .build()?,
503                    )
504                    .with_ecc_unique_identifier(unique);
505            }
506        }
507        pub_builder.build()
508    }
509
510    /// Load a key into a TPM given its [KeyMaterial]
511    ///
512    /// If the key has only a public part, it is loaded accordingly in the Owner Hierarchy
513    fn load_key(
514        &mut self,
515        params: KeyParams,
516        material: KeyMaterial,
517        auth: Option<Auth>,
518    ) -> Result<KeyHandle> {
519        let public = TransientKeyContext::get_public_from_params(params, Some(material.public))?;
520
521        self.set_session_attrs()?;
522        let key_handle = if material.private.is_empty() {
523            self.context.load_external(None, public, Hierarchy::Owner)?
524        } else {
525            self.context
526                .load(self.root_key_handle, material.private.try_into()?, public)?
527        };
528        let key_auth_value = auth.unwrap_or_default();
529        if !key_auth_value.is_empty() {
530            self.context
531                .tr_set_auth(key_handle.into(), key_auth_value)
532                .or_else(|e| {
533                    self.context.flush_context(key_handle.into())?;
534                    Err(e)
535                })?;
536        }
537        Ok(key_handle)
538    }
539
540    /// Get a builder for the structure
541    pub fn builder() -> TransientKeyContextBuilder {
542        TransientKeyContextBuilder::new()
543    }
544}
545
546impl AsRef<Context> for TransientKeyContext {
547    fn as_ref(&self) -> &Context {
548        &self.context
549    }
550}
551
552impl AsMut<Context> for TransientKeyContext {
553    fn as_mut(&mut self) -> &mut Context {
554        &mut self.context
555    }
556}
557
558/// Build a new `TransientKeyContext`.
559///
560/// # Default values
561/// * TCTI: Device TCTI
562/// * Hierarchy: Owner hierarchy
563/// * Root key size: 2048 bits
564/// * Root key authentication size: 32 bytes
565/// * Hierarchy authentication value: Empty array of bytes
566/// * Session encryption cipher: 256 bit AES in CFB mode
567/// * Session hash algorithm: SHA256
568#[derive(Debug)]
569pub struct TransientKeyContextBuilder {
570    tcti_name_conf: TctiNameConf,
571    root_key_size: u16, // TODO: replace with root key PUBLIC definition
572    root_key_auth_size: usize,
573    root_hierarchy: Hierarchy,
574    hierarchy_auth: HashMap<Hierarchy, Vec<u8>>,
575    default_context_cipher: SymmetricDefinitionObject,
576    session_hash_alg: HashingAlgorithm,
577}
578
579impl TransientKeyContextBuilder {
580    /// Create a new builder.
581    pub fn new() -> Self {
582        TransientKeyContextBuilder {
583            tcti_name_conf: TctiNameConf::Device(Default::default()),
584            root_hierarchy: Hierarchy::Owner,
585            root_key_size: 2048,
586            root_key_auth_size: 32,
587            hierarchy_auth: HashMap::new(),
588            default_context_cipher: SymmetricDefinitionObject::AES_256_CFB,
589            session_hash_alg: HashingAlgorithm::Sha256,
590        }
591    }
592
593    /// Define the TCTI name configuration to be used by the client.
594    pub fn with_tcti(mut self, tcti_name_conf: TctiNameConf) -> Self {
595        self.tcti_name_conf = tcti_name_conf;
596        self
597    }
598
599    /// Set the auth values for any hierarchies that will be used
600    pub fn with_hierarchy_auth(mut self, hierarchy: Hierarchy, auth: Vec<u8>) -> Self {
601        let _ = self.hierarchy_auth.insert(hierarchy, auth);
602        self
603    }
604
605    /// Define which hierarchy will be used for the keys being managed.
606    pub fn with_root_hierarchy(mut self, hierarchy: Hierarchy) -> Self {
607        self.root_hierarchy = hierarchy;
608        self
609    }
610
611    /// Choose length in bits of primary key that will serve as parent to all user keys.
612    pub fn with_root_key_size(mut self, root_key_size: u16) -> Self {
613        self.root_key_size = root_key_size;
614        self
615    }
616
617    /// Choose authentication value length (in bytes) for primary key.
618    pub fn with_root_key_auth_size(mut self, root_key_auth_size: usize) -> Self {
619        self.root_key_auth_size = root_key_auth_size;
620        self
621    }
622
623    /// Define the cipher to be used within this context as a default.
624    ///
625    /// Currently this default is used for:
626    /// * securing command parameters using session-based encryption
627    /// * encrypting all user keys using the primary key
628    pub fn with_default_context_cipher(
629        mut self,
630        default_context_cipher: SymmetricDefinitionObject,
631    ) -> Self {
632        self.default_context_cipher = default_context_cipher;
633        self
634    }
635
636    /// Define the cipher to be used by sessions for hashing commands.
637    pub fn with_session_hash_alg(mut self, session_hash_alg: HashingAlgorithm) -> Self {
638        self.session_hash_alg = session_hash_alg;
639        self
640    }
641
642    /// Bootstrap the TransientKeyContext.
643    ///
644    /// The root key is created as a primary key in the provided hierarchy and thus authentication is
645    /// needed for said hierarchy. The authentication value for the key is generated locally in the machine,
646    /// with a configurable length, and never exposed outside the context.
647    ///
648    /// # Warning
649    /// It is the responsibility of the client to ensure that the context can be initialized
650    /// safely, threading-wise by choosing the correct TCTI. See the Warning notice of the Context
651    /// structure for more information.
652    ///
653    /// # Constraints
654    /// * `root_key_size` must be 1024, 2048, 3072 or 4096
655    /// * `root_key_auth_size` must be at most 32
656    ///
657    /// # Errors
658    /// * errors are returned if any method calls return an error: `Context::start_auth_session`
659    ///   `Context::create_primary`, `Context::flush_context`, `Context::set_handle_auth`
660    ///   or if an internal error occurs when getting random numbers from the local machine
661    /// * if the root key authentication size is given greater than 32 or if the root key size is
662    ///   not 1024, 2048, 3072 or 4096, a `InvalidParam` wrapper error is returned
663    pub fn build(mut self) -> Result<TransientKeyContext> {
664        if self.root_key_auth_size > 32 {
665            return Err(Error::local_error(ErrorKind::WrongParamSize));
666        }
667
668        let root_key_rsa_key_bits = RsaKeyBits::try_from(self.root_key_size)?;
669
670        let mut context = Context::new(self.tcti_name_conf)?;
671
672        let root_key_auth = if self.root_key_auth_size > 0 {
673            let mut random = vec![0u8; self.root_key_auth_size];
674            getrandom::getrandom(&mut random).map_err(|_| {
675                log::error!("Failed to obtain a random value for root key authentication");
676                Error::WrapperError(ErrorKind::InternalError)
677            })?;
678            Some(Auth::from_bytes(random.as_slice())?)
679        } else {
680            None
681        };
682
683        for (hierarchy, auth) in self.hierarchy_auth.drain() {
684            let auth_hierarchy = Auth::try_from(auth)?;
685            context.tr_set_auth(hierarchy.into(), auth_hierarchy)?;
686        }
687
688        let session = context
689            .start_auth_session(
690                None,
691                None,
692                None,
693                SessionType::Hmac,
694                self.default_context_cipher.into(),
695                self.session_hash_alg,
696            )
697            .and_then(|session| {
698                session.ok_or_else(|| {
699                    error!("Received unexpected NONE handle from the TPM");
700                    Error::local_error(ErrorKind::WrongValueFromTpm)
701                })
702            })?;
703        let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new()
704            .with_decrypt(true)
705            .with_encrypt(true)
706            .build();
707        context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)?;
708        context.set_sessions((Some(session), None, None));
709
710        let root_key_handle = context
711            .create_primary(
712                self.root_hierarchy,
713                create_restricted_decryption_rsa_public(
714                    self.default_context_cipher,
715                    root_key_rsa_key_bits,
716                    RsaExponent::ZERO_EXPONENT,
717                )?,
718                root_key_auth,
719                None,
720                None,
721                None,
722            )?
723            .key_handle;
724
725        let new_session_cipher = self.default_context_cipher;
726        let new_session_hashing_algorithm = self.session_hash_alg;
727        let new_session = context.execute_without_session(|ctx| {
728            ctx.start_auth_session(
729                Some(root_key_handle),
730                None,
731                None,
732                SessionType::Hmac,
733                new_session_cipher.into(),
734                new_session_hashing_algorithm,
735            )
736            .and_then(|session| {
737                session.ok_or_else(|| {
738                    error!("Received unexpected NONE handle from the TPM");
739                    Error::local_error(ErrorKind::WrongValueFromTpm)
740                })
741            })
742        })?;
743        if let (Some(old_session), _, _) = context.sessions() {
744            context.set_sessions((Some(new_session), None, None));
745            context.flush_context(SessionHandle::from(old_session).into())?;
746        }
747        Ok(TransientKeyContext {
748            context,
749            root_key_handle,
750        })
751    }
752}
753
754impl Default for TransientKeyContextBuilder {
755    fn default() -> Self {
756        TransientKeyContextBuilder::new()
757    }
758}