tss_esapi/context/tpm_commands/
enhanced_authorization_ea_commands.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    attributes::LocalityAttributes,
5    constants::CommandCode,
6    handles::{AuthHandle, ObjectHandle, SessionHandle},
7    interface_types::{session_handles::PolicySession, YesNo},
8    structures::{
9        AuthTicket, Digest, DigestList, Name, Nonce, PcrSelectionList, Signature, Timeout,
10        VerifiedTicket,
11    },
12    tss2_esys::{
13        Esys_PolicyAuthValue, Esys_PolicyAuthorize, Esys_PolicyCommandCode, Esys_PolicyCpHash,
14        Esys_PolicyDuplicationSelect, Esys_PolicyGetDigest, Esys_PolicyLocality,
15        Esys_PolicyNameHash, Esys_PolicyNvWritten, Esys_PolicyOR, Esys_PolicyPCR,
16        Esys_PolicyPassword, Esys_PolicyPhysicalPresence, Esys_PolicySecret, Esys_PolicySigned,
17        Esys_PolicyTemplate,
18    },
19    Context, Error, Result, WrapperErrorKind as ErrorKind,
20};
21use log::error;
22use std::convert::{TryFrom, TryInto};
23use std::ptr::null_mut;
24use std::time::Duration;
25
26impl Context {
27    /// Cause the policy to include a signed authorization
28    #[allow(clippy::too_many_arguments)]
29    pub fn policy_signed(
30        &mut self,
31        policy_session: PolicySession,
32        auth_object: ObjectHandle,
33        nonce_tpm: Nonce,
34        cp_hash_a: Digest,
35        policy_ref: Nonce,
36        expiration: Option<Duration>,
37        signature: Signature,
38    ) -> Result<(Timeout, AuthTicket)> {
39        let mut out_timeout_ptr = null_mut();
40        let mut out_policy_ticket_ptr = null_mut();
41        let ret = unsafe {
42            Esys_PolicySigned(
43                self.mut_context(),
44                auth_object.into(),
45                SessionHandle::from(policy_session).into(),
46                self.required_session_1()?,
47                self.optional_session_2(),
48                self.optional_session_3(),
49                &nonce_tpm.into(),
50                &cp_hash_a.into(),
51                &policy_ref.into(),
52                i32::try_from(expiration.map_or(0, |v| v.as_secs())).map_err(|e| {
53                    error!("Unable to convert duration to i32: {}", e);
54                    Error::local_error(ErrorKind::InvalidParam)
55                })?,
56                &signature.try_into()?,
57                &mut out_timeout_ptr,
58                &mut out_policy_ticket_ptr,
59            )
60        };
61        let ret = Error::from_tss_rc(ret);
62        if ret.is_success() {
63            Ok((
64                Timeout::try_from(Context::ffi_data_to_owned(out_timeout_ptr))?,
65                AuthTicket::try_from(Context::ffi_data_to_owned(out_policy_ticket_ptr))?,
66            ))
67        } else {
68            error!("Error when sending policy signed: {}", ret);
69            Err(ret)
70        }
71    }
72
73    /// Cause the policy to require a secret in authValue
74    pub fn policy_secret(
75        &mut self,
76        policy_session: PolicySession,
77        auth_handle: AuthHandle,
78        nonce_tpm: Nonce,
79        cp_hash_a: Digest,
80        policy_ref: Nonce,
81        expiration: Option<Duration>,
82    ) -> Result<(Timeout, AuthTicket)> {
83        let mut out_timeout_ptr = null_mut();
84        let mut out_policy_ticket_ptr = null_mut();
85        let ret = unsafe {
86            Esys_PolicySecret(
87                self.mut_context(),
88                auth_handle.into(),
89                SessionHandle::from(policy_session).into(),
90                self.required_session_1()?,
91                self.optional_session_2(),
92                self.optional_session_3(),
93                &nonce_tpm.into(),
94                &cp_hash_a.into(),
95                &policy_ref.into(),
96                i32::try_from(expiration.map_or(0, |v| v.as_secs())).map_err(|e| {
97                    error!("Unable to convert duration to i32: {}", e);
98                    Error::local_error(ErrorKind::InvalidParam)
99                })?,
100                &mut out_timeout_ptr,
101                &mut out_policy_ticket_ptr,
102            )
103        };
104        let ret = Error::from_tss_rc(ret);
105        if ret.is_success() {
106            Ok((
107                Timeout::try_from(Context::ffi_data_to_owned(out_timeout_ptr))?,
108                AuthTicket::try_from(Context::ffi_data_to_owned(out_policy_ticket_ptr))?,
109            ))
110        } else {
111            error!("Error when sending policy secret: {}", ret);
112            Err(ret)
113        }
114    }
115
116    // Missing function: PolicyTicket
117
118    /// Cause conditional gating of a policy based on an OR'd condition.
119    ///
120    /// The TPM will ensure that the current policy digest equals at least
121    /// one of the digests.
122    /// If this is the case, the policyDigest of the policy session is replaced
123    /// by the value of the different hashes.
124    ///
125    /// # Constraints
126    /// * `digest_list` must be at least 2 and at most 8 elements long
127    ///
128    /// # Errors
129    /// * if the hash list provided is too short or too long, a `WrongParamSize` wrapper error will be returned
130    pub fn policy_or(
131        &mut self,
132        policy_session: PolicySession,
133        digest_list: DigestList,
134    ) -> Result<()> {
135        if digest_list.len() < 2 {
136            error!(
137                "The digest list only contains {} digests, it must contain at least 2",
138                digest_list.len()
139            );
140            return Err(Error::local_error(ErrorKind::WrongParamSize));
141        }
142
143        let ret = unsafe {
144            Esys_PolicyOR(
145                self.mut_context(),
146                SessionHandle::from(policy_session).into(),
147                self.optional_session_1(),
148                self.optional_session_2(),
149                self.optional_session_3(),
150                &digest_list.try_into()?,
151            )
152        };
153        let ret = Error::from_tss_rc(ret);
154        if ret.is_success() {
155            Ok(())
156        } else {
157            error!("Error when computing policy OR: {}", ret);
158            Err(ret)
159        }
160    }
161
162    /// Cause conditional gating of a policy based on PCR.
163    ///
164    /// # Details
165    /// The TPM will use the hash algorithm of the policy_session
166    /// to calculate a digest from the values of the pcr slots
167    /// specified in the pcr_selections.
168    /// This is then compared to pcr_policy_digest if they match then
169    /// the policyDigest of the policy session is extended.
170    ///
171    /// # Errors
172    /// * if the pcr policy digest provided is too long, a `WrongParamSize` wrapper error will be returned
173    pub fn policy_pcr(
174        &mut self,
175        policy_session: PolicySession,
176        pcr_policy_digest: Digest,
177        pcr_selection_list: PcrSelectionList,
178    ) -> Result<()> {
179        let ret = unsafe {
180            Esys_PolicyPCR(
181                self.mut_context(),
182                SessionHandle::from(policy_session).into(),
183                self.optional_session_1(),
184                self.optional_session_2(),
185                self.optional_session_3(),
186                &pcr_policy_digest.into(),
187                &pcr_selection_list.into(),
188            )
189        };
190        let ret = Error::from_tss_rc(ret);
191        if ret.is_success() {
192            Ok(())
193        } else {
194            error!("Error when computing policy PCR: {}", ret);
195            Err(ret)
196        }
197    }
198
199    /// Cause conditional gating of a policy based on locality.
200    ///
201    /// The TPM will ensure that the current policy can only complete in the specified
202    /// locality (extended) or any of the specified localities (non-extended).
203    pub fn policy_locality(
204        &mut self,
205        policy_session: PolicySession,
206        locality: LocalityAttributes,
207    ) -> Result<()> {
208        let ret = unsafe {
209            Esys_PolicyLocality(
210                self.mut_context(),
211                SessionHandle::from(policy_session).into(),
212                self.optional_session_1(),
213                self.optional_session_2(),
214                self.optional_session_3(),
215                locality.into(),
216            )
217        };
218        let ret = Error::from_tss_rc(ret);
219        if ret.is_success() {
220            Ok(())
221        } else {
222            error!("Error when computing policy locality: {}", ret);
223            Err(ret)
224        }
225    }
226
227    // Missing function: PolicyNV
228    // Missing function: PolicyCounterTimer
229
230    /// Cause conditional gating of a policy based on command code of authorized command.
231    ///
232    /// The TPM will ensure that the current policy can only be used to complete the command
233    /// indicated by code.
234    pub fn policy_command_code(
235        &mut self,
236        policy_session: PolicySession,
237        code: CommandCode,
238    ) -> Result<()> {
239        let ret = unsafe {
240            Esys_PolicyCommandCode(
241                self.mut_context(),
242                SessionHandle::from(policy_session).into(),
243                self.optional_session_1(),
244                self.optional_session_2(),
245                self.optional_session_3(),
246                code.into(),
247            )
248        };
249        let ret = Error::from_tss_rc(ret);
250        if ret.is_success() {
251            Ok(())
252        } else {
253            error!("Error when computing policy command code: {}", ret);
254            Err(ret)
255        }
256    }
257
258    /// Cause conditional gating of a policy based on physical presence.
259    ///
260    /// The TPM will ensure that the current policy can only complete when physical
261    /// presence is asserted. The way this is done is implementation-specific.
262    pub fn policy_physical_presence(&mut self, policy_session: PolicySession) -> Result<()> {
263        let ret = unsafe {
264            Esys_PolicyPhysicalPresence(
265                self.mut_context(),
266                SessionHandle::from(policy_session).into(),
267                self.optional_session_1(),
268                self.optional_session_2(),
269                self.optional_session_3(),
270            )
271        };
272        let ret = Error::from_tss_rc(ret);
273        if ret.is_success() {
274            Ok(())
275        } else {
276            error!("Error when computing policy physical presence: {}", ret);
277            Err(ret)
278        }
279    }
280
281    /// Cause conditional gating of a policy based on command parameters.
282    ///
283    /// The TPM will ensure that the current policy can only be used to authorize
284    /// a command where the parameters are hashed into cp_hash_a.
285    pub fn policy_cp_hash(
286        &mut self,
287        policy_session: PolicySession,
288        cp_hash_a: Digest,
289    ) -> Result<()> {
290        let ret = unsafe {
291            Esys_PolicyCpHash(
292                self.mut_context(),
293                SessionHandle::from(policy_session).into(),
294                self.optional_session_1(),
295                self.optional_session_2(),
296                self.optional_session_3(),
297                &cp_hash_a.into(),
298            )
299        };
300        let ret = Error::from_tss_rc(ret);
301        if ret.is_success() {
302            Ok(())
303        } else {
304            error!("Error when computing policy command parameters: {}", ret);
305            Err(ret)
306        }
307    }
308
309    /// Cause conditional gating of a policy based on name hash.
310    ///
311    /// The TPM will ensure that the current policy can only be used to authorize
312    /// a command acting on an object whose name hashes to name_hash.
313    pub fn policy_name_hash(
314        &mut self,
315        policy_session: PolicySession,
316        name_hash: Digest,
317    ) -> Result<()> {
318        let ret = unsafe {
319            Esys_PolicyNameHash(
320                self.mut_context(),
321                SessionHandle::from(policy_session).into(),
322                self.optional_session_1(),
323                self.optional_session_2(),
324                self.optional_session_3(),
325                &name_hash.into(),
326            )
327        };
328        let ret = Error::from_tss_rc(ret);
329        if ret.is_success() {
330            Ok(())
331        } else {
332            error!("Error when computing policy name hash: {}", ret);
333            Err(ret)
334        }
335    }
336
337    /// Cause conditional gating of a policy based on duplication parent's name.
338    ///
339    /// # Arguments
340    /// * `policy_session` - The [policy session][PolicySession] being extended.
341    /// * `object_name` - The [name][Name] of the object being duplicated.
342    /// * `new_parent_name` - The [name][Name] of the new parent.
343    /// * `include_object` - Flag indicating if `object_name` will be included in policy
344    ///                      calculation.
345    ///
346    /// # Details
347    /// Set `include_object` only when this command is used in conjunction with
348    /// [`policy_authorize`][Context::policy_authorize].
349    ///
350    /// # Example
351    ///
352    /// ```rust
353    /// # use std::convert::{TryFrom, TryInto};
354    /// # use tss_esapi::attributes::{ObjectAttributesBuilder, SessionAttributesBuilder};
355    /// # use tss_esapi::constants::{CommandCode, SessionType};
356    /// # use tss_esapi::handles::ObjectHandle;
357    /// # use tss_esapi::interface_types::{
358    /// #     algorithm::{HashingAlgorithm, PublicAlgorithm},
359    /// #     key_bits::RsaKeyBits,
360    /// #     resource_handles::Hierarchy,
361    /// #     session_handles::PolicySession,
362    /// # };
363    /// # use tss_esapi::structures::SymmetricDefinition;
364    /// # use tss_esapi::structures::{
365    /// #     PublicBuilder, PublicKeyRsa, PublicRsaParametersBuilder, RsaScheme,
366    /// #     RsaExponent, Name,
367    /// # };
368    /// # use tss_esapi::structures::SymmetricDefinitionObject;
369    /// # use tss_esapi::abstraction::cipher::Cipher;
370    /// # use tss_esapi::{Context, TctiNameConf};
371    /// #
372    /// # let mut context = // ...
373    /// #     Context::new(
374    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
375    /// #     ).expect("Failed to create Context");
376    /// #
377    /// # let trial_session = context
378    /// #     .start_auth_session(
379    /// #         None,
380    /// #         None,
381    /// #         None,
382    /// #         SessionType::Trial,
383    /// #         SymmetricDefinition::AES_256_CFB,
384    /// #         HashingAlgorithm::Sha256,
385    /// #     )
386    /// #     .expect("Start auth session failed")
387    /// #     .expect("Start auth session returned a NONE handle");
388    /// #
389    /// # let (policy_auth_session_attributes, policy_auth_session_attributes_mask) =
390    /// #     SessionAttributesBuilder::new()
391    /// #         .with_decrypt(true)
392    /// #         .with_encrypt(true)
393    /// #         .build();
394    /// # context
395    /// #     .tr_sess_set_attributes(
396    /// #         trial_session,
397    /// #         policy_auth_session_attributes,
398    /// #         policy_auth_session_attributes_mask,
399    /// #     )
400    /// #     .expect("tr_sess_set_attributes call failed");
401    /// #
402    /// # let policy_session = PolicySession::try_from(trial_session)
403    /// #     .expect("Failed to convert auth session into policy session");
404    /// #
405    /// # let object_name: Name = Vec::<u8>::new().try_into().unwrap();
406    /// # let parent_name = object_name.clone();
407    /// #
408    /// context
409    ///     .policy_duplication_select(policy_session, object_name, parent_name, false)
410    ///     .expect("Policy command code");
411    /// #
412    /// # /// Digest of the policy that allows duplication
413    /// # let digest = context
414    /// #     .policy_get_digest(policy_session)
415    /// #     .expect("Could retrieve digest");
416    /// ```
417    pub fn policy_duplication_select(
418        &mut self,
419        policy_session: PolicySession,
420        object_name: Name,
421        new_parent_name: Name,
422        include_object: bool,
423    ) -> Result<()> {
424        let ret = unsafe {
425            Esys_PolicyDuplicationSelect(
426                self.mut_context(),
427                SessionHandle::from(policy_session).into(),
428                self.optional_session_1(),
429                self.optional_session_2(),
430                self.optional_session_3(),
431                &object_name.into(),
432                &new_parent_name.into(),
433                u8::from(include_object),
434            )
435        };
436        let ret = Error::from_tss_rc(ret);
437        if ret.is_success() {
438            Ok(())
439        } else {
440            error!("Error when computing policy duplication select: {}", ret);
441            Err(ret)
442        }
443    }
444
445    /// Cause conditional gating of a policy based on an authorized policy
446    ///
447    /// The TPM will ensure that the current policy digest is correctly signed
448    /// by the ticket in check_ticket and that check_ticket is signed by the key
449    /// named in key_sign.
450    /// If this is the case, the policyDigest of the policy session is replaced
451    /// by the value of the key_sign and policy_ref values.
452    pub fn policy_authorize(
453        &mut self,
454        policy_session: PolicySession,
455        approved_policy: Digest,
456        policy_ref: Nonce,
457        key_sign: &Name,
458        check_ticket: VerifiedTicket,
459    ) -> Result<()> {
460        let ret = unsafe {
461            Esys_PolicyAuthorize(
462                self.mut_context(),
463                SessionHandle::from(policy_session).into(),
464                self.optional_session_1(),
465                self.optional_session_2(),
466                self.optional_session_3(),
467                &approved_policy.into(),
468                &policy_ref.into(),
469                key_sign.as_ref(),
470                &check_ticket.try_into()?,
471            )
472        };
473        let ret = Error::from_tss_rc(ret);
474        if ret.is_success() {
475            Ok(())
476        } else {
477            error!("Error when computing policy authorize: {}", ret);
478            Err(ret)
479        }
480    }
481
482    /// Cause conditional gating of a policy based on authValue.
483    ///
484    /// The TPM will ensure that the current policy requires the user to know the authValue
485    /// used when creating the object.
486    pub fn policy_auth_value(&mut self, policy_session: PolicySession) -> Result<()> {
487        let ret = unsafe {
488            Esys_PolicyAuthValue(
489                self.mut_context(),
490                SessionHandle::from(policy_session).into(),
491                self.optional_session_1(),
492                self.optional_session_2(),
493                self.optional_session_3(),
494            )
495        };
496        let ret = Error::from_tss_rc(ret);
497        if ret.is_success() {
498            Ok(())
499        } else {
500            error!("Error when computing policy auth value: {}", ret);
501            Err(ret)
502        }
503    }
504
505    /// Cause conditional gating of a policy based on password.
506    ///
507    /// The TPM will ensure that the current policy requires the user to know the password
508    /// used when creating the object.
509    pub fn policy_password(&mut self, policy_session: PolicySession) -> Result<()> {
510        let ret = unsafe {
511            Esys_PolicyPassword(
512                self.mut_context(),
513                SessionHandle::from(policy_session).into(),
514                self.optional_session_1(),
515                self.optional_session_2(),
516                self.optional_session_3(),
517            )
518        };
519        let ret = Error::from_tss_rc(ret);
520        if ret.is_success() {
521            Ok(())
522        } else {
523            error!("Error when computing policy password: {}", ret);
524            Err(ret)
525        }
526    }
527
528    /// Function for retrieving the current policy digest for
529    /// the session.
530    pub fn policy_get_digest(&mut self, policy_session: PolicySession) -> Result<Digest> {
531        let mut policy_digest_ptr = null_mut();
532        let ret = unsafe {
533            Esys_PolicyGetDigest(
534                self.mut_context(),
535                SessionHandle::from(policy_session).into(),
536                self.optional_session_1(),
537                self.optional_session_2(),
538                self.optional_session_3(),
539                &mut policy_digest_ptr,
540            )
541        };
542        let ret = Error::from_tss_rc(ret);
543        if ret.is_success() {
544            Digest::try_from(Context::ffi_data_to_owned(policy_digest_ptr))
545        } else {
546            error!(
547                "Error failed to perform policy get digest operation: {}.",
548                ret
549            );
550            Err(ret)
551        }
552    }
553
554    /// Cause conditional gating of a policy based on NV written state.
555    ///
556    /// The TPM will ensure that the NV index that is used has a specific written state.
557    pub fn policy_nv_written(
558        &mut self,
559        policy_session: PolicySession,
560        written_set: bool,
561    ) -> Result<()> {
562        let ret = unsafe {
563            Esys_PolicyNvWritten(
564                self.mut_context(),
565                SessionHandle::from(policy_session).into(),
566                self.optional_session_1(),
567                self.optional_session_2(),
568                self.optional_session_3(),
569                YesNo::from(written_set).into(),
570            )
571        };
572        let ret = Error::from_tss_rc(ret);
573        if ret.is_success() {
574            Ok(())
575        } else {
576            error!("Error when computing policy NV written state: {}", ret);
577            Err(ret)
578        }
579    }
580
581    /// Bind policy to a specific creation template.
582    ///
583    /// # Arguments
584    /// * `policy_session` - The [policy session][PolicySession] being extended.
585    /// * `template_hash` - The [digest][Digest] to be added to the policy.
586    pub fn policy_template(
587        &mut self,
588        policy_session: PolicySession,
589        template_hash: Digest,
590    ) -> Result<()> {
591        let ret = unsafe {
592            Esys_PolicyTemplate(
593                self.mut_context(),
594                SessionHandle::from(policy_session).into(),
595                self.optional_session_1(),
596                self.optional_session_2(),
597                self.optional_session_3(),
598                &template_hash.into(),
599            )
600        };
601        let ret = Error::from_tss_rc(ret);
602        if ret.is_success() {
603            Ok(())
604        } else {
605            error!(
606                "Failed to bind template to a specific creation template: {}",
607                ret
608            );
609            Err(ret)
610        }
611    }
612    // Missing function: PolicyAuthorizeNV
613}