Skip to main content

tss_esapi/context/tpm_commands/
attestation_commands.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::{
4    handles::{KeyHandle, ObjectHandle},
5    structures::{
6        Attest, AttestBuffer, CreationTicket, Data, Digest, PcrSelectionList, Signature,
7        SignatureScheme,
8    },
9    tss2_esys::{Esys_Certify, Esys_CertifyCreation, Esys_GetTime, Esys_Quote},
10    Context, Result, ReturnCode,
11};
12use log::error;
13use std::convert::TryFrom;
14use std::ptr::null_mut;
15
16impl Context {
17    /// Prove that an object is loaded in the TPM
18    ///
19    /// # Arguments
20    /// * `object_handle` - Handle of the object to be certified
21    /// * `signing_key_handle` - Handle of the key used to sign the attestation buffer
22    /// * `qualifying_data` - Qualifying data
23    /// * `signing_scheme` - Signing scheme to use if the scheme for `signing_key_handle` is `Null`.
24    ///
25    /// The object may be any object that is loaded with [Self::load()] or [Self::create_primary()]. An object that
26    /// only has its public area loaded may not be certified.
27    ///
28    /// The `signing_key_handle` must be usable for signing.
29    ///
30    /// If `signing_key_handle` has the Restricted attribute set to `true` then `signing_scheme` must be
31    /// [SignatureScheme::Null].
32    ///
33    /// # Returns
34    /// The command returns a tuple consisting of:
35    /// * `attest_data` - TPM-generated attestation data.
36    /// * `signature` - Signature for the attestation data.
37    ///
38    /// # Errors
39    /// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned
40    ///
41    /// # Examples
42    ///
43    /// ```rust
44    /// # use tss_esapi::{Context, TctiNameConf};
45    /// # use std::convert::TryFrom;
46    /// # use tss_esapi::{
47    /// #     abstraction::cipher::Cipher,
48    /// #     handles::KeyHandle,
49    /// #     interface_types::{
50    /// #         algorithm::{HashingAlgorithm, RsaSchemeAlgorithm, SignatureSchemeAlgorithm},
51    /// #         key_bits::RsaKeyBits,
52    /// #         reserved_handles::Hierarchy,
53    /// #     },
54    /// #     structures::{
55    /// #         RsaExponent, RsaScheme, SymmetricDefinition,
56    /// #     },
57    /// #     utils::{create_unrestricted_signing_rsa_public, create_restricted_decryption_rsa_public},
58    /// # };
59    /// use std::convert::TryInto;
60    /// use tss_esapi::{
61    ///     structures::{Data, SignatureScheme},
62    ///     interface_types::session_handles::AuthSession,
63    /// };
64    /// # let mut context =
65    /// #     Context::new(
66    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
67    /// #     ).expect("Failed to create Context");
68    /// let qualifying_data = vec![0xff; 16];
69    /// # let signing_key_pub = create_unrestricted_signing_rsa_public(
70    /// #         RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
71    /// #         .expect("Failed to create RSA scheme"),
72    /// #     RsaKeyBits::Rsa2048,
73    /// #     RsaExponent::default(),
74    /// # )
75    /// # .expect("Failed to create an unrestricted signing rsa public structure");
76    /// # let sign_key_handle = context
77    /// #     .execute_with_nullauth_session(|ctx| {
78    /// #         ctx.create_primary(Hierarchy::Owner, signing_key_pub, None, None, None, None)
79    /// #     })
80    /// #     .unwrap()
81    /// #     .key_handle;
82    /// # let decryption_key_pub = create_restricted_decryption_rsa_public(
83    /// #         Cipher::aes_256_cfb()
84    /// #         .try_into()
85    /// #         .expect("Failed to create symmetric object"),
86    /// #     RsaKeyBits::Rsa2048,
87    /// #     RsaExponent::default(),
88    /// # )
89    /// # .expect("Failed to create a restricted decryption rsa public structure");
90    /// # let obj_key_handle = context
91    /// #     .execute_with_nullauth_session(|ctx| {
92    /// #         ctx.create_primary(
93    /// #             Hierarchy::Owner,
94    /// #             decryption_key_pub,
95    /// #             None,
96    /// #             None,
97    /// #             None,
98    /// #             None,
99    /// #         )
100    /// #     })
101    /// #     .unwrap()
102    /// #     .key_handle;
103    /// let (attest, signature) = context
104    ///     .execute_with_sessions(
105    ///         (
106    ///             Some(AuthSession::Password),
107    ///             Some(AuthSession::Password),
108    ///             None,
109    ///         ),
110    ///         |ctx| {
111    ///             ctx.certify(
112    ///                 obj_key_handle.into(),
113    ///                 sign_key_handle,
114    ///                 Data::try_from(qualifying_data).unwrap(),
115    ///                 SignatureScheme::Null,
116    ///             )
117    ///         },
118    ///     )
119    ///     .expect("Failed to certify object handle");
120    /// ```
121    pub fn certify(
122        &mut self,
123        object_handle: ObjectHandle,
124        signing_key_handle: KeyHandle,
125        qualifying_data: Data,
126        signing_scheme: SignatureScheme,
127    ) -> Result<(Attest, Signature)> {
128        let mut certify_info_ptr = null_mut();
129        let mut signature_ptr = null_mut();
130        ReturnCode::ensure_success(
131            unsafe {
132                Esys_Certify(
133                    self.mut_context(),
134                    object_handle.into(),
135                    signing_key_handle.into(),
136                    self.required_session_1()?,
137                    self.required_session_2()?,
138                    self.optional_session_3(),
139                    &qualifying_data.into(),
140                    &signing_scheme.into(),
141                    &mut certify_info_ptr,
142                    &mut signature_ptr,
143                )
144            },
145            |ret| {
146                error!("Error in certifying: {:#010X}", ret);
147            },
148        )?;
149
150        let certify_info = Context::ffi_data_to_owned(certify_info_ptr)?;
151        let signature = Context::ffi_data_to_owned(signature_ptr)?;
152        Ok((
153            Attest::try_from(AttestBuffer::try_from(certify_info)?)?,
154            Signature::try_from(signature)?,
155        ))
156    }
157
158    /// Prove the association between an object and its creation data
159    ///
160    /// # Arguments
161    /// * `signing_key_handle` - Handle of the key used to sign the attestation buffer
162    /// * `object_handle` - Handle of the object to be certified
163    /// * `qualifying_data` - Qualifying data
164    /// * `creation_hash` - Digest of the creation data
165    /// * `signing_scheme` - Signing scheme to use if the scheme for `signing_key_handle` is `Null`.
166    /// * `creation_ticket` - CreationTicket returned at object creation time.
167    ///
168    /// The object may be any object that is loaded with [Self::load()] or [Self::create_primary()]. An object that
169    /// only has its public area loaded may not be certified.
170    ///
171    /// The `signing_key_handle` must be usable for signing.
172    ///
173    /// If `signing_key_handle` has the Restricted attribute set to `true` then `signing_scheme` must be
174    /// [SignatureScheme::Null].
175    ///
176    /// # Returns
177    /// The command returns a tuple consisting of:
178    /// * `attest_data` - TPM-generated attestation data.
179    /// * `signature` - Signature for the attestation data.
180    ///
181    /// # Errors
182    /// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned
183    ///
184    /// # Examples
185    ///
186    /// ```rust
187    /// # use tss_esapi::{Context, TctiNameConf};
188    /// # use std::convert::TryFrom;
189    /// # use tss_esapi::{
190    /// #     abstraction::cipher::Cipher,
191    /// #     handles::KeyHandle,
192    /// #     interface_types::{
193    /// #         algorithm::{HashingAlgorithm, EccSchemeAlgorithm, SignatureSchemeAlgorithm},
194    /// #         ecc::EccCurve,
195    /// #         reserved_handles::Hierarchy,
196    /// #     },
197    /// #     structures::{
198    /// #         EccScheme
199    /// #     },
200    /// #     utils::create_unrestricted_signing_ecc_public,
201    /// # };
202    /// use std::convert::TryInto;
203    /// use tss_esapi::{
204    ///     structures::{Data, SignatureScheme},
205    ///     interface_types::session_handles::AuthSession,
206    /// };
207    /// # let mut context =
208    /// #     Context::new(
209    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
210    /// #     ).expect("Failed to create Context");
211    /// let qualifying_data = vec![0xff; 16];
212    /// # let signing_key_pub = create_unrestricted_signing_ecc_public(
213    /// #         EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(HashingAlgorithm::Sha256), None)
214    /// #         .expect("Failed to create ECC scheme"),
215    /// #         EccCurve::NistP256,
216    /// # )
217    /// # .expect("Failed to create an unrestricted signing ecc public structure");
218    /// # let create_result = context
219    /// #     .execute_with_nullauth_session(|ctx| {
220    /// #         ctx.create_primary(Hierarchy::Owner, signing_key_pub, None, None, None, None)
221    /// #     }).unwrap();
222    /// let (attest, signature) = context
223    ///     .execute_with_sessions(
224    ///         (
225    ///             Some(AuthSession::Password),
226    ///             None,
227    ///             None,
228    ///         ),
229    ///         |ctx| {
230    ///             ctx.certify_creation(
231    ///               create_result.key_handle,
232    ///               create_result.key_handle.into(),
233    ///               qualifying_data.try_into()?,
234    ///               create_result.creation_hash,
235    ///               SignatureScheme::Null,
236    ///               create_result.creation_ticket,
237    ///             )
238    ///         },
239    ///     )
240    ///     .expect("Failed to certify creation");
241    /// ```
242    pub fn certify_creation(
243        &mut self,
244        signing_key_handle: KeyHandle,
245        created_object: ObjectHandle,
246        qualifying_data: Data,
247        creation_hash: Digest,
248        signing_scheme: SignatureScheme,
249        creation_ticket: CreationTicket,
250    ) -> Result<(Attest, Signature)> {
251        let mut certify_info_ptr = null_mut();
252        let mut signature_ptr = null_mut();
253        ReturnCode::ensure_success(
254            unsafe {
255                Esys_CertifyCreation(
256                    self.mut_context(),
257                    signing_key_handle.into(),
258                    created_object.into(),
259                    self.required_session_1()?,
260                    self.optional_session_2(),
261                    self.optional_session_3(),
262                    &qualifying_data.into(),
263                    &creation_hash.into(),
264                    &signing_scheme.into(),
265                    &creation_ticket.try_into()?,
266                    &mut certify_info_ptr,
267                    &mut signature_ptr,
268                )
269            },
270            |ret| {
271                error!("Error in certifying creation: {:#010X}", ret);
272            },
273        )?;
274
275        let certify_info = Context::ffi_data_to_owned(certify_info_ptr)?;
276        let signature = Context::ffi_data_to_owned(signature_ptr)?;
277        Ok((
278            Attest::try_from(AttestBuffer::try_from(certify_info)?)?,
279            Signature::try_from(signature)?,
280        ))
281    }
282
283    /// Generate a quote on the selected PCRs
284    ///
285    /// # Arguments
286    /// * `signing_key_handle`  - Handle of key that will perform signature.
287    /// * `qualifying_data`     - Data supplied by the caller.
288    /// * `signing_scheme`      - Signing scheme to use if the scheme for signing_key_handle is the null scheme.
289    /// * `pcr_selection_list`  - The PCR set to quote.
290    ///
291    /// # Errors
292    /// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned.
293    ///
294    /// # Examples
295    ///
296    /// ```rust
297    /// # use tss_esapi::{Context, TctiNameConf};
298    /// use std::convert::TryFrom;
299    /// # use tss_esapi::{
300    /// #     handles::KeyHandle,
301    /// #     interface_types::{
302    /// #         algorithm::{RsaSchemeAlgorithm, SignatureSchemeAlgorithm},
303    /// #         key_bits::RsaKeyBits,
304    /// #         reserved_handles::Hierarchy,
305    /// #     },
306    /// #     structures::{
307    /// #         AttestInfo, RsaExponent, RsaScheme, Signature,
308    /// #     },
309    /// #     utils::{create_unrestricted_signing_rsa_public, create_restricted_decryption_rsa_public},
310    /// # };
311    /// use tss_esapi::{
312    ///     interface_types::{
313    ///         algorithm::HashingAlgorithm,
314    ///         session_handles::AuthSession,
315    ///     },
316    ///     structures::{
317    ///         Data, PcrSelectionListBuilder, PcrSlot, SignatureScheme,
318    ///     },
319    /// };
320    ///
321    /// # let mut context =
322    /// #     Context::new(
323    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
324    /// #     ).expect("Failed to create Context");
325    /// let qualifying_data = Data::try_from(vec![0xff; 16])
326    ///     .expect("It should be possible to create qualifying data from bytes.");
327    /// # let signing_key_pub = create_unrestricted_signing_rsa_public(
328    /// #         RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
329    /// #         .expect("Failed to create RSA scheme"),
330    /// #     RsaKeyBits::Rsa2048,
331    /// #     RsaExponent::default(),
332    /// # )
333    /// # .expect("Failed to create an unrestricted signing rsa public structure");
334    /// # let sign_key_handle = context
335    /// #     .execute_with_nullauth_session(|ctx| {
336    /// #         ctx.create_primary(Hierarchy::Owner, signing_key_pub, None, None, None, None)
337    /// #     })
338    /// #     .unwrap()
339    /// #     .key_handle;
340    ///
341    /// // Quote PCR 0, 1, 2
342    /// let pcr_selection_list = PcrSelectionListBuilder::new()
343    ///     .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot0, PcrSlot::Slot1, PcrSlot::Slot2])
344    ///     .build()
345    ///     .expect("It should be possible to create PCR selection list with valid values.");
346    ///
347    /// let (attest, signature) = context
348    ///     .execute_with_sessions(
349    ///         (
350    ///             Some(AuthSession::Password),
351    ///             None,
352    ///             None,
353    ///         ),
354    ///         |ctx| {
355    ///             ctx.quote(
356    ///                 sign_key_handle,
357    ///                 qualifying_data,
358    ///                 SignatureScheme::Null,
359    ///                 pcr_selection_list.clone(),
360    ///             )
361    ///         },
362    ///     )
363    ///     .expect("Failed to get quote");
364    /// # match signature {
365    /// #     Signature::RsaSsa(signature) => {
366    /// #         assert_eq!(signature.hashing_algorithm(), HashingAlgorithm::Sha256);
367    /// #     }
368    /// #     _ => {
369    /// #         panic!("Received the wrong signature from the call to `quote`.");
370    /// #     }
371    /// # }
372    /// # match attest.attested() {
373    /// #     AttestInfo::Quote { info } => {
374    /// #         assert!(
375    /// #             !info.pcr_digest().is_empty(),
376    /// #             "Digest in QuoteInfo is empty"
377    /// #         );
378    /// #         assert_eq!(
379    /// #             &pcr_selection_list,
380    /// #             info.pcr_selection(),
381    /// #             "QuoteInfo selection list did not match the input selection list"
382    /// #         );
383    /// #     }
384    /// #     _ => {
385    /// #         panic!("Attested did not contain the expected variant.")
386    /// #     }
387    /// # }
388    /// ```
389    pub fn quote(
390        &mut self,
391        signing_key_handle: KeyHandle,
392        qualifying_data: Data,
393        signing_scheme: SignatureScheme,
394        pcr_selection_list: PcrSelectionList,
395    ) -> Result<(Attest, Signature)> {
396        let mut quoted_ptr = null_mut();
397        let mut signature_ptr = null_mut();
398        ReturnCode::ensure_success(
399            unsafe {
400                Esys_Quote(
401                    self.mut_context(),
402                    signing_key_handle.into(),
403                    self.optional_session_1(),
404                    self.optional_session_2(),
405                    self.optional_session_3(),
406                    &qualifying_data.into(),
407                    &signing_scheme.into(),
408                    &pcr_selection_list.into(),
409                    &mut quoted_ptr,
410                    &mut signature_ptr,
411                )
412            },
413            |ret| {
414                error!("Error in quoting PCR: {:#010X}", ret);
415            },
416        )?;
417
418        let quoted = Context::ffi_data_to_owned(quoted_ptr)?;
419        let signature = Context::ffi_data_to_owned(signature_ptr)?;
420        Ok((
421            Attest::try_from(AttestBuffer::try_from(quoted)?)?,
422            Signature::try_from(signature)?,
423        ))
424    }
425
426    /// Get the current time and clock from the TPM
427    ///
428    /// # Arguments
429    /// * `signing_key_handle` - Handle of the key used to sign the attestation buffer
430    /// * `qualifying_data` - Qualifying data
431    /// * `signing_scheme` - Signing scheme to use if the scheme for `signing_key_handle` is `Null`.
432    ///
433    /// The `signing_key_handle` must be usable for signing.
434    ///
435    /// If `signing_key_handle` has the Restricted attribute set to `true` then `signing_scheme` must be
436    /// [SignatureScheme::Null].
437    ///
438    /// # Returns
439    /// The command returns a tuple consisting of:
440    /// * `attest_data` - TPM-generated attestation data.
441    /// * `signature` - Signature for the attestation data.
442    ///
443    /// # Errors
444    /// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned
445    ///
446    /// # Examples
447    ///
448    /// ```rust
449    /// # use tss_esapi::{Context, TctiNameConf};
450    /// # use std::convert::TryFrom;
451    /// # use tss_esapi::{
452    /// #     abstraction::cipher::Cipher,
453    /// #     interface_types::{
454    /// #         algorithm::{HashingAlgorithm, RsaSchemeAlgorithm},
455    /// #         key_bits::RsaKeyBits,
456    /// #         reserved_handles::Hierarchy,
457    /// #     },
458    /// #     structures::{
459    /// #         RsaExponent, RsaScheme,
460    /// #     },
461    /// #     utils::{create_unrestricted_signing_rsa_public, create_restricted_decryption_rsa_public},
462    /// # };
463    /// use std::convert::TryInto;
464    /// use tss_esapi::{
465    ///     structures::{Data, SignatureScheme},
466    ///     interface_types::session_handles::AuthSession,
467    /// };
468    /// # let mut context =
469    /// #     Context::new(
470    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
471    /// #     ).expect("Failed to create Context");
472    /// let qualifying_data = vec![0xff; 16];
473    /// # let signing_key_pub = create_unrestricted_signing_rsa_public(
474    /// #         RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
475    /// #         .expect("Failed to create RSA scheme"),
476    /// #     RsaKeyBits::Rsa2048,
477    /// #     RsaExponent::default(),
478    /// # )
479    /// # .expect("Failed to create an unrestricted signing rsa public structure");
480    /// # let sign_key_handle = context
481    /// #     .execute_with_nullauth_session(|ctx| {
482    /// #         ctx.create_primary(Hierarchy::Owner, signing_key_pub, None, None, None, None)
483    /// #     })
484    /// #     .unwrap()
485    /// #     .key_handle;
486    /// let (attest, signature) = context
487    ///     .execute_with_sessions(
488    ///         (
489    ///             Some(AuthSession::Password),
490    ///             Some(AuthSession::Password),
491    ///             None,
492    ///         ),
493    ///         |ctx| {
494    ///             ctx.get_time(
495    ///                 sign_key_handle,
496    ///                 Data::try_from(qualifying_data).unwrap(),
497    ///                 SignatureScheme::Null,
498    ///             )
499    ///         },
500    ///     )
501    ///     .expect("Failed to get tpm time");
502    /// ```
503    pub fn get_time(
504        &mut self,
505        signing_key_handle: KeyHandle,
506        qualifying_data: Data,
507        signing_scheme: SignatureScheme,
508    ) -> Result<(Attest, Signature)> {
509        let mut timeinfo_ptr = null_mut();
510        let mut signature_ptr = null_mut();
511        ReturnCode::ensure_success(
512            unsafe {
513                Esys_GetTime(
514                    self.mut_context(),
515                    ObjectHandle::Endorsement.into(),
516                    signing_key_handle.into(),
517                    self.required_session_1()?,
518                    self.required_session_2()?,
519                    self.optional_session_3(),
520                    &qualifying_data.into(),
521                    &signing_scheme.into(),
522                    &mut timeinfo_ptr,
523                    &mut signature_ptr,
524                )
525            },
526            |ret| {
527                error!("Error in GetTime: {:#010X}", ret);
528            },
529        )?;
530
531        let timeinfo = Context::ffi_data_to_owned(timeinfo_ptr)?;
532        let signature = Context::ffi_data_to_owned(signature_ptr)?;
533        Ok((
534            Attest::try_from(AttestBuffer::try_from(timeinfo)?)?,
535            Signature::try_from(signature)?,
536        ))
537    }
538
539    // Missing function: GetSessionAuditDigest
540    // Missing function: GestCommandAuditDigest
541    // Missing function: CertifyX509
542}