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}