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