Skip to main content

tss_esapi/context/tpm_commands/
hierarchy_commands.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    Context, Result, ReturnCode,
5    context::handle_manager::HandleDropAction,
6    handles::{AuthHandle, KeyHandle, ObjectHandle},
7    interface_types::{
8        YesNo,
9        algorithm::HashingAlgorithm,
10        reserved_handles::{Enables, Hierarchy, HierarchyAuth},
11    },
12    structures::{
13        Auth, CreatePrimaryKeyResult, CreationData, CreationTicket, Data, Digest, PcrSelectionList,
14        Public, SensitiveCreate, SensitiveData,
15    },
16    tss2_esys::{
17        Esys_ChangeEPS, Esys_ChangePPS, Esys_Clear, Esys_ClearControl, Esys_CreatePrimary,
18        Esys_HierarchyChangeAuth, Esys_HierarchyControl, Esys_SetPrimaryPolicy,
19    },
20};
21use log::error;
22use std::convert::{TryFrom, TryInto};
23use std::ptr::null_mut;
24
25impl Context {
26    /// Create a primary key and return the handle.
27    ///
28    /// The authentication value, initial data, outside info and creation PCRs are passed as slices
29    /// which are then converted by the method into TSS native structures.
30    ///
31    /// # Errors
32    /// * if either of the slices is larger than the maximum size of the native objects, a
33    ///   `WrongParamSize` wrapper error is returned
34    // TODO: Fix when compacting the arguments into a struct
35    #[allow(clippy::too_many_arguments)]
36    pub fn create_primary(
37        &mut self,
38        primary_handle: Hierarchy,
39        public: Public,
40        auth_value: Option<Auth>,
41        initial_data: Option<SensitiveData>,
42        outside_info: Option<Data>,
43        creation_pcrs: Option<PcrSelectionList>,
44    ) -> Result<CreatePrimaryKeyResult> {
45        let sensitive_create = SensitiveCreate::new(
46            auth_value.unwrap_or_default(),
47            initial_data.unwrap_or_default(),
48        );
49        let creation_pcrs = PcrSelectionList::list_from_option(creation_pcrs);
50
51        let mut out_public_ptr = null_mut();
52        let mut creation_data_ptr = null_mut();
53        let mut creation_hash_ptr = null_mut();
54        let mut creation_ticket_ptr = null_mut();
55        let mut object_handle = ObjectHandle::None.into();
56
57        ReturnCode::ensure_success(
58            unsafe {
59                Esys_CreatePrimary(
60                    self.mut_context(),
61                    ObjectHandle::from(primary_handle).into(),
62                    self.required_session_1()?,
63                    self.optional_session_2(),
64                    self.optional_session_3(),
65                    &sensitive_create.try_into()?,
66                    &public.try_into()?,
67                    &outside_info.unwrap_or_default().into(),
68                    &creation_pcrs.into(),
69                    &mut object_handle,
70                    &mut out_public_ptr,
71                    &mut creation_data_ptr,
72                    &mut creation_hash_ptr,
73                    &mut creation_ticket_ptr,
74                )
75            },
76            |ret| {
77                error!("Error in creating primary key: {:#010X}", ret);
78            },
79        )?;
80        let out_public_owned = Context::ffi_data_to_owned(out_public_ptr)?;
81        let creation_data_owned = Context::ffi_data_to_owned(creation_data_ptr)?;
82        let creation_hash_owned = Context::ffi_data_to_owned(creation_hash_ptr)?;
83        let creation_ticket_owned = Context::ffi_data_to_owned(creation_ticket_ptr)?;
84        let primary_key_handle = KeyHandle::from(object_handle);
85        self.handle_manager
86            .add_handle(primary_key_handle.into(), HandleDropAction::Flush)?;
87
88        Ok(CreatePrimaryKeyResult {
89            key_handle: primary_key_handle,
90            out_public: Public::try_from(out_public_owned)?,
91            creation_data: CreationData::try_from(creation_data_owned)?,
92            creation_hash: Digest::try_from(creation_hash_owned)?,
93            creation_ticket: CreationTicket::try_from(creation_ticket_owned)?,
94        })
95    }
96
97    /// Enables or disables use of a hierarchy.
98    ///
99    /// # Arguments
100    ///
101    /// * `enable` - The hierarchy or associated NV storage whose state will be changed.
102    /// * `state` - `true` to enable use of the hierarchy, or `false` to disable it.
103    ///
104    /// # Details
105    ///
106    /// *From the specification*
107    /// > This command enables and disables use of a hierarchy and its associated NV storage. The
108    /// > command allows phEnable, phEnableNV, shEnable, and ehEnable to be changed when the proper
109    /// > authorization is provided.
110    ///
111    /// # Example
112    ///
113    /// ```rust
114    /// # use tss_esapi::{Context, TctiNameConf};
115    /// # use tss_esapi::interface_types::{
116    /// #     reserved_handles::Enables, session_handles::AuthSession,
117    /// # };
118    /// # let mut context = Context::new(
119    /// #     TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
120    /// # ).expect("Failed to create Context");
121    /// context
122    ///     .execute_with_session(Some(AuthSession::Password), |ctx| {
123    ///         ctx.hierarchy_control(Enables::Endorsement, false)?;
124    ///         ctx.hierarchy_control(Enables::Endorsement, true)
125    ///     })
126    ///     .unwrap();
127    /// ```
128    pub fn hierarchy_control(&mut self, enable: Enables, state: bool) -> Result<()> {
129        ReturnCode::ensure_success(
130            unsafe {
131                Esys_HierarchyControl(
132                    self.mut_context(),
133                    ObjectHandle::Platform.into(),
134                    self.required_session_1()?,
135                    self.optional_session_2(),
136                    self.optional_session_3(),
137                    ObjectHandle::from(enable).into(),
138                    YesNo::from(state).into(),
139                )
140            },
141            |ret| {
142                error!("Error controlling hierarchy: {:#010X}", ret);
143            },
144        )
145    }
146
147    /// Sets the authorization policy for a hierarchy.
148    ///
149    /// # Arguments
150    ///
151    /// * `auth_handle` - The hierarchy whose authorization policy will be changed.
152    /// * `auth_policy` - The new authorization policy digest.
153    /// * `hash_algorithm` - The hash algorithm used to compute `auth_policy`. An empty policy must
154    ///   be paired with [`HashingAlgorithm::Null`].
155    ///
156    /// # Details
157    ///
158    /// *From the specification*
159    /// > This command allows setting of the authorization policy for the lockout (lockoutPolicy),
160    /// > the platform hierarchy (platformPolicy), the storage hierarchy (ownerPolicy), and the
161    /// > endorsement hierarchy (endorsementPolicy).
162    ///
163    /// # Example
164    ///
165    /// ```rust
166    /// # use tss_esapi::{Context, TctiNameConf};
167    /// # use tss_esapi::{
168    /// #     interface_types::{algorithm::HashingAlgorithm, reserved_handles::HierarchyAuth,
169    /// #         session_handles::AuthSession},
170    /// #     structures::Digest,
171    /// # };
172    /// # let mut context = Context::new(
173    /// #     TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
174    /// # ).expect("Failed to create Context");
175    /// context
176    ///     .execute_with_session(Some(AuthSession::Password), |ctx| {
177    ///         ctx.set_primary_policy(
178    ///             HierarchyAuth::Platform,
179    ///             Digest::default(),
180    ///             HashingAlgorithm::Null,
181    ///         )
182    ///     })
183    ///     .unwrap();
184    /// ```
185    pub fn set_primary_policy(
186        &mut self,
187        auth_handle: HierarchyAuth,
188        auth_policy: Digest,
189        hash_algorithm: HashingAlgorithm,
190    ) -> Result<()> {
191        ReturnCode::ensure_success(
192            unsafe {
193                Esys_SetPrimaryPolicy(
194                    self.mut_context(),
195                    ObjectHandle::from(auth_handle).into(),
196                    self.required_session_1()?,
197                    self.optional_session_2(),
198                    self.optional_session_3(),
199                    &auth_policy.into(),
200                    hash_algorithm.into(),
201                )
202            },
203            |ret| {
204                error!("Error setting primary policy: {:#010X}", ret);
205            },
206        )
207    }
208
209    /// Replaces the platform primary seed with a new random value.
210    ///
211    /// # Arguments
212    ///
213    /// This command has no command-specific arguments. The first configured session must authorize
214    /// the platform hierarchy.
215    ///
216    /// # Details
217    ///
218    /// *From the specification*
219    /// > This replaces the current platform primary seed (PPS) with a value from the RNG and sets
220    /// > platformPolicy to the default initialization value (the Empty Buffer).
221    ///
222    /// Existing objects in the platform hierarchy can no longer be loaded after this command.
223    ///
224    /// # Example
225    ///
226    /// ```rust
227    /// # use tss_esapi::{Context, TctiNameConf};
228    /// # use tss_esapi::interface_types::session_handles::AuthSession;
229    /// # let mut context = Context::new(
230    /// #     TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
231    /// # ).expect("Failed to create Context");
232    /// context
233    ///     .execute_with_session(Some(AuthSession::Password), |ctx| ctx.change_pps())
234    ///     .unwrap();
235    /// ```
236    pub fn change_pps(&mut self) -> Result<()> {
237        ReturnCode::ensure_success(
238            unsafe {
239                Esys_ChangePPS(
240                    self.mut_context(),
241                    ObjectHandle::Platform.into(),
242                    self.required_session_1()?,
243                    self.optional_session_2(),
244                    self.optional_session_3(),
245                )
246            },
247            |ret| {
248                error!("Error changing platform primary seed: {:#010X}", ret);
249            },
250        )
251    }
252
253    /// Replaces the endorsement primary seed with a new random value.
254    ///
255    /// # Arguments
256    ///
257    /// This command has no command-specific arguments. The first configured session must authorize
258    /// the platform hierarchy.
259    ///
260    /// # Details
261    ///
262    /// *From the specification*
263    /// > This replaces the current endorsement primary seed (EPS) with a value from the RNG and
264    /// > sets the Endorsement hierarchy controls to their default initialization values: ehEnable
265    /// > is SET, endorsementAuth and endorsementPolicy are both set to the Empty Buffer. It will
266    /// > flush any resident objects (transient or persistent) in the Endorsement hierarchy and not
267    /// > allow objects in the hierarchy associated with the previous EPS to be loaded.
268    ///
269    /// Existing objects in the endorsement hierarchy can no longer be loaded after this command.
270    ///
271    /// # Example
272    ///
273    /// ```rust
274    /// # use tss_esapi::{Context, TctiNameConf};
275    /// # use tss_esapi::interface_types::session_handles::AuthSession;
276    /// # let mut context = Context::new(
277    /// #     TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
278    /// # ).expect("Failed to create Context");
279    /// context
280    ///     .execute_with_session(Some(AuthSession::Password), |ctx| ctx.change_eps())
281    ///     .unwrap();
282    /// ```
283    pub fn change_eps(&mut self) -> Result<()> {
284        ReturnCode::ensure_success(
285            unsafe {
286                Esys_ChangeEPS(
287                    self.mut_context(),
288                    ObjectHandle::Platform.into(),
289                    self.required_session_1()?,
290                    self.optional_session_2(),
291                    self.optional_session_3(),
292                )
293            },
294            |ret| {
295                error!("Error changing endorsement primary seed: {:#010X}", ret);
296            },
297        )
298    }
299
300    /// Clear all TPM context associated with a specific Owner
301    pub fn clear(&mut self, auth_handle: AuthHandle) -> Result<()> {
302        ReturnCode::ensure_success(
303            unsafe {
304                Esys_Clear(
305                    self.mut_context(),
306                    auth_handle.into(),
307                    self.required_session_1()?,
308                    self.optional_session_2(),
309                    self.optional_session_3(),
310                )
311            },
312            |ret| {
313                error!("Error in clearing TPM hierarchy: {:#010X}", ret);
314            },
315        )
316    }
317
318    /// Disable or enable the TPM2_CLEAR command
319    pub fn clear_control(&mut self, auth_handle: AuthHandle, disable: bool) -> Result<()> {
320        ReturnCode::ensure_success(
321            unsafe {
322                Esys_ClearControl(
323                    self.mut_context(),
324                    auth_handle.into(),
325                    self.required_session_1()?,
326                    self.optional_session_2(),
327                    self.optional_session_3(),
328                    YesNo::from(disable).into(),
329                )
330            },
331            |ret| {
332                error!("Error in controlling clear command: {:#010X}", ret);
333            },
334        )
335    }
336
337    /// Change authorization for a hierarchy root
338    pub fn hierarchy_change_auth(&mut self, auth_handle: AuthHandle, new_auth: Auth) -> Result<()> {
339        ReturnCode::ensure_success(
340            unsafe {
341                Esys_HierarchyChangeAuth(
342                    self.mut_context(),
343                    auth_handle.into(),
344                    self.required_session_1()?,
345                    self.optional_session_2(),
346                    self.optional_session_3(),
347                    &new_auth.into(),
348                )
349            },
350            |ret| {
351                error!("Error changing hierarchy auth: {:#010X}", ret);
352            },
353        )
354    }
355}