Skip to main content

tss_esapi/abstraction/transient/
key_attestation.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use super::{ObjectWrapper, TransientKeyContext};
4use crate::{
5    abstraction::{ek, AsymmetricAlgorithmSelection},
6    constants::SessionType,
7    handles::{AuthHandle, KeyHandle, SessionHandle},
8    interface_types::{
9        algorithm::HashingAlgorithm,
10        key_bits::RsaKeyBits,
11        session_handles::{AuthSession, PolicySession},
12    },
13    structures::{EncryptedSecret, IdObject, SymmetricDefinition},
14    traits::Marshall,
15    utils::PublicKey,
16    Result,
17};
18use std::convert::TryFrom;
19
20#[derive(Debug)]
21/// Wrapper for the parameters needed by MakeCredential
22///
23/// The 3rd party requesting proof that the key is indeed backed
24/// by a TPM would perform a MakeCredential and would thus require
25/// `name` and `attesting_key_pub` as inputs for that operation.
26///
27/// `public` is not strictly needed, however it is returned as a
28/// convenience block of data. Since the MakeCredential operation
29/// bakes into the encrypted credential the identity of the key to
30/// be attested via its `name`, the correctness of the `name` must
31/// be verifiable by the said 3rd party. `public` bridges this gap:
32///
33/// * it includes all the public parameters of the attested key
34/// * can be hashed (in its marshaled form) with the name hash
35///   (found by unmarshaling it) to obtain `name`
36pub struct MakeCredParams {
37    /// TPM name of the object being attested
38    pub name: Vec<u8>,
39    /// Encoding of the public parameters of the object whose name
40    /// will be included in the credential computations
41    pub public: Vec<u8>,
42    /// Public part of the key used to protect the credential
43    pub attesting_key_pub: PublicKey,
44}
45
46impl TransientKeyContext {
47    /// Get the data required to perform a MakeCredential
48    ///
49    /// # Parameters
50    ///
51    /// * `object` - the object whose TPM name will be included in the credential
52    /// * `key` - the key to be used to encrypt the secret that wraps the credential
53    ///
54    /// **Note**: If no `key` is given, the default Endorsement Key
55    /// will be used.
56    pub fn get_make_cred_params(
57        &mut self,
58        object: ObjectWrapper,
59        key: Option<ObjectWrapper>,
60    ) -> Result<MakeCredParams> {
61        let object_handle = self.load_key(object.params, object.material, None)?;
62        let (object_public, object_name, _) =
63            self.context.read_public(object_handle).or_else(|e| {
64                self.context.flush_context(object_handle.into())?;
65                Err(e)
66            })?;
67        self.context.flush_context(object_handle.into())?;
68
69        // Name of objects is derived from their publicArea, i.e. the marshaled TPMT_PUBLIC
70        let public = object_public.marshall()?;
71
72        let attesting_key_pub = match key {
73            None => get_ek_object_public(&mut self.context)?,
74            Some(key) => key.material.public,
75        };
76        Ok(MakeCredParams {
77            name: object_name.value().to_vec(),
78            public,
79            attesting_key_pub,
80        })
81    }
82
83    /// Perform an ActivateCredential operation for the given object
84    ///
85    /// # Parameters
86    ///
87    /// * `object` - the object whose TPM name is included in the credential
88    /// * `key` - the key used to encrypt the secret that wraps the credential
89    /// * `credential_blob` - encrypted credential that will be returned by the TPM
90    /// * `secret` - encrypted secret that was used to encrypt the credential
91    ///
92    /// **Note**: if no `key` is given, the default Endorsement Key
93    /// will be used. You can find more information about the default Endorsement
94    /// Key in the [ek] module.
95    pub fn activate_credential(
96        &mut self,
97        object: ObjectWrapper,
98        key: Option<ObjectWrapper>,
99        credential_blob: Vec<u8>,
100        secret: Vec<u8>,
101    ) -> Result<Vec<u8>> {
102        let credential_blob = IdObject::try_from(credential_blob)?;
103        let secret = EncryptedSecret::try_from(secret)?;
104        let object_handle = self.load_key(object.params, object.material, object.auth)?;
105        let (key_handle, session_2) = match key {
106            Some(key) => self.prepare_key_activate_cred(key),
107            None => self.prepare_ek_activate_cred(),
108        }
109        .or_else(|e| {
110            self.context.flush_context(object_handle.into())?;
111            Err(e)
112        })?;
113
114        let (session_1, _, _) = self.context.sessions();
115        let credential = self
116            .context
117            .execute_with_sessions((session_1, session_2, None), |ctx| {
118                ctx.activate_credential(object_handle, key_handle, credential_blob, secret)
119            })
120            .or_else(|e| {
121                self.context.flush_context(object_handle.into())?;
122                self.context.flush_context(key_handle.into())?;
123                self.context
124                    .flush_context(SessionHandle::from(session_2).into())?;
125                Err(e)
126            })?;
127
128        self.context.flush_context(object_handle.into())?;
129        self.context.flush_context(key_handle.into())?;
130        self.context
131            .flush_context(SessionHandle::from(session_2).into())?;
132        Ok(credential.as_bytes().to_vec())
133    }
134
135    // No key was given, use the EK. This requires using a Policy session
136    fn prepare_ek_activate_cred(&mut self) -> Result<(KeyHandle, Option<AuthSession>)> {
137        let session = self.context.start_auth_session(
138            None,
139            None,
140            None,
141            SessionType::Policy,
142            SymmetricDefinition::AES_128_CFB,
143            HashingAlgorithm::Sha256,
144        )?;
145        let _ = self.context.policy_secret(
146            PolicySession::try_from(session.unwrap())
147                .expect("Failed to convert auth session to policy session"),
148            AuthHandle::Endorsement,
149            Default::default(),
150            Default::default(),
151            Default::default(),
152            None,
153        );
154        Ok((
155            ek::create_ek_object(
156                &mut self.context,
157                AsymmetricAlgorithmSelection::Rsa(RsaKeyBits::Rsa2048),
158                None,
159            )
160            .or_else(|e| {
161                self.context
162                    .flush_context(SessionHandle::from(session).into())?;
163                Err(e)
164            })?,
165            session,
166        ))
167    }
168
169    // Load key and create a HMAC session for it
170    fn prepare_key_activate_cred(
171        &mut self,
172        key: ObjectWrapper,
173    ) -> Result<(KeyHandle, Option<AuthSession>)> {
174        let session = self.context.start_auth_session(
175            None,
176            None,
177            None,
178            SessionType::Hmac,
179            SymmetricDefinition::AES_128_CFB,
180            HashingAlgorithm::Sha256,
181        )?;
182        Ok((
183            self.load_key(key.params, key.material, key.auth)
184                .or_else(|e| {
185                    self.context
186                        .flush_context(SessionHandle::from(session).into())?;
187                    Err(e)
188                })?,
189            session,
190        ))
191    }
192}
193
194fn get_ek_object_public(context: &mut crate::Context) -> Result<PublicKey> {
195    let key_handle = ek::create_ek_object(
196        context,
197        AsymmetricAlgorithmSelection::Rsa(RsaKeyBits::Rsa2048),
198        None,
199    )?;
200    let (attesting_key_pub, _, _) = context.read_public(key_handle).or_else(|e| {
201        context.flush_context(key_handle.into())?;
202        Err(e)
203    })?;
204    context.flush_context(key_handle.into())?;
205
206    PublicKey::try_from(attesting_key_pub)
207}